1<?php
2
3namespace Iodev\Whois\Loaders;
4
5use Iodev\Whois\Exceptions\ConnectionException;
6use Iodev\Whois\Exceptions\WhoisException;
7use Iodev\Whois\Helpers\TextHelper;
8
9class SocketLoader implements ILoader
10{
11    public function __construct($timeout = 60)
12    {
13        $this->setTimeout($timeout);
14    }
15
16    /** @var int */
17    private $timeout;
18
19    /** @var string|bool */
20    private $origEnv = false;
21
22    /**
23     * @return int
24     */
25    public function getTimeout()
26    {
27        return $this->timeout;
28    }
29
30    /**
31     * @param int $seconds
32     * @return $this
33     */
34    public function setTimeout($seconds)
35    {
36        $this->timeout = max(0, (int)$seconds);
37        return $this;
38    }
39
40    /**
41     * @param string $whoisHost
42     * @param string $query
43     * @return string
44     * @throws ConnectionException
45     * @throws WhoisException
46     */
47    public function loadText($whoisHost, $query)
48    {
49        $this->setupEnv();
50        if (!gethostbynamel($whoisHost)) {
51            $this->teardownEnv();
52            throw new ConnectionException("Host is unreachable: $whoisHost");
53        }
54        $this->teardownEnv();
55        $errno = null;
56        $errstr = null;
57        $handle = @fsockopen($whoisHost, 43, $errno, $errstr, $this->timeout);
58        if (!$handle) {
59            throw new ConnectionException($errstr, $errno);
60        }
61        if (false === fwrite($handle, $query)) {
62            throw new ConnectionException("Query cannot be written");
63        }
64        $text = "";
65        while (!feof($handle)) {
66            $chunk = fread($handle, 8192);
67            if (false === $chunk) {
68                throw new ConnectionException("Response chunk cannot be read");
69            }
70            $text .= $chunk;
71        }
72        fclose($handle);
73
74        return $this->validateResponse(TextHelper::toUtf8($text));
75    }
76
77    /**
78     * @param string $text
79     * @return mixed
80     * @throws WhoisException
81     */
82    private function validateResponse($text)
83    {
84        if (preg_match('~^WHOIS\s+.*?LIMIT\s+EXCEEDED~ui', $text, $m)) {
85            throw new WhoisException($m[0]);
86        }
87        return $text;
88    }
89
90    /**
91     *
92     */
93    private function setupEnv()
94    {
95        $this->origEnv = getenv('RES_OPTIONS');
96        putenv("RES_OPTIONS=retrans:1 retry:1 timeout:{$this->timeout} attempts:1");
97    }
98
99    /**
100     *
101     */
102    private function teardownEnv()
103    {
104        $this->origEnv === false ? putenv("RES_OPTIONS") : putenv("RES_OPTIONS={$this->origEnv}");
105    }
106}
107