PageRenderTime 49ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/code/classes/pinetd/TCP/Base.class.php

https://github.com/blekkzor/pinetd2
PHP | 237 lines | 188 code | 23 blank | 26 comment | 28 complexity | a669e65555c0b8da5ddaabb3fe685326 MD5 | raw file
Possible License(s): GPL-2.0
  1. <?php
  2. /* Portable INET daemon v2 in PHP
  3. * Copyright (C) 2007 Mark Karpeles <mark@kinoko.fr>
  4. *
  5. * This program is free software; you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License as published by
  7. * the Free Software Foundation; either version 2 of the License, or
  8. * (at your option) any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU General Public License
  16. * along with this program; if not, write to the Free Software
  17. * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
  18. */
  19. namespace pinetd\TCP;
  20. use pinetd\Logger;
  21. use pinetd\SQL;
  22. use pinetd\IPC;
  23. use pinetd\Timer;
  24. use \Exception;
  25. abstract class Base extends \pinetd\DaemonBase {
  26. protected $port;
  27. protected $daemon;
  28. protected $IPC;
  29. protected $socket;
  30. protected $clients = array();
  31. protected $fclients = array(); // forked clients
  32. protected $protocol = 'tcp';
  33. protected $TLS = false;
  34. public function __construct($port, $daemon, &$IPC, $node) {
  35. $this->port = $port;
  36. $this->daemon = $daemon;
  37. $this->IPC = &$IPC;
  38. // create tcp server socket
  39. $this->loadLocalConfig($node);
  40. $ip = $this->localConfig['Network']['Bind']['Ip']['_'];
  41. if (isset($this->daemon['Ip'])) $ip = $this->daemon['Ip'];
  42. Logger::log(Logger::LOG_INFO, 'Loading '.get_class($this).' on port '.$port.', bound to ip '.$ip);
  43. $protocol = 'tcp';
  44. $context = stream_context_create(array());
  45. if ($this->daemon['SSL']) {
  46. $cert_name = $this->daemon['SSL'];
  47. if ($cert_name[0] == '~') {
  48. $cert_name = substr($cert_name, 1);
  49. $this->TLS = true;
  50. } else {
  51. $protocol = 'ssl';
  52. }
  53. $cert = $this->IPC->loadCertificate($cert_name);
  54. if (!$cert) {
  55. throw new Exception('ERROR: Trying to give me a non-existant certificate');
  56. }
  57. if (!isset($cert['ciphers'])) $cert['ciphers'] = 'ALL:!aNULL:!ADH:!eNULL:!LOW:!EXP:RC4+RSA:+HIGH:+MEDIUM';
  58. stream_context_set_option($context, array('ssl' => $cert));
  59. }
  60. $cert = $this->IPC->loadCertificate(strtolower($this->daemon['Service']));
  61. $this->protocol = $protocol;
  62. $this->socket = @stream_socket_server($protocol.'://'.$ip.':'.$this->daemon['Port'], $errno, $errstr, STREAM_SERVER_BIND | STREAM_SERVER_LISTEN, $context);
  63. if (!$this->socket) {
  64. throw new Exception('Error creating listening socket: ['.$errno.'] '.$errstr);
  65. }
  66. $this->IPC->registerSocketWait($this->socket, array(&$this, 'doAccept'), $foo = array(&$this->socket));
  67. }
  68. public function _ChildIPC_hasTLS() {
  69. return $this->hasTLS();
  70. }
  71. public function hasTLS() {
  72. return $this->TLS;
  73. }
  74. public function getDebug() {
  75. if (!isset($this->localConfig['Debug']['Target'])) return NULL;
  76. return $this->localConfig['Debug']['Target'];
  77. }
  78. public function _ChildIPC_canSUID() {
  79. if (!PINETD_CAN_SUID) return false;
  80. if (!isset($this->localConfig['Security']['SUID'])) return false;
  81. return $this->localConfig['Security']['SUID'];
  82. }
  83. public function canSUID() {
  84. return false;
  85. }
  86. public function _ChildIPC_canChroot() {
  87. if (!PINETD_CAN_CHROOT) return false;
  88. if (!isset($this->localConfig['Security']['Chroot'])) return false;
  89. return true;
  90. }
  91. public function canChroot() {
  92. return false;
  93. }
  94. function spawnClient($socket, $peer, $parent, $protocol) {
  95. $class = relativeclass($this, 'Client');
  96. return new $class($socket, $peer, $parent, $protocol);
  97. }
  98. public function shutdown() {
  99. Logger::log(Logger::LOG_INFO, 'Parent is killing its child on port '.$this->daemon['Port']);
  100. foreach($this->fclients as $pid => $data) {
  101. // todo: close all children
  102. $data['IPC']->stop();
  103. }
  104. foreach($this->clients as $port => $data) {
  105. $data['obj']->shutdown();
  106. $data['obj']->close();
  107. unset($this->clients[$port]);
  108. }
  109. fclose($this->socket);
  110. }
  111. public function quit() {
  112. $this->shutdown();
  113. $this->IPC->killSelf();
  114. $this->IPC->ping();
  115. exit;
  116. }
  117. public function doAccept($sock) {
  118. $news = @stream_socket_accept($sock, 0, $peer);
  119. if (!$news) return;
  120. $new = $this->spawnClient($news, $peer, $this, $this->protocol);
  121. if (!$new) {
  122. @fclose($news);
  123. return;
  124. }
  125. if (!$new->welcomeUser()) {
  126. $new->close();
  127. return;
  128. }
  129. Logger::log(Logger::LOG_DEBUG, 'Accepting new client on port '.$this->daemon['Port'].' from '.$peer);
  130. if ((isset($this->localConfig['Security']['Fork'])) && PINETD_CAN_FORK) {
  131. $pair = stream_socket_pair(STREAM_PF_UNIX, STREAM_SOCK_STREAM, 0);
  132. $pid = pcntl_fork();
  133. if ($pid > 0) {
  134. // we are parent
  135. fclose($news);
  136. fclose($pair[1]);
  137. unset($new);
  138. SQL::parentForked();
  139. $this->fclients[$pid] = array(
  140. 'pid' => $pid,
  141. 'peer' => $peer,
  142. 'socket' => $pair[0],
  143. 'IPC' => new \pinetd\IPC($pair[0], false, $this, $this->IPC),
  144. 'connect' => time(),
  145. );
  146. $this->IPC->registerSocketWait($pair[0], array($this->fclients[$pid]['IPC'], 'run'), $foobar = array(&$this->fclients[$pid]));
  147. return;
  148. }
  149. if ($pid == 0) {
  150. // we are child
  151. SQL::forked();
  152. Timer::reset();
  153. foreach($this->clients as $c) fclose($c['fd']);
  154. fclose($this->socket);
  155. fclose($pair[0]);
  156. $IPC = new IPC($pair[1], true, $foo = null, $bar = null);
  157. $IPC->ping(); // wait for parent to be ready
  158. Logger::setIPC($IPC);
  159. Logger::log(Logger::LOG_DEBUG, 'Daemon started for client, pid '.getmypid());
  160. $new->mainLoop($IPC);
  161. $IPC->Error('Exited from main loop!', 0);
  162. exit;
  163. }
  164. fclose($pair[0]);
  165. fclose($pair[1]);
  166. }
  167. $this->clients[$news] = array(
  168. 'socket' => $news,
  169. 'obj' => $new,
  170. 'peer' => $peer,
  171. 'connect' => time(),
  172. );
  173. $new->doResolve();
  174. $new->sendBanner();
  175. $this->IPC->registerSocketWait($news, array($new, 'readData'), $foo = array());
  176. }
  177. public function _ChildIPC_killSelf(&$daemon, $fd) {
  178. $daemon['IPC']->stop();
  179. unset($this->fclients[$daemon['pid']]);
  180. }
  181. public function killSelf($fd) {
  182. if (!isset($this->clients[$fd])) return;
  183. unset($this->clients[$fd]);
  184. $this->IPC->removeSocket($fd);
  185. }
  186. public function IPCDied($fd) {
  187. // $info = $this->IPC->getSocketInfo($fd);
  188. $this->IPC->removeSocket($fd);
  189. // $info = &$this->fclients[$info['pid']];
  190. // Logger::log(Logger::LOG_WARN, 'IPC for '.$info['pid'].' died');
  191. }
  192. public function childSignaled($res, $status, $signal = NULL) {
  193. if (count($this->fclients) == 0) return; // nothing to do
  194. // search what ended
  195. $ended = $this->fclients[$res];
  196. if (is_null($ended)) return; // we do not know what ended
  197. if (is_null($signal)) {
  198. Logger::log(Logger::LOG_DEBUG, 'Client with pid #'.$res.' exited');
  199. } else {
  200. Logger::log(Logger::LOG_INFO, 'Client with pid #'.$res.' died due to signal '.$signal);
  201. }
  202. unset($this->fclients[$res]);
  203. }
  204. public function mainLoop() {
  205. // We are in a "own fork" if we reach this, so let's rename our process!
  206. if (defined('PINETD_GOT_PROCTITLE')) {
  207. setproctitle('TCP: '.get_class($this).' on port '.$this->port);
  208. }
  209. while(1) {
  210. $this->IPC->selectSockets(200000);
  211. $this->processTimers();
  212. }
  213. }
  214. }