pinetd2 /code/classes/Daemon/DNSd/Process.class.php

Language PHP Lines 175
MD5 Hash 815115a4466a5cd50c2651efa7371c7b
Repository https://github.com/blekkzor/pinetd2.git View Raw File View Project SPDX
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
<?php

namespace Daemon\DNSd;

use pinetd\Timer;
use pinetd\Logger;

class Process extends \pinetd\Process {
	private $master_link = NULL;
	private $status = 0;
	private $buf = '';
	private $db_engine;

	public function __construct($id, $daemon, $IPC, $node) {
		parent::__construct($id, $daemon, $IPC, $node);
	}

	public function pingMaster(&$extra = null) {
		if (!is_null($this->master_link)) $this->sendPkt('Ping');
	}

	public function checkMaster(&$extra = null) {
		if (!isset($this->localConfig['Master'])) return true;

		if (is_null($this->master_link)) {
			Logger::log(Logger::LOG_INFO, 'Connecting to DNSd master');

			$config = $this->localConfig['Master'];

			$this->master_link = @stream_socket_client('tcp://'.$config['Host'].':'.$config['Port'], $errno, $errstr, 4);

			if (!$this->master_link) {
				$this->master_link = NULL;
				Logger::log(Logger::LOG_WARN, 'Could not connect to DNSd master: ['.$errno.'] '.$errstr);
				return true;
			}

			// send introduction packet
			$pkt = $this->localConfig['Name']['_'] . pack('N', time());
			$pkt .= sha1($pkt.$config['Signature'], true);
			$pkt = 'BEGIN'.$pkt;
			fputs($this->master_link, pack('n', strlen($pkt)) . $pkt);
			$this->status = 0;
			$this->buf = '';

			$this->IPC->registerSocketWait($this->master_link, array($this, 'readData'), $foo = array());
		}
		return true;
	}

	protected function receivePacket($pkt) {
		if ($this->status == 0) {
			// check if we got what we want
			if ($pkt == 'BAD') {
				$this->IPC->removeSocket($this->master_link);
				fclose($this->master_link);
				Logger::log(Logger::LOG_WARN, 'Connection handshake refused by DNSd master (please check master log for details)');
				$this->master_link = NULL;
				return;
			}

			// check stuff about master
			$config = $this->localConfig['Master'];

			$good_sign = sha1(substr($pkt, 0, -20).$config['Signature'], true);
			if ($good_sign != substr($pkt, -20)) {
				Logger::log(Logger::LOG_WARN, 'Got bad signature from master, this is fishy!');
				$this->IPC->removeSocket($this->master_link);
				fclose($this->master_link);
				$this->master_link = NULL;
				return;
			}
			
			// strip signature
			$pkt = substr($pkt, 0, -20);

			// check timestamp
			list(,$stamp) = unpack('N', substr($pkt, -4));
			if (abs(time() - $stamp) > 5) {
				Logger::log(Logger::LOG_WARN, 'DNSd master is reporting bad timestamp, please look for forged packets and master/slave time synch (hint: install ntpd on both)!');
				$this->IPC->removeSocket($this->master_link);
				fclose($this->master_link);
				$this->master_link = NULL;
				return;
			}

			// check server name
			$master = substr($pkt, 0, -4);
			if ($master != $config['Name']) {
				Logger::log(Logger::LOG_WARN, 'DNSd master is note reporting the same name as the one found in config (hint: pay attention to case)');
				$this->IPC->removeSocket($this->master_link);
				fclose($this->master_link);
				$this->master_link = NULL;
				return;
			}

			$this->status = 1;

			// Get most recent change in tables
			$recent = $this->db_engine->lastUpdateDate();

			$this->sendPkt('DoSync', array($recent));

			return;
		}
		$pkt = @unserialize($pkt);
		switch($pkt['type']) {
			case 'dispatch':
				$data = $pkt['data'];
				$this->db_engine->processUpdate($data[0], $data[1]);
				break;
			case 'pong':
				// TODO: compute latency and let user know the master/slave latency :)
		}
	}

	public function domainHit($domain, $hit_count) {
		return $this->sendPkt('domainHit', array($domain, $hit_count));
	}

	protected function sendPkt($cmd, array $params = array()) {
		$data = serialize(array($cmd, $params));

		if (strlen($data) > 65535) return false;

		return fwrite($this->master_link, pack('n', strlen($data)).$data);
	}

	protected function parseBuffer() {
		while(!is_null($this->master_link)) {
			if (strlen($this->buf) < 2) break;
			list(,$len) = unpack('n', $this->buf);

			if (strlen($this->buf) < (2+$len)) break;

			$dat = substr($this->buf, 2, $len);
			$this->buf = substr($this->buf, $len+2);
			$this->receivePacket($dat);
		}
	}

	public function readData() {
		$dat = fread($this->master_link, 4096);
		if (($dat === false) || ($dat === '')) {
			Logger::log(Logger::LOG_WARN, "Lost link with DNSd master");
			$this->IPC->removeSocket($this->master_link);
			fclose($this->master_link);
			$this->master_link = NULL;
		}
		$this->buf .= $dat;
		$this->parseBuffer();
	}

	public function mainLoop() {
		parent::initMainLoop();

		$class = relativeclass($this, 'DbEngine');
		$this->db_engine = new $class($this, $this->localConfig, $this->IPC);
		$this->IPC->createPort('DNSd::DbEngine::'.$this->db_engine->unique(), $this->db_engine);

		Timer::addTimer(array($this, 'checkMaster'), 5, $foo = NULL, true);
		Timer::addTimer(array($this, 'pingMaster'), 120, $foo = NULL, true);
		$this->checkMaster();

		while(1) {
			$this->IPC->selectSockets(200000);
			Timer::processTimers();
		}
	}

	public function shutdown() {
	}
}
Back to Top