<?php

//-----------------------------------------------------------------------
// LegacyWorlds Beta 5
// Game libraries
//
// lib/pcheck_thread.inc
//
// This library contains the code of the open proxy detector's scanning
// threads.
//
// Copyright(C) 2004-2008, DeepClone Development
//-----------------------------------------------------------------------


class pcheck_thread {

	/** The process ID of the thread.
	 */
	public $pid;

	/** The message queue the thread is bound to. Undefined in the
	 * parent process.
	 */
	private $queue;

	/** This property indicates that the thread has ended.
	 */
	public $ended = false;


	/** The constructor forks (throwing an exception on failure), then
	 * depending on whether it is in the parent process or in the child,
	 * returns or enters the instruction loop.
	 */
	public function __construct($queue) {
		$pid = @pcntl_fork();
		if ($pid == -1) {
			throw new Exception("Unable to fork");
		}

		if ($pid != 0) {
			// In the parent, store the child's PID and get going.
			$this->pid = $pid;
			$this->free = true;
			$this->queue = $queue;
			return;
		}

		// In the child, store our PID and the queue, then starts
		// waiting for instructions.
		$this->pid = posix_getpid();
		$this->queue = $queue;
		$this->waitLoop();
	}


	/** This method is called by the manager to send messages to the
	 * threads.
	 */
	public function send($message) {
		return $this->ended ? false : msg_send($this->queue, $this->pid, $message, true);
	}


	/** This method implements the instruction loop.
	 */
	private function waitLoop() {
		$quit = false;
		$error = false;

		do {
			$success = msg_receive($this->queue, $this->pid, $type, 32768, $message, true);
			if (! $success) {
				l::error("Child failed to receive a message");
				$quit = $error = true;
			} elseif ($message['type'] == 'QUIT') {
				$quit = true;
			} elseif ($message['type'] == 'SCAN') {
				list($host, $port) = $message['scan'];

				$found = $this->executeCheck($host, $port, "GET")
					|| $this->executeCheck($host, $port, "POST");

				if (!msg_send($this->queue, 1, array($this->pid, $host, $found), true)) {
					l::error("Child failed to send message");
					$quit = $error = true;
				}
			} else {
				l::notice("Unknown message {$message['type']} received");
			}
		} while (!$quit);

		exit($error ? 1 : 0);
	}


	/** This method will try to check one port for open proxy software, using
	 * a specific method (POST or GET).
	 */
	private function executeCheck($ipAddress, $port, $method) {
		$key = md5(uniqid(rand()));

		// Open the socket
		$socket = @fsockopen("tcp://$ipAddress", $port, $errno, $errstr, pcheck_manager::$timeout);
		if ($socket === FALSE) {
			return false;
		}

		// Make sure I/O doesn't timeout
		stream_set_timeout($socket, pcheck_manager::$timeout);

		// Send the request
		$result = @fwrite($socket, preg_replace('/__key__/', $key, pcheck_manager::$requests[$method]));
		if ($result !== FALSE) {
			$info = stream_get_meta_data($socket);
			if ($info['timed_out']) {
				$result = false;
			}
		}

		// Get the page
		if ($result !== FALSE) {
			$result = @fread($socket, 4096);
		}
		if ($result !== FALSE) {
			$info = stream_get_meta_data($socket);
			if ($info['timed_out']) {
				$result = false;
			}
		}

		// Close the socket
		@fclose($socket);
		if ($result === FALSE) {
			return false;
		}

		return preg_match("/Key is \\<$key\\>/", $result);
	}
}


?>