diff options
Diffstat (limited to 'vendor/gipfl/socket')
-rw-r--r-- | vendor/gipfl/socket/composer.json | 29 | ||||
-rw-r--r-- | vendor/gipfl/socket/src/ConnectionList.php | 87 | ||||
-rw-r--r-- | vendor/gipfl/socket/src/UnixSocketInspection.php | 89 | ||||
-rw-r--r-- | vendor/gipfl/socket/src/UnixSocketPeer.php | 102 |
4 files changed, 307 insertions, 0 deletions
diff --git a/vendor/gipfl/socket/composer.json b/vendor/gipfl/socket/composer.json new file mode 100644 index 0000000..cc19780 --- /dev/null +++ b/vendor/gipfl/socket/composer.json @@ -0,0 +1,29 @@ +{ + "name": "gipfl/socket", + "description": "Helpful ReactPHP socket utility classes", + "type": "library", + "license": "MIT", + "autoload": { + "psr-4": { + "gipfl\\Socket\\": "src/" + } + }, + "authors": [ + { + "name": "Thomas Gelf", + "email": "thomas@gelf.net" + } + ], + "config": { + "sort-packages": true + }, + "require": { + "php": ">=5.6.0", + "ext-posix": "*", + "ext-sockets": "*", + "evenement/evenement": ">=2.0", + "gipfl/json": ">=0.1", + "react/event-loop": ">=1.0", + "react/socket": ">=1.0" + } +} diff --git a/vendor/gipfl/socket/src/ConnectionList.php b/vendor/gipfl/socket/src/ConnectionList.php new file mode 100644 index 0000000..47ea489 --- /dev/null +++ b/vendor/gipfl/socket/src/ConnectionList.php @@ -0,0 +1,87 @@ +<?php + +namespace gipfl\Socket; + +use Evenement\EventEmitterInterface; +use Evenement\EventEmitterTrait; +use InvalidArgumentException; +use React\EventLoop\LoopInterface; +use React\Promise\Deferred; +use React\Socket\ConnectionInterface; +use SplObjectStorage; +use function React\Promise\all; + +/** + * @method ConnectionInterface current + */ +class ConnectionList extends SplObjectStorage implements EventEmitterInterface +{ + use EventEmitterTrait; + + const ON_ATTACHED = 'attached'; + const ON_DETACHED = 'detached'; + + /** @var LoopInterface */ + protected $loop; + + public function __construct(LoopInterface $loop) + { + $this->loop = $loop; + } + + #[\ReturnTypeWillChange] + public function attach($object, $info = null) + { + if (! $object instanceof ConnectionInterface || $info !== null) { + throw new InvalidArgumentException(sprintf( + 'Can attach only %s instances', + ConnectionInterface::class + )); + } + $object->on('close', function () use ($object) { + $this->detach($object); + }); + + parent::attach($object, $info); + $this->emit(self::ON_ATTACHED, [$object]); + } + + #[\ReturnTypeWillChange] + public function detach($object) + { + parent::detach($object); + $this->emit(self::ON_DETACHED, [$object]); + } + + public function close() + { + $pending = []; + foreach ($this as $connection) { + $pending[] = $this->closeConnection($connection, $this->loop); + } + + return all($pending); + } + + public static function closeConnection(ConnectionInterface $connection, LoopInterface $loop, $timeout = 5) + { + $deferred = new Deferred(); + $connection->end(); + if ($connection->isWritable() || $connection->isReadable()) { + $timer = $loop->addTimer($timeout, function () use ($connection, $deferred) { + $connection->close(); + }); + + $connection->on('close', function () use ($deferred, $timer) { + $this->loop->cancelTimer($timer); + $deferred->resolve(); + }); + } else { + $loop->futureTick(function () use ($deferred) { + $deferred->resolve(); + }); + } + + return $deferred->promise(); + } +} diff --git a/vendor/gipfl/socket/src/UnixSocketInspection.php b/vendor/gipfl/socket/src/UnixSocketInspection.php new file mode 100644 index 0000000..e1e9819 --- /dev/null +++ b/vendor/gipfl/socket/src/UnixSocketInspection.php @@ -0,0 +1,89 @@ +<?php + +namespace gipfl\Socket; + +use RuntimeException; +use React\Socket\ConnectionInterface; +use function array_shift; +use function file_exists; +use function is_int; +use function posix_getgrgid; +use function posix_getpwuid; +use function preg_split; +use function socket_get_option; +use function socket_import_stream; +use function stat; +use function strlen; +use function trim; + +class UnixSocketInspection +{ + /** + * @param ConnectionInterface $connection + * @return UnixSocketPeer + */ + public static function getPeer(ConnectionInterface $connection) + { + $socket = socket_import_stream($connection->stream); + $remotePid = static::getRemotePidFromSocket($socket); + $stat = static::statProcFile($remotePid); + $uid = $stat['uid']; + $gid = $stat['gid']; + $userInfo = static::getUserInfo($uid); + $gecosParts = preg_split('/,/', $userInfo['gecos']); + $fullName = trim(array_shift($gecosParts)); + $groupInfo = static::getGroupInfo($gid); + + return new UnixSocketPeer( + $remotePid, + $uid, + $gid, + $userInfo['name'], + strlen($fullName) ? $fullName : null, + $groupInfo['name'] + ); + } + + protected static function getRemotePidFromSocket($socket) + { + // SO_PEERCRED = 17 + $remotePid = socket_get_option($socket, SOL_SOCKET, 17); + if (! is_int($remotePid) || ! $remotePid > 0) { + throw new RuntimeException("Remote PID expected, got " . var_export($remotePid)); + } + + return $remotePid; + } + + protected static function statProcFile($pid) + { + $procDir = "/proc/$pid"; + if (file_exists($procDir)) { + return stat($procDir); + } else { + throw new RuntimeException("Got no proc dir ($procDir) for remote node"); + } + } + + protected static function getUserInfo($uid) + { + $userInfo = posix_getpwuid($uid); + + if ($userInfo === false) { + throw new RuntimeException("Unable to resolve remote UID '$uid'"); + } + + return $userInfo; + } + + protected static function getGroupInfo($gid) + { + $groupInfo = posix_getgrgid($gid); + + if ($groupInfo === false) { + throw new RuntimeException("Unable to resolve remote GID '$gid'"); + } + + return $groupInfo; + } +} diff --git a/vendor/gipfl/socket/src/UnixSocketPeer.php b/vendor/gipfl/socket/src/UnixSocketPeer.php new file mode 100644 index 0000000..409487b --- /dev/null +++ b/vendor/gipfl/socket/src/UnixSocketPeer.php @@ -0,0 +1,102 @@ +<?php + +namespace gipfl\Socket; + +use gipfl\Json\JsonSerialization; + +class UnixSocketPeer implements JsonSerialization +{ + /** @var int */ + protected $pid; + + /** @var int */ + protected $uid; + + /** @var int */ + protected $gid; + + /** @var string */ + protected $username; + + /** @var ?string */ + protected $fullName; + + /** @var string */ + protected $groupName; + + public function __construct($pid, $uid, $gid, $username, $fullName, $groupName) + { + $this->pid = $pid; + $this->uid = $uid; + $this->gid = $gid; + $this->username = $username; + $this->fullName = $fullName; + $this->groupName = $groupName; + } + + /** + * @return int + */ + public function getPid() + { + return $this->pid; + } + + /** + * @return int + */ + public function getUid() + { + return $this->uid; + } + + /** + * @return int + */ + public function getGid() + { + return $this->gid; + } + + /** + * @return string + */ + public function getUsername() + { + return $this->username; + } + + /** + * @return string|null + */ + public function getFullName() + { + return $this->fullName; + } + + /** + * @return string + */ + public function getGroupName() + { + return $this->groupName; + } + + public static function fromSerialization($any) + { + return new static($any->pid, $any->uid, $any->gid, $any->username, $any->fullName, $any->groupName); + } + + #[\ReturnTypeWillChange] + public function jsonSerialize() + { + return (object) [ + 'pid' => $this->pid, + 'uid' => $this->uid, + 'gid' => $this->gid, + 'username' => $this->username, + 'fullName' => $this->fullName, + 'groupName' => $this->groupName, + ]; + } +} |