+++ /dev/null
-<?php
-/**
- * A hub peer class for communicating with the hub peer
- *
- * @author Roland Haeder <webmaster@ship-simu.org>
- * @version 0.0
- * @copyright Copyright(c) 2007, 2008 Roland Haeder, this is free software
- * @license GNU GPL 3.0 or any newer version
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 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.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-class HubPeer extends BaseFrameworkSystem {
- /**
- * This peer's socket resource
- */
- private $peerSocket = null;
-
- /**
- * Timestamp for connection
- */
- private $connectTime = 0;
-
- /**
- * Timestamp of last received message
- */
- private $lastReceivedMessageStamp = 0;
-
- /**
- * Last received message
- */
- private $lastReceivedMessage = "";
-
- /**
- * Timestamp of last sent message
- */
- private $lastSentMessageStamp = 0;
-
- /**
- * Last sent message
- */
- private $lastSentMessage = "";
-
- /**
- * Number of tries for authorization
- */
- private $authRetries = 0;
-
- /**
- * Wether the peer is authorized (DO NEVER SET THIS TO "true"!)
- */
- private $isAuthorized = false;
-
- /**
- * Wether this peer needs to be requested for the AUTH command
- */
- private $needAuthRequest = true;
-
- /**
- * The peer's IP number
- */
- private static $peerIP = "0.0.0.0";
-
- /**
- * Wether the peer has "hello-ed" to the other
- */
- private $helloSent = false;
-
- /**
- * Wether the peer has replied our HELLO
- */
- private $helloReplied = false;
-
- /**
- * Wether we have sent our ELHO
- */
- private $elhoSent = false;
-
- /**
- * An instance of HubCoreLoop
- */
- private $hubInstance = null;
-
- /**
- * When the last ping was
- */
- private $lastPinged = 0;
-
- /**
- * When the last pong was
- */
- private $lastPonged = 0;
-
- /**
- * Number of missing pongs from the other peer
- */
- private $missingPongs = 0;
-
- /**
- * An instance of a HubCommandProcessor class
- */
- private $commandInstance = null;
-
- /**
- * A queue for reading data in non-blocking mode
- */
- private $queues = "";
-
- /**
- * Line ends
- */
- const LINE_END = "\n";
-
- //----------------------------------------------------------
- // Exceptions
- //----------------------------------------------------------
- const EXCEPTION_PEER_SOCKET_INVALID = 0x200;
- const EXCEPTION_PEER_IP_CHANGED = 0x201;
- const EXCEPTION_PEER_SOCKET_BROKEN = 0x202;
- //----------------------------------------------------------
-
- /**
- * The private constructor
- */
- private function __construct () {
- // Call parent constructor
- parent::constructor(__CLASS__);
-
- // Set description
- $this->setPartDescr("Hub-Peer");
-
- // Set unique ID
- $this->createUniqueID();
-
- // Tidy up a little
- $this->removeSystemArray();
- $this->removeNumberFormaters();
-
- // Get a command processor
- $this->commandInstance = HubCommandProcessor::createHubCommandProcessor($this);
- }
-
- /**
- * Creates an instance of a HubPeer by a provided valid socket resource
- *
- * @param $peerSocket The socket resource for the peer
- * @param $hubInstance An instance of HubCoreLoop
- * @return $peerInstance An instance of HubPeer
- */
- public final static function createHubPeerBySocket ($peerSocket, HubCoreLoop $hubInstance) {
- // Get a new instance
- $peerInstance = new HubPeer();
-
- // Is the peer socket fine?
- if (!is_resource($peerSocket)) {
- // There is a problem with the socket
- throw new PeerSocketException (
- array(
- 'this' => $peerInstance,
- 'type' => gettype($peerSocket)
- ), self::EXCEPTION_PEER_SOCKET_INVALID
- );
- }
-
- // Set the socket
- $peerInstance->setPeerSocket($peerSocket);
-
- // Set connection timestamp
- $peerInstance->setConnectionTimestamp();
-
- // Set the hub instance
- $peerInstance->setHubInstance($hubInstance);
-
- // Return the instance
- return $peerInstance;
- }
-
- //----------------------------------------------------------
- // Public getter/setter
- //----------------------------------------------------------
-
- /**
- * Setter for peer socket
- *
- * @param $peerSocket The peer's socket
- * @return void
- */
- public final function setPeerSocket ($peerSocket) {
- if (is_resource($peerSocket)) {
- $this->peerSocket = $peerSocket;
- } else {
- $this->peerSocket = null;
- }
- }
-
- /**
- * Setter for connection timestamp only once
- *
- * @return void
- */
- public final function setConnectionTimestamp () {
- if ($this->connectTime == 0) $this->connectTime = time();
- }
-
- /**
- * Getter for a raw IP number
- *
- * @return $ip The peer's IP number
- * @throws BrokenPipeException If a socket has lost its connection to the peer
- */
- public final function getRawIP () {
- if (is_resource($this->peerSocket)) {
- // Get IP from socket
- @socket_getpeername($this->peerSocket, $ip);
-
- // Connection problems?
- if (socket_last_error() > 0) {
- // Throw an exception
- throw new BrokenPipeException(
- array(
- 'this' => $this,
- 'code' => socket_last_error()
- ), self::EXCEPTION_PEER_SOCKET_BROKEN
- );
- }
- } else {
- // Without a socket we cannot determine the right IP
- $ip = "0.0.0.0";
- }
-
- // And return it...
- return $ip;
- }
-
- /**
- * Getter for a validated peer IP
- *
- * @return $peerIP The peer's IP number
- */
- public final function getValidatedIP() {
- // Is the socket valid and IP not set?
- if ((is_resource($this->peerSocket)) && (socket_last_error() == 0) && (self::$peerIP == "0.0.0.0")) {
- // Get peer's IP number
- self::$peerIP = $this->getRawIP();
- } elseif ((is_resource($this->peerSocket)) && (socket_last_error() == 0)) {
- // Get current raw IP number for validation
- $ip = $this->getRawIP();
-
- // Check if the IP has changed
- if ($ip !== self::$peerIP) {
- // The IP number has changed!
- throw new IPSpoofingException(
- array(
- 'this' => $this,
- 'old_ip' => self::$peerIP,
- 'new_ip' => $ip
- ), self::EXCEPTION_PEER_IP_CHANGED
- );
- }
- }
-
- return self::$peerIP;
- }
-
- /**
- * Getter for number of authorization tries
- * @return $authRetries Authorization tries
- */
- public final function getAuthRetries () {
- return $this->authRetries;
- }
-
- //----------------------------------------------------------
- // Public methods
- //----------------------------------------------------------
-
- /**
- * Setter for HubCoreLoop instances
- *
- * @param $hubInstance An instance of a HubCoreLoop class
- * @return void
- */
- public final function setHubInstance(HubCoreLoop $hubInstance) {
- $this->hubInstance = $hubInstance;
- }
-
- /**
- * Getter for HubCoreLoop instances
- *
- * @return $hubInstance An instance of a HubCoreLoop class
- */
- public final function getHubInstance() {
- return $this->hubInstance;
- }
-
- /**
- * Getter for last sent message timestamp
- *
- * @return $lastSentMessageStamp Timestamp of last sent message
- */
- public final function getLastSentMessageStamp () {
- return $this->lastSentMessageStamp;
- }
-
- /**
- * Getter for last sent message
- *
- * @return $lastSentMessage Last sent message to this peer
- */
- public final function getLastSentMessage () {
- return $this->lastSentMessage;
- }
-
- /**
- * Determines wether the peer is authorized
- *
- * @return void
- */
- public final function ifPeerIsAuthorized () {
- return (($this->isAuthorized === true) && ($this->ifPeerNeedsAuthRequest() === false));
- }
-
- /**
- * Returns wether this peer needs to receive the AUTH command
- */
- public final function ifPeerNeedsAuthRequest () {
- return $this->needAuthRequest;
- }
-
- /**
- * Disconnects the peer with a special reason (status)
- *
- * @param $reason The special reason
- * @return void
- */
- public final function disconnectWithReason ($reason) {
- // Is the message set?
- if (!empty($reason)) {
- // Send the given message
- $this->sendMessage($reason);
- } else {
- // Send default "good bye"
- $this->sendMessage($this->getConfigInstance()->readConfig("hub_msg_bye"));
- }
-
- // Disconnect the client
- socket_shutdown($this->peerSocket, 2);
- socket_close($this->peerSocket);
- }
-
- /**
- * Sends a specified message to the peer's socket
- *
- * @param $message The special message
- * @return void
- * @throws BrokenPipeException If a pipe to a socket peer is lost
- */
- public final function sendMessage ($message) {
- if (empty($message)) {
- // Set default message
- $message = $this->getConfigInstance()->readConfig("hub_msg_bye");
- }
-
- // Debug message
- $this->getDebugInstance()->output(sprintf("[%s] Sending message \"%s\" to peer %s.<br />\n",
- __METHOD__,
- $message,
- $this->getValidatedIP()
- ));
-
- // Send it to the peer
- if (socket_write($this->peerSocket, $message."\n") !== false) {
- // Set the timestamp and message
- $this->lastSentMessage = $message;
- $this->lastSentMessageStamp = time();
- } else {
- // Message could not be sent
- throw new BrokenPipeException (
- array(
- 'this' => $this,
- 'code' => socket_last_error()
- ), self::EXCEPTION_PEER_SOCKET_BROKEN
- );
- }
- }
-
- /**
- * Asks the connected new peer for the authorization key
- *
- * @return void
- */
- public final function askAuthorizationKey () {
- if ($this->ifPeerNeedsAuthRequest()) {
- // This peer needs to receive the AUTH command so send it to him
- $this->sendMessage($this->getConfigInstance()->readConfig("hub_auth_request"));
-
- // Set it to done
- $this->needAuthRequest = false;
- }
- }
-
- /**
- * Determines wether the peer is a local admin (localhost connection)
- *
- * @return $isLocalAdmin Wether the peer is a local admin
- */
- public function ifPeerIsLocalAdmin () {
- // Get the remote IP and extract only the first three numbers
- $remoteArray = explode(".", self::$peerIP);
-
- // Is the peer a local admin?
- return (($remoteArray[0].".".$remoteArray[1].".".$remoteArray[2]) == "127.0.0");
- }
-
- /**
- * Enables the local admin and authorizes this peer
- *
- * @return void
- */
- public function enableLocalAdmin() {
- // Is this IP really local?
- if (($this->ifPeerIsLocalAdmin()) && (!$this->ifPeerIsAuthorized())) {
- // Then authorize him
- $this->isAuthorized = true;
- $this->needAuthRequest = false;
- } // END - if
- }
-
- /**
- * Says "hi" to the local admin
- *
- * @return void
- */
- public function sayHi2Admin () {
- // Send a message to him... ;-)
- $this->sendMessage("Local admin console is ready. Enter HELP for instructions.");
- }
-
- /**
- * Enables wether the peer is authorized
- *
- * @param $isAuthValid Wether the authorization wents fine
- * @return void
- */
- public function enableIsAuthorized ($isAuthValid = true) {
- $this->isAuthorized = true;
- }
-
- /**
- * Checks wether a HELLO has been sent and if not it will be send to the other peer
- *
- * @return $helloSent Wether a HELLO has been sent
- */
- public function ifHelloReceived () {
- // HELLO has been sent?
- if (!$this->helloSent) {
- // Read some data
- $read = $this->readFromSocket();
-
- // Is this a HELLO?
- if ($read == $this->getConfigInstance()->readConfig("hub_peer_hello")) {
- // All right! A HELLO has been received
- $this->helloSent = true;
- $this->getHubInstance()->getOutputInstance()->output(sprintf("[%s] Peer %s said HELLO to us.",
- __METHOD__,
- $this->getValidatedIP()
- ));
- } // END - if
- } // END - if
-
- // Return status
- return $this->helloSent;
- }
-
- /**
- * Wether this hub has replied our HELLO request
- *
- * @return $helloReplied Wether this hub has replied our HELLO request
- */
- public function ifHelloReplied () {
- if ((!$this->helloReplied) && ($this->lastSentMessage == $this->getConfigInstance()->readConfig("hub_peer_hello"))) {
- // Read some data
- $read = $this->readFromSocket();
-
- // Is this a HELLO?
- if ($read == $this->getConfigInstance()->readConfig("hub_hello_reply")) {
- // Is this the master IP?
- if ($this->getValidatedIP() == $this->getConfigInstance()->readConfig("hub_master_ip")) {
- // All right! A HELLO has been received
- $this->helloReplied = true;
- $this->getHubInstance()->getOutputInstance()->output(sprintf("[%s] The master hub at %s:%d replied our %s.",
- __METHOD__,
- $this->getValidatedIP(),
- $this->getConfigInstance()->readConfig("hub_master_port"),
- $this->getConfigInstance()->readConfig("hub_peer_hello")
- ));
- } else {
- // ELHOs from non-masters are not valid!
- $this->getHubInstance()->getOutputInstance()->output(sprintf("[%s] Peer %s replied our %s but is not the master hub!",
- __METHOD__,
- $this->getValidatedIP(),
- $this->getConfigInstance()->readConfig("hub_peer_hello")
- ));
- }
- } // END - if
- } // END - if
-
- // Return status
- return $this->helloReplied;
- }
-
- /**
- * Returns wether a ELHO (HELLO reply has been sent)
- *
- * @return $elhoSent Wether a ELHO has been sent to the peer
- */
- public final function ifELHOsent () {
- return $this->elhoSent;
- }
-
- /**
- * Replies a HELLO message with a ELHO message
- *
- * @return void
- */
- public function replyHelloMessage () {
- $this->sendMessage($this->getConfigInstance()->readConfig("hub_hello_reply"));
- $this->elhoSent = true;
- }
-
- /**
- * Handles pinging this peer
- *
- * @return void
- */
- public function handlePingPeer () {
- $lost = false;
-
- // Do we need to ping?
- if ((time() - $this->lastPinged) > $this->getConfigInstance()->readConfig("hub_ping_timeout")) {
- // Don't ping any masters! ;-)
- if ($this->getValidatedIP() != $this->getConfigInstance()->readConfig("hub_master_ip")) {
- // Debug message
- $this->getHubInstance()->getOutputInstance()->output(sprintf("[%s] Sending ping to peer %s...",
- $this->__toString(),
- $this->getValidatedIP()
- ));
-
- // Send out a PING and await a PONG
- $this->commandInstance->simpleExecute(
- $this->getConfigInstance()->readConfig("hub_peer_ping"),
- $this->getConfigInstance()->readConfig("hub_ping_reply")
- );
-
- // PONG received within last ping?
- if (($this->lastPonged < time()) && (($this->lastPonged - $this->lastPinged) < 0)) {
- // Not replied so far
- $this->missingPongs++;
-
- // Debug message
- $this->getHubInstance()->getOutputInstance()->output(sprintf("[%s] Ping not replied! Try: %d",
- $this->__toString(),
- $this->missingPongs
- ));
-
- // Limit reached?
- if ($this->missingPongs == $this->getConfigInstance()->readConfig("hub_ping_maxdrops")) {
- // This peer is lost
- $this->getHubInstance()->disconnectPeerWithReason("hub_peer_miss_pong");
- $lost = true;
- } // END - if
- } // END - if
-
- // Last time we pinged is now.
- $this->lastPinged = time();
-
- } // END - if
- } // END - if
-
- // Connection is lost?
- if ($lost === true) return false;
-
- // Awaiting PONG here
- if ($this->commandInstance->ifAwaitsCommand($this->getConfigInstance()->readConfig("hub_ping_reply"))) {
- // PONG received! :-) So reset all counters...
- $this->lastPonged = time();
- $this->missingPongs = 0;
-
- // Notify the loop about the ping-pong-time
- $this->getHubInstance()->updatePeerEntry($this, (time() - $this->lastPinged));
- } // END - if
- }
-
- /**
- * Handles any incoming commands from the master hub
- *
- * @return void
- */
- public function handleMasterRequests () {
- // Read the raw socket for data packages
- if ($this->commandInstance->awaitAnyCommand()) {
- // A command has been received from the master hub
- $command = $this->commandInstance->pull();
-
- // TODO Handle a command from the master here...
- } // END - if
- }
-
- /**
- * Reads raw data from the socket and trims leading/trailing spaces away.
- * Returns an empty string if no data has been received.
- *
- * @return $data Raw data from the underlaying socket
- * @throws BrokenPipeException If a socket has lost its connection to the peer
- */
- public function readFromSocket () {
- $data = "";
- $read = array($this->peerSocket);
- $write = null;
- $except = null;
- $num = socket_select($read, $write, $except, 0);
- if ($num > 0) {
- // Something has changed on a socket
- foreach ($read as $socket) {
- if (is_resource($socket)) {
- $data = trim(@socket_read($socket, 1024, PHP_NORMAL_READ));
- if (socket_last_error() > 0) {
- // Throw an exception
- throw new BrokenPipeException(
- array(
- 'this' => $this,
- 'code' => socket_last_error()
- ), self::EXCEPTION_PEER_SOCKET_BROKEN
- );
- }
- break;
- } // END - if
- } // END - foreach
- } // END - if
- return $data;
- }
-
-} // END - class
-
-// [EOF]
-?>