├── tests
├── bootstrap.php
├── phpunit.xml
└── MxToolbox
│ └── MxToolboxTest.php
├── src
└── MxToolbox
│ ├── Container
│ ├── MxContainer.php
│ └── MxToolboxContainer.php
│ ├── Exceptions
│ ├── MxToolboxLogicException.php
│ └── MxToolboxRuntimeException.php
│ ├── Python
│ └── quickDig.py
│ ├── autoload.php
│ ├── NetworkTools
│ ├── DigQueryParser.php
│ ├── SmtpDiagnosticParser.php
│ ├── QuickDig.php
│ ├── NetworkTools.php
│ └── SmtpServerChecks.php
│ ├── FileSystem
│ └── BlacklistsHostnameFile.php
│ ├── DataGrid
│ └── MxToolboxDataGrid.php
│ └── MxToolbox.php
├── .codeclimate.yml
├── composer.json
├── examples
├── smtpDiagnostics.php
├── refreshDnsblFile.php
├── getAdditionalInformation.php
├── nagiosUsage.php
├── myOwnBlackList.php
├── basicUsage.php
├── userDefinedBlacklistFilesPath.php
├── basicMultiprocessingUsage.php
└── complexExample.php
├── .travis.yml
├── LICENSE.md
└── README.md
/tests/bootstrap.php:
--------------------------------------------------------------------------------
1 |
12 |
13 | ./
14 |
15 |
16 |
17 |
18 | ../*
19 |
20 | ../examples
21 | ../tests
22 | ../vendor
23 | ../src/MxToolbox/autoload.php
24 |
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "mxtoolbox/mxtoolbox",
3 | "description": "IP blacklist tools",
4 | "type": "library",
5 | "license": "MIT",
6 | "keywords": [
7 | "mxtoolbox",
8 | "dnsbl",
9 | "blacklist"
10 | ],
11 | "authors": [
12 | {
13 | "name": "Lubomir Spacek",
14 | "homepage": "https://dns-tools.best-hosting.cz/"
15 | }
16 | ],
17 | "require": {
18 | "php": ">=5.6.5",
19 | "mxtoolbox-blacklists/mxtoolbox-blacklists": ">=0.0.1"
20 | },
21 | "require-dev": {
22 | "codeclimate/php-test-reporter": "dev-master"
23 | },
24 | "autoload": {
25 | "psr-4": {
26 | "MxToolbox\\": "src/MxToolbox",
27 | "MxToolbox\\Container\\": "src/MxToolbox/Container",
28 | "MxToolbox\\DataGrid\\": "src/MxToolbox/DataGrid",
29 | "MxToolbox\\Exceptions\\": "src/MxToolbox/Exceptions",
30 | "MxToolbox\\FileSystem\\": "src/MxToolbox/FileSystem",
31 | "MxToolbox\\NetworkTools\\": "src/MxToolbox/NetworkTools"
32 | }
33 | }
34 | }
--------------------------------------------------------------------------------
/examples/smtpDiagnostics.php:
--------------------------------------------------------------------------------
1 | setDig('/usr/bin/dig')
18 | // set dns resolver - required
19 | //->setDnsResolver('8.8.8.8')
20 | //->setDnsResolver('8.8.4.4')
21 | ->setDnsResolver('127.0.0.1');
22 |
23 | /**
24 | * Get SMTP server diagnostics responses
25 | */
26 | var_dump($test->getSmtpDiagnosticsInfo(
27 | '64.12.91.197',
28 | 'google.com',
29 | 'mxtool@example.com',
30 | 'test@example.com'
31 | ));
32 |
33 | } catch (MxToolboxRuntimeException $e) {
34 | echo $e->getMessage().PHP_EOL;
35 | } catch (MxToolboxLogicException $e) {
36 | echo $e->getMessage().PHP_EOL;
37 | }
38 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: php
2 |
3 | php:
4 | - 5.6
5 | - 7.0
6 | - hhvm
7 |
8 | matrix:
9 | allow_failures:
10 | - php: hhvm
11 |
12 | addons:
13 | apt:
14 | packages:
15 | - dnsutils
16 | code_climate:
17 | repo_token: 78f6ef7ba154b03b8f3abbd9913bef999191427add26561220fd0fecd4b2d1e0
18 |
19 | script:
20 | - phpunit --configuration tests/phpunit.xml --coverage-text --coverage-clover build/logs/clover.xml
21 |
22 | before_script:
23 | - travis_retry composer self-update
24 | - travis_retry composer update
25 |
26 | after_script:
27 | - CODECLIMATE_REPO_TOKEN="78f6ef7ba154b03b8f3abbd9913bef999191427add26561220fd0fecd4b2d1e0" ./vendor/bin/test-reporter --stdout > codeclimate.json
28 | - "curl -X POST -d @codeclimate.json -H 'Content-Type: application/json' -H 'User-Agent: Code Climate (PHP Test Reporter v0.1.1)' https://codeclimate.com/test_reports"
29 |
30 | before_install:
31 | - pip install --user codecov
32 |
33 | after_success:
34 | - bash <(curl -s https://codecov.io/bash)
35 |
36 | branches:
37 | only:
38 | - master
39 | - stable
40 |
41 |
--------------------------------------------------------------------------------
/examples/refreshDnsblFile.php:
--------------------------------------------------------------------------------
1 | setDig('/usr/bin/dig')
21 | // set dns resolver - required
22 | //->setDnsResolver('8.8.8.8')
23 | //->setDnsResolver('8.8.4.4')
24 | ->setDnsResolver('127.0.0.1');
25 |
26 | /*
27 | * Update the blacklistAlive.txt file - ! time-consuming process !
28 | */
29 | $test->updateAliveBlacklistFile();
30 |
31 | /*
32 | * Get new test array prepared for check (without any test results)
33 | */
34 | var_dump($test->getBlacklistsArray());
35 |
36 | } catch (MxToolboxRuntimeException $e) {
37 | echo $e->getMessage();
38 | } catch (MxToolboxLogicException $e) {
39 | echo $e->getMessage();
40 | }
41 |
--------------------------------------------------------------------------------
/examples/getAdditionalInformation.php:
--------------------------------------------------------------------------------
1 | setDig('/usr/bin/dig')
21 | // set dns resolver - required
22 | //->setDnsResolver('8.8.8.8')
23 | //->setDnsResolver('8.8.4.4')
24 | ->setDnsResolver('127.0.0.1');
25 |
26 | /* Get additional information for IP address
27 | * Return array structure:
28 | * ['ipAddress']
29 | * ['domainName']
30 | * ['ptrRecord']
31 | * ['mxRecords'][array]
32 | */
33 | var_dump($test->getDomainInformation('8.8.8.8'));
34 |
35 | } catch (MxToolboxRuntimeException $e) {
36 | echo $e->getMessage();
37 | } catch (MxToolboxLogicException $e) {
38 | echo $e->getMessage();
39 | }
40 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | # MX Toolbox is released under the MIT license.
2 |
3 | Copyright (c) 2016 Lubomir Spacek
4 |
5 | > Permission is hereby granted, free of charge, to any person obtaining
6 | > a copy of this software and associated documentation files (the "Software"),
7 | > to deal in the Software without restriction, including without limitation the
8 | > rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
9 | > sell copies of the Software, and to permit persons to whom the Software is
10 | > furnished to do so, subject to the following conditions:
11 |
12 | > The above copyright notice and this permission notice shall be included
13 | > in all copies or substantial portions of the Software.
14 |
15 | > THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | > IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | > FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18 | > THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
19 | > OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
20 | > ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
21 | > OTHER DEALINGS IN THE SOFTWARE.
22 |
--------------------------------------------------------------------------------
/src/MxToolbox/Python/quickDig.py:
--------------------------------------------------------------------------------
1 | import json, sys, subprocess, multiprocessing
2 |
3 |
4 | class DataObject:
5 | def __init__(self, params):
6 | self.domain, self.ip, self.digPath, self.resolver = params
7 |
8 |
9 | def run_process(obj_instance):
10 | domain, ip, digPath, resolver = obj_instance.domain, obj_instance.ip, obj_instance.digPath, obj_instance.resolver
11 | processed = []
12 | processed.append(subprocess.check_output([digPath + ' @' + resolver + ' ' + ip + '.' + domain + ' TXT'], shell=True))
13 | return processed
14 |
15 |
16 | if __name__ == '__main__':
17 | """ sys.argv[1] = test ip address (must be reversed)
18 | sys.argv[2] = dig path (/usr/bin/dig)
19 | sys.argv[3] = resolver ip address
20 | sys.argv[4] = dnsbl host names json array
21 | Usage example: python ./quickDig.py 2.0.0.127 /usr/bin/dig 127.0.0.1 []
22 | """
23 | try:
24 | blacklist = json.loads(sys.argv[4])
25 | objList = []
26 | for domain in blacklist:
27 | objList.append(DataObject((domain, sys.argv[1], sys.argv[2], sys.argv[3])))
28 | pool = multiprocessing.Pool()
29 | results = pool.map(run_process, objList)
30 | print(json.dumps(results))
31 | except Exception as e:
32 | print ('error')
33 |
--------------------------------------------------------------------------------
/src/MxToolbox/autoload.php:
--------------------------------------------------------------------------------
1 | 'MxToolbox.php',
9 | 'MxToolbox\IMxToolbox' => 'IMxToolbox.php',
10 | 'MxToolbox\Container\MxToolboxContainer' => 'Container/MxToolboxContainer.php',
11 | 'MxToolbox\Container\MxContainer' => 'Container/MxContainer.php',
12 | 'MxToolbox\Exceptions\MxToolboxLogicException' => 'Exceptions/MxToolboxLogicException.php',
13 | 'MxToolbox\Exceptions\MxToolboxRuntimeException' => 'Exceptions/MxToolboxRuntimeException.php',
14 | 'MxToolbox\FileSystem\BlacklistsHostnameFile' => 'FileSystem/BlacklistsHostnameFile.php',
15 | 'MxToolbox\DataGrid\MxToolboxDataGrid' => 'DataGrid/MxToolboxDataGrid.php',
16 | 'MxToolbox\NetworkTools\NetworkTools' => 'NetworkTools/NetworkTools.php',
17 | 'MxToolbox\NetworkTools\QuickDig' => 'NetworkTools/QuickDig.php',
18 | 'MxToolbox\NetworkTools\SmtpServerChecks' => 'NetworkTools/SmtpServerChecks.php',
19 | 'MxToolbox\NetworkTools\SmtpDiagnosticParser' => 'NetworkTools/SmtpDiagnosticParser.php',
20 | 'MxToolbox\NetworkTools\DigQueryParser' => 'NetworkTools/DigQueryParser.php'
21 | ];
22 |
23 | if (isset($map[$class]))
24 | require __DIR__ . DIRECTORY_SEPARATOR . $map[$class];
25 | }
26 | );
27 |
--------------------------------------------------------------------------------
/examples/nagiosUsage.php:
--------------------------------------------------------------------------------
1 | setDig('/usr/bin/dig')
21 | // set dns resolver - required
22 | ->setDnsResolver('127.0.0.1')
23 | // load default blacklists for dnsbl check - required in this example
24 | ->setBlacklists();
25 |
26 | /**
27 | * Check any IP address on all DNSBL
28 | */
29 | $test->checkIpAddressOnDnsbl('8.8.8.8');
30 |
31 | /**
32 | * Check any IP address on all DNSBL
33 | * Multiprocessing - much faster but experimental in v0.1.0
34 | */
35 | //$test->checkIpAddressOnDnsbl('8.8.8.8', true);
36 |
37 |
38 | /**
39 | * Nagios return POSIX code
40 | * https://nagios-plugins.org/doc/guidelines.html#AEN78
41 | * Positive check - return WARNING
42 | * All OK - return OK
43 | * Exception - return UNKNOWN
44 | */
45 | foreach ($test->getBlacklistsArray() as $list) {
46 | if($list['blPositive']) {
47 | print('WARNING');
48 | exit(1);
49 | }
50 | }
51 | print('OK');
52 | exit(0);
53 |
54 | } catch (MxToolboxRuntimeException $e) {
55 | print('UNKNOWN');
56 | exit(3);
57 | } catch (MxToolboxLogicException $e) {
58 | print('UNKNOWN');
59 | exit(3);
60 | } catch (Exception $e) {
61 | print('UNKNOWN');
62 | exit(3);
63 | }
64 |
--------------------------------------------------------------------------------
/examples/myOwnBlackList.php:
--------------------------------------------------------------------------------
1 | 'zen.spamhaus.org',
13 | 1 => 'xbl.spamhaus.org'
14 | );
15 |
16 | try {
17 |
18 | $test = new MxToolbox();
19 |
20 | /**
21 | * Configure MxToolbox
22 | */
23 | $test
24 | // path to the dig tool - required
25 | ->setDig('/usr/bin/dig')
26 | // set dns resolver - required
27 | //->setDnsResolver('8.8.8.8')
28 | //->setDnsResolver('8.8.4.4')
29 | ->setDnsResolver('127.0.0.1')
30 | // load user defined blacklists for dnsbl check
31 | ->setBlacklists($myBlacklist);
32 |
33 | /*
34 | * Check IP address on all DNSBL
35 | */
36 | $test->checkIpAddressOnDnsbl('8.8.8.8');
37 |
38 | /*
39 | * Get the same array but with a check results
40 | *
41 | * Return structure:
42 | * []['blHostName'] = dnsbl hostname
43 | * []['blPositive'] = true if IP address have the positive check
44 | * []['blPositiveResult'] = array() array of a URL addresses if IP address have the positive check
45 | * []['blResponse'] = true if DNSBL host name is alive or DNSBL responded during the test
46 | * []['blQueryTime'] = false or response time of a last dig query
47 | */
48 | var_dump($test->getBlacklistsArray());
49 |
50 | /*
51 | * Cleaning old results - REQUIRED only in loop before next test
52 | * TRUE = check responses for all DNSBL again (default value)
53 | * FALSE = only cleaning old results ([blResponse] => true)
54 | */
55 | $test->cleanBlacklistArray(false);
56 |
57 | } catch (MxToolboxRuntimeException $e) {
58 | echo $e->getMessage();
59 | } catch (MxToolboxLogicException $e) {
60 | echo $e->getMessage();
61 | }
62 |
--------------------------------------------------------------------------------
/examples/basicUsage.php:
--------------------------------------------------------------------------------
1 | setDig('/usr/bin/dig')
21 | // set dns resolver - required
22 | //->setDnsResolver('8.8.8.8')
23 | //->setDnsResolver('8.8.4.4')
24 | ->setDnsResolver('127.0.0.1')
25 | // load default blacklists for dnsbl check - optional
26 | ->setBlacklists();
27 |
28 | /**
29 | * Get test array prepared for check if you need (without any test results)
30 | */
31 | //var_dump($this->getBlacklistsArray());
32 |
33 | /**
34 | * Check IP address on all DNSBL
35 | */
36 | $test->checkIpAddressOnDnsbl('8.8.8.8');
37 |
38 | /**
39 | * Get the same array but with a check results
40 | *
41 | * Return structure:
42 | * []['blHostName'] = dnsbl hostname
43 | * []['blPositive'] = true if IP address have the positive check
44 | * []['blPositiveResult'] = array() array of a URL addresses if IP address have the positive check
45 | * []['blResponse'] = true if DNSBL host name is alive or DNSBL responded during the test
46 | * []['blQueryTime'] = false or response time of a last dig query
47 | */
48 |
49 | var_dump($test->getBlacklistsArray());
50 | /**
51 | * Cleaning old results - REQUIRED only in loop before next test
52 | * TRUE = check responses for all DNSBL again (default value)
53 | * FALSE = only cleaning old results ([blResponse] => true)
54 | */
55 | $test->cleanBlacklistArray(false);
56 |
57 | } catch (MxToolboxRuntimeException $e) {
58 | echo $e->getMessage();
59 | } catch (MxToolboxLogicException $e) {
60 | echo $e->getMessage();
61 | }
62 |
--------------------------------------------------------------------------------
/examples/userDefinedBlacklistFilesPath.php:
--------------------------------------------------------------------------------
1 | setDig('/usr/bin/dig')
21 | // set dns resolver - required
22 | //->setDnsResolver('8.8.8.8')
23 | //->setDnsResolver('8.8.4.4')
24 | ->setDnsResolver('127.0.0.1')
25 | // set user path to the blacklist files - optional
26 | ->setBlacklistFilePath(dirname(__FILE__) . '/../vendor/mxtoolbox-blacklists/mxtoolbox-blacklists/')
27 | // load default blacklists for dnsbl check - optional
28 | ->setBlacklists();
29 |
30 | /*
31 | * Check IP address on all DNSBL
32 | */
33 | $test->checkIpAddressOnDnsbl('8.8.8.8');
34 |
35 | /*
36 | * Get array with a check results
37 | *
38 | * Return structure:
39 | * []['blHostName'] = dnsbl hostname
40 | * []['blPositive'] = true if IP address have the positive check
41 | * []['blPositiveResult'] = array() array of a URL addresses if IP address have the positive check
42 | * []['blResponse'] = true if DNSBL host name is alive or DNSBL responded during the test
43 | * []['blQueryTime'] = false or response time of a last dig query
44 | */
45 | var_dump($test->getBlacklistsArray());
46 |
47 | /*
48 | * Cleaning old results - REQUIRED only in loop before next test
49 | * TRUE = check responses for all DNSBL again (default value)
50 | * FALSE = only cleaning old results ([blResponse] => true)
51 | */
52 | $test->cleanBlacklistArray(false);
53 |
54 | } catch (MxToolboxRuntimeException $e) {
55 | echo $e->getMessage();
56 | } catch (MxToolboxLogicException $e) {
57 | echo $e->getMessage();
58 | }
59 |
--------------------------------------------------------------------------------
/examples/basicMultiprocessingUsage.php:
--------------------------------------------------------------------------------
1 | setDig('/usr/bin/dig')
21 | // set dns resolver - required
22 | //->setDnsResolver('8.8.8.8')
23 | //->setDnsResolver('8.8.4.4')
24 | ->setDnsResolver('127.0.0.1')
25 | // load default blacklists for dnsbl check - optional
26 | ->setBlacklists();
27 |
28 | /**
29 | * Get test array prepared for check if you need (without any test results)
30 | */
31 | //var_dump($this->getBlacklistsArray());
32 |
33 | /**
34 | * Check IP address on all DNSBL
35 | * TRUE for using multiprocessing dig command (Python >=2.7 required!)
36 | * FALSE (default)
37 | */
38 | $test->checkIpAddressOnDnsbl('8.8.8.8', true);
39 |
40 | /**
41 | * Get the same array but with a check results
42 | *
43 | * Return structure:
44 | * []['blHostName'] = dnsbl hostname
45 | * []['blPositive'] = true if IP address have the positive check
46 | * []['blPositiveResult'] = array() array of a URL addresses if IP address have the positive check
47 | * []['blResponse'] = true if DNSBL host name is alive or DNSBL responded during the test
48 | * []['blQueryTime'] = false or response time of a last dig query
49 | */
50 |
51 | var_dump($test->getBlacklistsArray());
52 | /**
53 | * Cleaning old results - REQUIRED only in loop before next test
54 | * TRUE = check responses for all DNSBL again (default value)
55 | * FALSE = only cleaning old results ([blResponse] => true)
56 | */
57 | $test->cleanBlacklistArray(false);
58 |
59 | } catch (MxToolboxRuntimeException $e) {
60 | echo $e->getMessage();
61 | } catch (MxToolboxLogicException $e) {
62 | echo $e->getMessage();
63 | }
64 |
--------------------------------------------------------------------------------
/src/MxToolbox/NetworkTools/DigQueryParser.php:
--------------------------------------------------------------------------------
1 | setDig('/usr/bin/dig')
21 | // set dns resolver - required
22 | //->setDnsResolver('8.8.8.8')
23 | //->setDnsResolver('8.8.4.4')
24 | ->setDnsResolver('127.0.0.1')
25 | // set user path to the blacklist files - optional
26 | //->setBlacklistFilePath();
27 | // load default blacklists for dnsbl check - optional
28 | ->setBlacklists();
29 | /*
30 | * Get test array prepared for check (without any test results)
31 | */
32 | //var_dump($this->getBlacklistsArray());
33 |
34 | /*
35 | * Check IP address on all DNSBL
36 | */
37 | $test->checkIpAddressOnDnsbl('8.8.8.8');
38 |
39 | /*
40 | * Get the same array but with a check results
41 | *
42 | * Return structure:
43 | * []['blHostName'] = dnsbl hostname
44 | * []['blPositive'] = true if IP address have the positive check
45 | * []['blPositiveResult'] = array() array of a URL addresses if IP address have the positive check
46 | * []['blResponse'] = true if DNSBL host name is alive or DNSBL responded during the test
47 | * []['blQueryTime'] = false or response time of a last dig query
48 | */
49 |
50 | var_dump($test->getBlacklistsArray());
51 |
52 | /*
53 | * Cleaning old results - REQUIRED only in loop before next test
54 | * TRUE = check responses for all DNSBL again (default value)
55 | * FALSE = only cleaning old results ([blResponse] => true)
56 | */
57 | $test->cleanBlacklistArray(false);
58 |
59 | // Get SMTP server diagnostics responses
60 | // '64.12.91.197' is any public SMTP server
61 | var_dump($this->getSmtpDiagnosticsInfo(
62 | '64.12.91.197',
63 | 'google.com',
64 | 'mxtool@example.com',
65 | 'test@example.com'
66 | ));
67 |
68 | /* Get additional information for IP address
69 | * Array structure:
70 | * ['ipAddress']
71 | * ['domainName']
72 | * ['ptrRecord']
73 | * ['mxRecords'][array]
74 | */
75 | var_dump($test->getDomainInformation('8.8.8.8'));
76 |
77 | // get array with dns resolvers
78 | var_dump($test->getDnsResolvers());
79 |
80 | // get DIG path
81 | var_dump($test->getDigPath());
82 |
83 | } catch (MxToolboxRuntimeException $e) {
84 | echo $e->getMessage();
85 | } catch (MxToolboxLogicException $e) {
86 | echo $e->getMessage();
87 | }
88 |
--------------------------------------------------------------------------------
/src/MxToolbox/Container/MxToolboxContainer.php:
--------------------------------------------------------------------------------
1 | container['quickDig']) || !$this->container['quickDig'] instanceof QuickDig)
38 | $this->container['quickDig'] = new QuickDig($this->createServiceNetworkTool());
39 | return $this->container['quickDig'];
40 | }
41 |
42 | /**
43 | * Create service NetworkTool
44 | * @return NetworkTools
45 | */
46 | protected function createServiceNetworkTool()
47 | {
48 | if (!isset($this->container['netTool']) || !$this->container['netTool'] instanceof NetworkTools)
49 | $this->container['netTool'] = new NetworkTools();
50 | return $this->container['netTool'];
51 | }
52 |
53 | /**
54 | * Create service BlacklistsHostnameFile
55 | * @return BlacklistsHostnameFile
56 | */
57 | protected function createServiceBlacklistsHostnameFile()
58 | {
59 | if (!isset($this->container['fileSys']) || !$this->container['fileSys'] instanceof BlacklistsHostnameFile)
60 | $this->container['fileSys'] = new BlacklistsHostnameFile();
61 | return $this->container['fileSys'];
62 | }
63 |
64 | /**
65 | * Create service MxToolboxDataGrid
66 | * @return MxToolboxDataGrid
67 | */
68 | protected function createServiceMxToolboxDataGrid()
69 | {
70 | if (!isset($this->container['dataGrid']) || !$this->container['dataGrid'] instanceof MxToolboxDataGrid)
71 | $this->container['dataGrid'] = new MxToolboxDataGrid(
72 | $this->createServiceBlacklistsHostnameFile(),
73 | $this->createServiceNetworkTool()
74 | );
75 | return $this->container['dataGrid'];
76 | }
77 |
78 | /**
79 | * Create service MxToolbox\NetworkTools\SmtpServerChecks
80 | * @param string $addr
81 | * @param string $myHostName
82 | * @param string $mailFrom
83 | * @param string $mailRcptTo
84 | * @return SmtpServerChecks
85 | */
86 | protected function createServiceSmtpServerChecks($addr, $myHostName, $mailFrom, $mailRcptTo)
87 | {
88 | if (!isset($this->container['smtpDiagnostics']) || !$this->container['smtpDiagnostics'] instanceof SmtpServerChecks)
89 | $this->container['smtpDiagnostics'] = new SmtpServerChecks(
90 | $this->createServiceNetworkTool(),
91 | $addr,
92 | $myHostName,
93 | $mailFrom,
94 | $mailRcptTo
95 | );
96 | return $this->container['smtpDiagnostics'];
97 | }
98 |
99 | }
100 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # MXToolbox
2 |
3 | [](https://github.com/heximcz/mxtoolbox/releases)
4 | [](https://travis-ci.org/heximcz/mxtoolbox)
5 | [](https://github.com/heximcz/mxtoolbox)
6 | [](https://github.com/heximcz/mxtoolbox/blob/master/LICENSE.md)
7 | [](https://codecov.io/github/heximcz/mxtoolbox?branch=master)
8 | [](https://codeclimate.com/github/heximcz/mxtoolbox)
9 |
10 | ## Homepage
11 | [DNS TOOLS](https://dns-tools.best-hosting.cz/)
12 |
13 | ## Prerequisites
14 |
15 | - PHP > 5.6.x
16 | - Installed dnsutils (dig)
17 |
18 | ## Installation / Usage
19 |
20 | - 1. Via composer
21 |
22 | ```
23 | composer require mxtoolbox/mxtoolbox
24 | ```
25 |
26 | - 2. Create a composer.json defining your dependencies.
27 |
28 | ```json
29 | {
30 | "require": {
31 | "mxtoolbox/mxtoolbox": ">=0.0.1"
32 | }
33 | }
34 | ```
35 |
36 | - 3. Example usage:
37 |
38 | ``` php
39 | setDig('/usr/bin/dig')
56 | // set dns resolver - required
57 | //->setDnsResolver('8.8.8.8')
58 | //->setDnsResolver('8.8.4.4')
59 | ->setDnsResolver('127.0.0.1')
60 | // load default blacklists for dnsbl check - optional
61 | ->setBlacklists();
62 |
63 | /**
64 | * Get test array prepared for check if you need (without any test results)
65 | */
66 | //var_dump($this->getBlacklistsArray());
67 |
68 | /**
69 | * Check IP address on all DNSBL
70 | */
71 | $test->checkIpAddressOnDnsbl('8.8.8.8');
72 |
73 | /**
74 | * Get the same array but with a check results
75 | *
76 | * Return structure:
77 | * []['blHostName'] = dnsbl hostname
78 | * []['blPositive'] = true if IP address have the positive check
79 | * []['blPositiveResult'] = array() array of a URL addresses if IP address have the positive check
80 | * []['blResponse'] = true if DNSBL host name is alive or DNSBL responded during the test
81 | * []['blQueryTime'] = false or response time of a last dig query
82 | */
83 |
84 | var_dump($test->getBlacklistsArray());
85 | /**
86 | * Cleaning old results - REQUIRED only in loop before next test
87 | * TRUE = check responses for all DNSBL again (default value)
88 | * FALSE = only cleaning old results ([blResponse] => true)
89 | */
90 | $test->cleanBlacklistArray(false);
91 |
92 | } catch (MxToolboxRuntimeException $e) {
93 | echo $e->getMessage();
94 | } catch (MxToolboxLogicException $e) {
95 | echo $e->getMessage();
96 | }
97 | ```
98 |
99 | [More examples](https://github.com/heximcz/mxtoolbox/tree/master/examples)
100 |
101 | ## License
102 |
103 | [MIT](https://github.com/heximcz/mxtoolbox/blob/master/LICENSE.md)
104 |
--------------------------------------------------------------------------------
/src/MxToolbox/NetworkTools/SmtpDiagnosticParser.php:
--------------------------------------------------------------------------------
1 | Service ready
24 | 221 Service closing transmission channel
25 | 250 Requested mail action okay, completed
26 | 251 User not local; will forward to
27 | 252 Cannot VRFY user, but will accept message and attempt delivery
28 | 354 Start mail input; end with .
29 | 421 Service not available, closing transmission channel
30 | 450 Requested mail action not taken: mailbox unavailable
31 | 451 Requested action aborted: local error in processing
32 | 452 Requested action not taken: insufficient system storage
33 | 500 Syntax error, command unrecognised
34 | 501 Syntax error in parameters or arguments
35 | 502 Command not implemented
36 | 503 Bad sequence of commands
37 | 504 Command parameter not implemented
38 | 521 does not accept mail (see rfc1846)
39 | 530 Access denied (???a Sendmailism)
40 | 550 Requested action not taken: mailbox unavailable
41 | 551 User not local; please try
42 | 552 Requested mail action aborted: exceeded storage allocation
43 | 553 Requested action not taken: mailbox name not allowed
44 | 554 Transaction failed
45 | */
46 |
47 | /**
48 | * Test if START TLS is present in EHLO array
49 | * @param array $smtpOutput Output array from EHLO SMTP command
50 | * @return bool
51 | */
52 | public function isTls($smtpOutput)
53 | {
54 | foreach ($smtpOutput as $value) {
55 | if (preg_match('/^250\-STARTTLS/', $value))
56 | return true;
57 | }
58 | return false;
59 | }
60 |
61 | /**
62 | * Check if PTR record corresponds with SMTP EHLO answer
63 | * @param array $smtpOutput
64 | * @param string $ptrRecord
65 | * @return bool
66 | */
67 | public function isValidHostname($smtpOutput, $ptrRecord)
68 | {
69 | foreach ($smtpOutput as $value) {
70 | if (preg_match('/^250\-' . preg_quote(strtolower($ptrRecord), '.-') . '/', $value))
71 | return true;
72 | }
73 | return false;
74 |
75 | }
76 |
77 | /**
78 | * Find ip address from ptr record, TRUE = all OK and not a reverse DNS mismatch
79 | * @param string $addr IP address
80 | * @param array $aRecords array from dns_get_record($info['ptrRecord'], DNS_A)
81 | * @return bool
82 | */
83 | public function isReverseDnsMismatch($addr, $aRecords)
84 | {
85 | foreach ($aRecords as $idx => $value) {
86 | if ($value['ip'] == $addr)
87 | return true;
88 | }
89 | return false;
90 | }
91 |
92 | /**
93 | * Check SMTP banner response and PTR match
94 | * @param string $smtpBanner
95 | * @param string $ptrRecord
96 | * @return bool
97 | */
98 | public function isReverseDnsInBanner($smtpBanner, $ptrRecord)
99 | {
100 | if (preg_match('/^220/', $smtpBanner)
101 | && preg_match('/' . preg_quote(strtolower($ptrRecord), '.-') . '/', $smtpBanner)
102 | )
103 | return true;
104 | return false;
105 | }
106 |
107 | //string(55) "554 5.7.1 : Relay access denied"
108 |
109 | /**
110 | * Is relay access denied, code 250 is open relay!
111 | * @param string $rcptTo
112 | * @return bool
113 | */
114 | public function isOpenRelay($rcptTo)
115 | {
116 | if (!preg_match('/^250/', $rcptTo))
117 | return true;
118 | return false;
119 | }
120 |
121 | }
122 |
--------------------------------------------------------------------------------
/src/MxToolbox/NetworkTools/QuickDig.php:
--------------------------------------------------------------------------------
1 | netTool = $netTool;
40 | $this->digParser = new DigQueryParser();
41 | }
42 |
43 | /**
44 | * Start dig multiprocessing - fast process how to run multiple dig tasks in the same time
45 | * @param string $addr
46 | * @param array $testResult
47 | * @return $this
48 | */
49 | public function getJsonFromDigMultiprocess($addr, &$testResult)
50 | {
51 | $this->ipAddress = $addr;
52 | $this->testResult = &$testResult;
53 | $this->netTool->ipValidator($this->ipAddress);
54 | $this->ipAddress = $this->netTool->getIpAddressFromDomainName($this->ipAddress);
55 | if (!count($this->testResult) > 0)
56 | throw new MxToolboxRuntimeException(sprintf('Array is empty for dig checks in: %s\%s.', get_class(), __FUNCTION__));
57 | // prepare domain names only for python script
58 | $dnsblDomainNames = array();
59 | foreach ($this->testResult as $item) {
60 | if ($item['blResponse'])
61 | $dnsblDomainNames[] = $item['blHostName'];
62 | }
63 | // call python script
64 | $this->digOutput = shell_exec(
65 | 'python ' .
66 | dirname(__FILE__) .
67 | DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . 'Python' . DIRECTORY_SEPARATOR . 'quickDig.py ' .
68 | $this->netTool->reverseIP($this->ipAddress) . ' ' .
69 | $this->netTool->getDigPath() . ' ' .
70 | $this->netTool->getRandomDNSResolverIP() . ' ' .
71 | escapeshellarg(json_encode($dnsblDomainNames))
72 | );
73 | // check errors
74 | if($this->digOutput == 'error')
75 | throw new MxToolboxRuntimeException('Python multiprocessing script exception!');
76 | // parse json to array
77 | if(!empty($this->digOutput) && $this->isJson($this->digOutput)) {
78 | $this->digOutput = json_decode($this->digOutput);
79 | return $this;
80 | }
81 | throw new MxToolboxRuntimeException('Python digOutput is empty or does not json format.');
82 | }
83 |
84 | /**
85 | * Parse data array returned from multiprocessing
86 | * @return $this
87 | */
88 | public function parseDataFromMultiprocessing()
89 | {
90 | if(is_array($this->digOutput) && count($this->digOutput) > 0) {
91 |
92 | foreach ($this->testResult as &$blackList) {
93 | if ($digOutput = $this->searchDigResult($blackList['blHostName'])) {
94 | if ($this->digParser->isNoError($digOutput)) {
95 | $blackList['blPositive'] = true;
96 | $blackList['blPositiveResult'] = $this->digParser->getPositiveUrlAddresses($digOutput);
97 | }
98 | $blackList['blQueryTime'] = $this->digParser->getQueryTime($digOutput);
99 | }
100 | }
101 | //var_dump($this->testResult);
102 | return $this;
103 | }
104 | throw new MxToolboxRuntimeException('DigOutput is empty or does not array. Maybe call getJsonFromDigMultiprocess() first.');
105 | }
106 |
107 | /**
108 | * Search dig output for a specific dnsbl domain
109 | * @param string $domainName
110 | * @return false|string
111 | */
112 | private function searchDigResult($domainName) {
113 | foreach ($this->digOutput as $item) {
114 | if (($firstLine = strtok($item[0], "\n")) !== false) {
115 | if ($this->digParser->isDomainNameInString($domainName, $firstLine))
116 | return $item[0];
117 | }
118 | }
119 | return false;
120 | }
121 |
122 | /**
123 | * is json
124 | * @param string $string
125 | * @return bool
126 | */
127 | private function isJson($string) {
128 | json_decode($string);
129 | return (json_last_error() == JSON_ERROR_NONE);
130 | }
131 |
132 | }
133 |
--------------------------------------------------------------------------------
/src/MxToolbox/FileSystem/BlacklistsHostnameFile.php:
--------------------------------------------------------------------------------
1 | blacklistHostNames) && count($this->blacklistHostNames) > 0)
37 | return $this->blacklistHostNames;
38 | throw new MxToolboxLogicException('Array is empty, load blacklist first.');
39 | }
40 |
41 | /**
42 | * Load blacklists host names from a file
43 | *
44 | * @param string $fileName
45 | * @throws MxToolboxRuntimeException;
46 | * @throws MxToolboxLogicException;
47 | * @return $this
48 | */
49 | public function loadBlacklistsFromFile($fileName)
50 | {
51 | // user not defined any path
52 | if (empty($this->blacklistPath))
53 | $this->setBlacklistFilePath();
54 |
55 | $blFile = $this->blacklistPath . $fileName;
56 | if (!is_readable($blFile))
57 | throw new MxToolboxRuntimeException("Blacklists file does not exist in: " . $blFile, 400);
58 |
59 | if (!($this->blacklistHostNames = file($blFile, FILE_SKIP_EMPTY_LINES | FILE_IGNORE_NEW_LINES)) === false) {
60 | if (!count($this->blacklistHostNames) > 0) {
61 | throw new MxToolboxLogicException(sprintf('Blacklist file' . $blFile . ' is empty in %s\%s()',
62 | get_class(), __FUNCTION__));
63 | }
64 | return $this;
65 | }
66 | throw new MxToolboxRuntimeException(sprintf('Cannot get contents from: ' . $blFile . ' in %s\%s()',
67 | get_class(), __FUNCTION__), 500);
68 | }
69 |
70 | /**
71 | * Build new file with alive DNSBLs host names
72 | *
73 | * @param array $aliveBlacklists
74 | * @return $this
75 | * @throws MxToolboxRuntimeException
76 | */
77 | public function makeAliveBlacklistFile(&$aliveBlacklists)
78 | {
79 | if (!array_key_exists('blHostName', $aliveBlacklists[0]))
80 | throw new MxToolboxRuntimeException("Cannot found index ['blHostName'] in array. Build test array first.");
81 |
82 | $blAliveFileTmp = $this->blacklistPath . 'blacklistsAlive.tmp';
83 | $blAliveFileOrg = $this->blacklistPath . 'blacklistsAlive.txt';
84 |
85 | // create temp file
86 | if (!@$file = fopen($blAliveFileTmp, 'w'))
87 | throw new MxToolboxRuntimeException ('Cannot create new file: ' . $blAliveFileTmp);
88 |
89 | foreach ($aliveBlacklists as $blackList) {
90 | if ($blackList['blResponse']) {
91 | fwrite($file, $blackList['blHostName'] . PHP_EOL);
92 | }
93 | }
94 | fclose($file);
95 |
96 | // check file size
97 | if (!filesize($blAliveFileTmp) > 0) {
98 | @unlink($blAliveFileTmp);
99 | throw new MxToolboxRuntimeException ('Blacklist temp file is empty: ' . $blAliveFileTmp);
100 | }
101 | // create new blacklist file from temp
102 | if (!rename($blAliveFileTmp, $blAliveFileOrg))
103 | throw new MxToolboxRuntimeException('Cannot create Alive Blacklist file. Rename the file failed.');
104 |
105 | return $this;
106 | }
107 |
108 | /**
109 | * Delete alive blacklists file if exist
110 | * @return $this
111 | */
112 | public function deleteAliveBlacklist()
113 | {
114 | if (empty($this->blacklistPath))
115 | $this->setBlacklistFilePath();
116 | $blAliveFile = $this->blacklistPath . 'blacklistsAlive.txt';
117 | if (is_readable($blAliveFile))
118 | @unlink($blAliveFile);
119 | return $this;
120 | }
121 |
122 | /**
123 | * Set blacklist file path
124 | * @param string|boolean $path - default FALSE for auto configuration
125 | * @return $this
126 | */
127 | public function setBlacklistFilePath($path=false)
128 | {
129 | // user configuration path
130 | if (is_string($path)) {
131 | $this->blacklistPath = $path;
132 | return $this;
133 | }
134 |
135 | // standard composer installation
136 | $this->blacklistPath = dirname(__FILE__) .
137 | DIRECTORY_SEPARATOR . '..' .
138 | DIRECTORY_SEPARATOR . '..' .
139 | DIRECTORY_SEPARATOR . '..' .
140 | DIRECTORY_SEPARATOR . '..' .
141 | DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR .
142 | 'mxtoolbox-blacklists' . DIRECTORY_SEPARATOR .
143 | 'mxtoolbox-blacklists' . DIRECTORY_SEPARATOR;
144 |
145 | if (!file_exists($this->blacklistPath . 'blacklists.txt')) {
146 | // install blacklist files directly to mxtoolbox (travis,...)
147 | $this->blacklistPath = dirname(__FILE__) .
148 | DIRECTORY_SEPARATOR . '..' .
149 | DIRECTORY_SEPARATOR . '..' .
150 | DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR .
151 | 'vendor' . DIRECTORY_SEPARATOR .
152 | 'mxtoolbox-blacklists' . DIRECTORY_SEPARATOR .
153 | 'mxtoolbox-blacklists' . DIRECTORY_SEPARATOR;
154 | if (!file_exists($this->blacklistPath . 'blacklists.txt')) {
155 | throw new MxToolboxRuntimeException('Standard path to the blacklist file not exist.');
156 | }
157 | }
158 | return $this;
159 | }
160 |
161 | }
162 |
--------------------------------------------------------------------------------
/src/MxToolbox/DataGrid/MxToolboxDataGrid.php:
--------------------------------------------------------------------------------
1 | string '',
28 | * ['blPositive'] => boolean ,
29 | * ['blPositiveResult'] => array() ,
30 | * ['blResponse'] => boolean ,
31 | * ['blQueryTime'] => boolean
32 | * ));
33 | *
34 | * @var array for dnsbl tests
35 | */
36 | protected $testStructure;
37 | /** @var NetworkTools object */
38 | private $netTool;
39 | /** @var BlacklistsHostnameFile object */
40 | private $fileSys;
41 |
42 | /**
43 | * MxToolboxDataGrid constructor.
44 | * @param BlacklistsHostnameFile $fileSys
45 | * @param NetworkTools $netTool
46 | */
47 | public function __construct(BlacklistsHostnameFile $fileSys, NetworkTools $netTool)
48 | {
49 | $this->fileSys = $fileSys;
50 | $this->netTool = $netTool;
51 | }
52 |
53 | /**
54 | * Return complete array with tests
55 | * @return array
56 | * @throws MxToolboxLogicException
57 | */
58 | public function &getTestResultArray()
59 | {
60 | if ($this->isArrayInitialized($this->testStructure))
61 | return $this->testStructure;
62 | throw new MxToolboxLogicException(sprintf('Array is empty in %s\%s(), set first test results array.', get_class(), __FUNCTION__));
63 | }
64 |
65 | /**
66 | * Load alive blacklists and create test array.
67 | * @see BlacklistsHostnameFile::loadBlacklistsFromFile()
68 | * @param array $blacklistHostNames - optional (you may use your own blacklist array, default NULL)
69 | * @return $this
70 | * @throws MxToolboxRuntimeException;
71 | * @throws MxToolboxLogicException;
72 | */
73 | public function buildBlacklistHostNamesArray($blacklistHostNames = NULL)
74 | {
75 | if (is_null($blacklistHostNames) || !is_array($blacklistHostNames)) {
76 | $this->fileSys->loadBlacklistsFromFile('blacklistsAlive.txt');
77 | $this->setTestResultArray($this->fileSys->getBlacklistsHostNames(), true);
78 | return $this;
79 | }
80 | // build user own blacklist array
81 | $this->setTestResultArray($blacklistHostNames, false, true);
82 | return $this;
83 | }
84 |
85 | /**
86 | * Load default blacklist and check all for alive
87 | * @see BlacklistsHostnameFile::makeAliveBlacklistFile
88 | * @return $this
89 | * @throws MxToolboxLogicException
90 | * @throws MxToolboxRuntimeException
91 | */
92 | public function buildNewBlacklistHostNames()
93 | {
94 | $this->fileSys->loadBlacklistsFromFile('blacklists.txt');
95 | // set default blacklists to $testResults
96 | $this->setTestResultArray($this->fileSys->getBlacklistsHostNames(), false);
97 | // set only alive host names
98 | $this->netTool->setDnsblResponse($this->getTestResultArray());
99 | // check all for alive
100 | $this->fileSys->makeAliveBlacklistFile($this->getTestResultArray());
101 | // build array from blacklistAlive file
102 | $this->buildBlacklistHostNamesArray();
103 | return $this;
104 | }
105 |
106 | /**
107 | * Build the array for check DNSBLs
108 | * @param array $blacklistHostNamesArray
109 | * @param bool $alive TRUE is set default blResponse (usually load from alive file)
110 | * @param bool $ownBlacklist
111 | * @return $this
112 | * @throws MxToolboxLogicException
113 | */
114 | protected function setTestResultArray($blacklistHostNamesArray, $alive = true, $ownBlacklist = false)
115 | {
116 | if ($this->isArrayInitialized($blacklistHostNamesArray)) {
117 | $this->testStructure = array();
118 | foreach ($blacklistHostNamesArray as $index => $blackList) {
119 | $this->testStructure[$index]['blHostName'] = $blackList;
120 | $this->testStructure[$index]['blPositive'] = false;
121 | $this->testStructure[$index]['blPositiveResult'] = array();
122 | $this->testStructure[$index]['blResponse'] = $alive;
123 | if ($ownBlacklist)
124 | $this->testStructure[$index]['blResponse'] = $this->netTool->isDnsblResponse($blackList);
125 | $this->testStructure[$index]['blQueryTime'] = false;
126 | }
127 | unset($blackList);
128 | return $this;
129 | }
130 | throw new MxToolboxLogicException(sprintf('Input parameter is empty or is not a array in %s\%s()', get_class(), __FUNCTION__));
131 | }
132 |
133 | /**
134 | * Clean previous results, reinitialize array
135 | * @param bool $checkResponse - FALSE is faster but without check DNSBL response
136 | * @return $this
137 | * @throws MxToolboxLogicException
138 | */
139 | public function cleanPrevResults($checkResponse = true)
140 | {
141 | if ($this->isArrayInitialized($this->testStructure)) {
142 | foreach ($this->testStructure as $index => $blackList) {
143 | // here is default true because blacklist is loaded from alive file
144 | $this->testStructure[$index]['blResponse'] = true;
145 | if ($checkResponse)
146 | $this->testStructure[$index]['blResponse'] = $this->netTool->isDnsblResponse($this->testStructure[$index]['blHostName']);
147 | $this->testStructure[$index]['blPositive'] = false;
148 | $this->testStructure[$index]['blPositiveResult'] = array();
149 | $this->testStructure[$index]['blQueryTime'] = false;
150 | }
151 | return $this;
152 | }
153 | throw new MxToolboxLogicException(sprintf('Array is empty in %s\%s(), set first test results array.', get_class(), __FUNCTION__));
154 | }
155 |
156 | /**
157 | * Simple check if var is a array and non empty
158 | * @param array $anyArray
159 | * @return bool
160 | */
161 | protected function isArrayInitialized($anyArray)
162 | {
163 | if (is_array($anyArray) && count($anyArray) > 0)
164 | return true;
165 | return false;
166 | }
167 | }
--------------------------------------------------------------------------------
/tests/MxToolbox/MxToolboxTest.php:
--------------------------------------------------------------------------------
1 | basicConfigureMxToolbox($mxt);
23 | unset($mxt);
24 | }
25 |
26 | /**
27 | * @expectedException \MxToolbox\Exceptions\MxToolboxLogicException
28 | */
29 | public function testSetDnsResolver()
30 | {
31 | $mxt = new MxToolbox();
32 | $this->basicConfigureMxToolbox($mxt, '/usr/bin/dig', '');
33 | unset($mxt);
34 | }
35 |
36 | /**
37 | * test domain information like PTR record, Domain name, MX record
38 | */
39 | public function testDomainInformation()
40 | {
41 | $mxt = new MxToolbox();
42 | $this->basicConfigureMxToolbox($mxt, '/usr/bin/dig', '8.8.8.8');
43 | $this->assertFalse($mxt->getDomainInformation('8.8.8.83'));
44 | $dnsRecords = $mxt->getDomainInformation('8.8.8.8');
45 | $this->assertInternalType('array', $dnsRecords);
46 | $this->assertInternalType('array', $dnsRecords['mxRecords']);
47 | $this->assertEquals(4, count($dnsRecords));
48 | }
49 |
50 | /**
51 | * mail server diagnostics
52 | */
53 | public function testSmtpDiagnostics()
54 | {
55 | $mxt = new MxToolbox();
56 | $this->basicConfigureMxToolbox($mxt, '/usr/bin/dig', '8.8.8.8');
57 | $info = $mxt->getSmtpDiagnosticsInfo(
58 | '194.8.253.5',
59 | 'best-hosting.cz',
60 | 'mxtool@best-hosting.cz',
61 | 'test@example.com'
62 | );
63 | $this->assertInternalType('array', $info);
64 | }
65 |
66 | /**
67 | * test setBlacklist()
68 | */
69 | public function testSetBlacklistsDefaultAndOwn()
70 | {
71 | $mxt = new MxToolbox();
72 | $this->basicConfigureMxToolbox($mxt, '/usr/bin/dig', '8.8.8.8');
73 | $this->assertInstanceOf('MxToolbox\MxToolbox', $mxt->setBlacklists());
74 | $this->assertInternalType('array', $mxt->getBlacklistsArray());
75 | $myBlacklist = array(
76 | 0 => 'zen.spamhaus.org',
77 | 1 => 'xbl.spamhaus.org'
78 | );
79 | $this->assertInstanceOf('MxToolbox\MxToolbox', $mxt->setBlacklists($myBlacklist));
80 | $this->assertInternalType('array', $mxt->getBlacklistsArray());
81 | $this->assertCount(2, $mxt->getBlacklistsArray());
82 | }
83 |
84 | /**
85 | * @expectedException \MxToolbox\Exceptions\MxToolboxLogicException
86 | */
87 | public function testGetBlacklistsArrayException()
88 | {
89 | $mxt = new MxToolbox();
90 | $this->basicConfigureMxToolbox($mxt, '/usr/bin/dig', '8.8.8.8');
91 | $mxt->getBlacklistsArray();
92 | }
93 |
94 | /**
95 | * test get blacklist array
96 | */
97 | public function testGetBlacklistsArray()
98 | {
99 | $mxt = new MxToolbox();
100 | $this->basicConfigureMxToolbox($mxt, '/usr/bin/dig', '8.8.8.8');
101 | $mxt->setBlacklists();
102 | $this->assertInternalType('array', $mxt->getBlacklistsArray());
103 | }
104 |
105 | /**
106 | * test dig path, file exist, string contain dig
107 | */
108 | public function testGetDigPath()
109 | {
110 | $mxt = new MxToolbox();
111 | $this->basicConfigureMxToolbox($mxt, '/usr/bin/dig', '8.8.8.8');
112 | $this->assertInternalType('string', $mxt->getDigPath());
113 | $this->assertFileExists($mxt->getDigPath());
114 | $this->assertContains('dig', $mxt->getDigPath());
115 | }
116 |
117 | /**
118 | * test get resolvers array
119 | */
120 | public function testGetDnsResolvers()
121 | {
122 | $mxt = new MxToolbox();
123 | $this->basicConfigureMxToolbox($mxt, '/usr/bin/dig', '8.8.8.8');
124 | $this->assertInternalType('array', $mxt->getDnsResolvers());
125 |
126 | }
127 |
128 | /**
129 | * test refresh alive blacklist file
130 | */
131 | public function testUpdateAliveBlackFile()
132 | {
133 | $mxt = new MxToolbox();
134 | $this->basicConfigureMxToolbox($mxt, '/usr/bin/dig', '8.8.8.8');
135 | $this->assertInstanceOf('MxToolbox\MxToolbox', $mxt->updateAliveBlacklistFile());
136 | $this->assertInternalType('array', $mxt->getBlacklistsArray());
137 | }
138 |
139 | /**
140 | * test check IP address in DNSBL and clean results for next test
141 | */
142 | public function testCheckIpDnsblCleanArray()
143 | {
144 | $mxt = new MxToolbox();
145 | $this->basicConfigureMxToolbox($mxt, '/usr/bin/dig', '8.8.8.8');
146 | $this->assertInstanceOf('MxToolbox\MxToolbox',
147 | $mxt
148 | ->setBlacklists()
149 | ->checkIpAddressOnDnsbl('127.0.0.2')
150 | );
151 | $this->assertInternalType('array', $mxt->getBlacklistsArray());
152 | $this->assertInstanceOf('MxToolbox\MxToolbox', $mxt->cleanBlacklistArray());
153 | $this->assertInternalType('array', $mxt->getBlacklistsArray());
154 | }
155 |
156 | /**
157 | * test check IP address in DNSBL with python multiprocess script and clean results for next test
158 | */
159 | public function testCheckIpDnsblMultiprocess()
160 | {
161 | $mxt = new MxToolbox();
162 | $this->basicConfigureMxToolbox($mxt, '/usr/bin/dig', '8.8.8.8');
163 | $this->assertInstanceOf('MxToolbox\MxToolbox',
164 | $mxt
165 | ->setBlacklists()
166 | ->checkIpAddressOnDnsbl('127.0.0.2',true)
167 | );
168 | $this->assertInternalType('array', $mxt->getBlacklistsArray());
169 | $this->assertInstanceOf('MxToolbox\MxToolbox', $mxt->cleanBlacklistArray());
170 | $this->assertInternalType('array', $mxt->getBlacklistsArray());
171 | }
172 |
173 | /**
174 | * @expectedException \MxToolbox\Exceptions\MxToolboxLogicException
175 | */
176 | public function testCheckIpDnsblCleanArrayException()
177 | {
178 | $mxt = new MxToolbox();
179 | $this->basicConfigureMxToolbox($mxt, '/usr/bin/dig', '8.8.8.8');
180 | $this->assertInstanceOf('MxToolbox\MxToolbox', $mxt->checkIpAddressOnDnsbl('8.8.8.8'));
181 | }
182 |
183 | /**
184 | * test user defined path to the blacklist files
185 | */
186 | public function testUserDefinedBlacklistFilePath()
187 | {
188 | $mxt = new MxToolbox();
189 | $this->basicConfigureMxToolbox($mxt, '/usr/bin/dig', '8.8.8.8');
190 | $mxt->setBlacklistFilePath(dirname(__FILE__) . '/../../vendor/mxtoolbox-blacklists/mxtoolbox-blacklists/');
191 | $this->assertInstanceOf('MxToolbox\MxToolbox', $mxt->setBlacklists());
192 | $this->assertInstanceOf('MxToolbox\MxToolbox', $mxt->checkIpAddressOnDnsbl('8.8.8.8'));
193 | $this->assertInternalType('array', $mxt->getBlacklistsArray());
194 | }
195 |
196 | /**
197 | * Test user defined bad path to the blacklist files
198 | * @expectedException \MxToolbox\Exceptions\MxToolboxRuntimeException
199 | */
200 | public function testUserDefinedBlacklistFilePathException()
201 | {
202 | $mxt = new MxToolbox();
203 | $this->basicConfigureMxToolbox($mxt, '/usr/bin/dig', '8.8.8.8');
204 | $mxt->setBlacklistFilePath('./foo/');
205 | $mxt->setBlacklists();
206 | }
207 |
208 | /**
209 | * Basic configuration
210 | * @param MxToolbox $mxt
211 | * @param string|bool $dns
212 | * @param string|bool $dig
213 | */
214 | private function basicConfigureMxToolbox(MxToolbox $mxt, $dig = false, $dns = false)
215 | {
216 | $mxt->setDig($dig)->setDnsResolver($dns);
217 | }
218 |
219 | }
220 |
--------------------------------------------------------------------------------
/src/MxToolbox/MxToolbox.php:
--------------------------------------------------------------------------------
1 | netTool = $this->createServiceNetworkTool();
41 | $this->fileSys = $this->createServiceBlacklistsHostnameFile();
42 | $this->dataGrid = $this->createServiceMxToolboxDataGrid();
43 | }
44 |
45 | /**
46 | * Set dig path - required, etc: /usr/bin/dig
47 | * @param string $digPath
48 | * @return $this
49 | * @throws MxToolboxLogicException
50 | */
51 | public function setDig($digPath)
52 | {
53 | $this->netTool->setDigPath($digPath);
54 | return $this;
55 | }
56 |
57 | /**
58 | * Set path to the blacklist files - optional
59 | * @param string $path
60 | * @return $this
61 | */
62 | public function setBlacklistFilePath($path)
63 | {
64 | $this->fileSys->setBlacklistFilePath($path);
65 | return $this;
66 | }
67 |
68 | /**
69 | * Set DNS resolver IP address - required (support for multiples push)
70 | * @param string $addr IP address
71 | * @return $this
72 | * @throws MxToolboxLogicException
73 | */
74 | public function setDnsResolver($addr)
75 | {
76 | $this->netTool->setDnsResolverIP($addr);
77 | return $this;
78 | }
79 |
80 | /**
81 | * Initialize blacklist array from file or custom array - optional
82 | * @param array $ownBlacklist optional, default nothing
83 | * @return $this
84 | * @throws MxToolboxRuntimeException
85 | * @throws MxToolboxLogicException
86 | */
87 | public function setBlacklists($ownBlacklist = null)
88 | {
89 | try {
90 | $this->dataGrid->buildBlacklistHostNamesArray($ownBlacklist);
91 | return $this;
92 | } catch (MxToolboxRuntimeException $e) {
93 | if ($e->getCode() == 400) {
94 | $this->dataGrid->buildNewBlacklistHostNames();
95 | return $this;
96 | }
97 | return $e;
98 | }
99 | }
100 |
101 | /**
102 | * Get DNS resolvers
103 | * @return array
104 | */
105 | public function getDnsResolvers()
106 | {
107 | return $this->netTool->getDnsResolvers();
108 | }
109 |
110 | /**
111 | * Get DIG path
112 | * @return string
113 | */
114 | public function getDigPath()
115 | {
116 | return $this->netTool->getDigPath();
117 | }
118 |
119 | /**
120 | * Get blacklists array.
121 | *
122 | * array( array(
123 | * ['blHostName'] => string '',
124 | * ['blPositive'] => boolean ,
125 | * ['blPositiveResult'] => array() ,
126 | * ['blResponse'] => boolean ,
127 | * ['blQueryTime'] => boolean
128 | * ));
129 | * @return array
130 | * @throws MxToolboxLogicException
131 | */
132 | public function getBlacklistsArray()
133 | {
134 | return $this->dataGrid->getTestResultArray();
135 | }
136 |
137 | /**
138 | * Get some additional information about IP address (IP Address, PTR record, Domain name, MX records )
139 | *
140 | * Return information in array(
141 | * ['ipAddress'] => string,
142 | * ['domainName'] => string,
143 | * ['ptrRecord'] => string,
144 | * ['mxRecords'] => array(
145 | * ));
146 | * @param string $addr IP address or domain name
147 | * @return array|bool - return array or FALSE if no information here.
148 | */
149 | public function getDomainInformation($addr)
150 | {
151 | $info = $this->netTool->getDomainDetailInfo($addr);
152 | if (count($info) > 0)
153 | return $info;
154 | return false;
155 | }
156 |
157 | /**
158 | * Get SMTP diagnostics information.
159 | *
160 | * Return information in array(
161 | * ['rDnsMismatch']['state'] => boolean,
162 | * ['rDnsMismatch']['info'] => string,
163 | * ['validHostname']['state'] => boolean,
164 | * ['validHostname']['info'] => string,
165 | * ['bannerCheck']['state'] => boolean,
166 | * ['bannerCheck']['info'] => string,
167 | * ['tls']['state'] => boolean,
168 | * ['tls']['info'] => string,
169 | * ['openRelay']['state'] => boolean,
170 | * ['openRelay']['info'] => string,
171 | * ['errors']['state'] => boolean,
172 | * ['errors']['info'] => string,
173 | * ['allResponses'] => array(
174 | * [connection] => string,
175 | * [ehlo] => array(),
176 | * [mailFrom] => string,
177 | * [rcptTo] => string,
178 | * ),
179 | * );
180 | *
181 | * @param string $addr IP address or domain name
182 | * @param string $myHostName real HostName of the server where script is running (must be resolved to IP address)
183 | * @param string $mailFrom Any testing mail address (domain is same as hostname)
184 | * @param string $mailRcptTo non exist email address as test@example.com
185 | * @return array
186 | * @throws MxToolboxRuntimeException
187 | * @throws MxToolboxLogicException
188 | */
189 | public function getSmtpDiagnosticsInfo($addr, $myHostName, $mailFrom, $mailRcptTo)
190 | {
191 | return $this
192 | ->createServiceSmtpServerChecks($addr, $myHostName, $mailFrom, $mailRcptTo)
193 | ->getSmtpServerDiagnostic();
194 | }
195 |
196 | /**
197 | * Refresh alive blacklists host names in static file (/blacklistsAlive.txt)
198 | * @return $this
199 | * @throws MxToolboxRuntimeException
200 | * @throws MxToolboxLogicException
201 | */
202 | public function updateAliveBlacklistFile()
203 | {
204 | $this->fileSys->deleteAliveBlacklist();
205 | $this->setBlacklists();
206 | return $this;
207 | }
208 |
209 | /**
210 | * Clean blacklist array from previous search
211 | *
212 | * @param bool $checkResponse - default TRUE, FALSE is faster but without check DNSBL response
213 | * @return $this
214 | * @throws MxToolboxLogicException
215 | */
216 | public function cleanBlacklistArray($checkResponse = true)
217 | {
218 | $this->dataGrid->cleanPrevResults($checkResponse);
219 | return $this;
220 | }
221 |
222 | /**
223 | * Check IP address or domain name on all DNSBL servers
224 | * @param string $addr ip address or domain name
225 | * @param boolean $quick (default false) for run quick multiprocess (Python >=2.7 required!)
226 | * @return $this
227 | * @throws MxToolboxRuntimeException
228 | * @throws MxToolboxLogicException
229 | */
230 | public function checkIpAddressOnDnsbl($addr, $quick = false)
231 | {
232 | if ($quick) {
233 | $quickDig = $this->createServiceQuickDig();
234 | $quickDig
235 | ->getJsonFromDigMultiprocess($addr, $this->dataGrid->getTestResultArray())
236 | ->parseDataFromMultiprocessing();
237 | return $this;
238 | }
239 | $this->netTool->checkAllDnsbl($addr, $this->dataGrid->getTestResultArray());
240 | return $this;
241 | }
242 |
243 | }
244 |
--------------------------------------------------------------------------------
/src/MxToolbox/NetworkTools/NetworkTools.php:
--------------------------------------------------------------------------------
1 | digParser = new DigQueryParser();
48 | }
49 |
50 | /**
51 | * Push one IP address of a DNS resolver to the resolvers list
52 | * (tcp port 53 must be open)
53 | * (UDP sockets will sometimes appear to have opened without an error, even if the remote host is unreachable.)
54 | * (DNS Works On Both TCP and UDP ports)
55 | * @param string $addr
56 | * @return $this
57 | * @throws MxToolboxLogicException
58 | */
59 | public function setDnsResolverIP($addr)
60 | {
61 | if ($this->validateIPAddress($addr) && $fss = @fsockopen('tcp://' . $addr, 53, $errNo, $errStr, 5)) {
62 | fclose($fss);
63 | $this->dnsResolvers[] = $addr;
64 | return $this;
65 | }
66 | throw new MxToolboxLogicException('DNS Resolver: tcp://' . $addr . ':53 can\'t open listening socket.');
67 | }
68 |
69 | /**
70 | * Set path to dig utility, etc: '/usr/bin/dig'
71 | * @param string $digPath
72 | * @return $this
73 | * @throws MxToolboxLogicException
74 | */
75 | public function setDigPath($digPath)
76 | {
77 | if (!empty($digPath) && is_file($digPath)) {
78 | $this->digPath = $digPath;
79 | return $this;
80 | }
81 | throw new MxToolboxLogicException('DIG file does not exist!');
82 | }
83 |
84 | /**
85 | * Set 'blResponse' in testResult array on true if is dnsbl hostname alive
86 | * @param array $testResults
87 | * @return $this
88 | */
89 | public function setDnsblResponse(&$testResults)
90 | {
91 | foreach ($testResults as $key => $val) {
92 | if ($this->isDnsblResponse($val['blHostName']))
93 | $testResults[$key]['blResponse'] = true;
94 | }
95 | return $this;
96 | }
97 |
98 | /**
99 | * Get Dns resolvers array
100 | * @return array
101 | */
102 | public function getDnsResolvers()
103 | {
104 | return $this->dnsResolvers;
105 | }
106 |
107 | /**
108 | * Get DIG path
109 | * @return string
110 | */
111 | public function getDigPath()
112 | {
113 | return $this->digPath;
114 | }
115 |
116 | /**
117 | * Check DNSBL PTR Record
118 | * TODO: ipv6 support
119 | * @param string $addr
120 | * @param string $dnsResolver
121 | * @param string $blackList
122 | * @param string $record 'A,TXT,PTR,AAAA?', default 'A'
123 | * @return string
124 | */
125 | public function getDigResult($addr, $dnsResolver, $blackList, $record = 'A')
126 | {
127 | $checkResult = shell_exec($this->digPath . ' @' . $dnsResolver .
128 | ' +time=2 +tries=2 +nocmd ' . $this->reverseIP($addr) . '.' . $blackList . ' ' . $record);
129 | return $checkResult;
130 | }
131 |
132 | /**
133 | * Get some additional information about ip address as PTR,Domain name, MX records.
134 | *
135 | * Structure:
136 | * array(
137 | * ['ipAddress'] => string
138 | * ['domainName'] => string,
139 | * ['ptrRecord'] => string,
140 | * ['mxRecords'] => array(
141 | * ));
142 | * @param string $addr IP address
143 | * @return array
144 | */
145 | public function getDomainDetailInfo($addr)
146 | {
147 | $this->setDigPath($this->digPath);
148 | $info = array();
149 | if ($this->checkExistPTR($addr)) {
150 | $info['ipAddress'] = $this->ipAddress;
151 | $info['domainName'] = $this->domainName;
152 | $info['ptrRecord'] = $this->ptrRecord;
153 | $info['mxRecords'] = array();
154 | if ($this->getMxRecords($this->domainName))
155 | $info['mxRecords'] = $this->mxRecords;
156 | }
157 | return $info;
158 | }
159 |
160 | /**
161 | * Get IP address from domain name
162 | * @param string $addr Domain name or IP address
163 | * @return string ip address
164 | */
165 | public function getIpAddressFromDomainName($addr) {
166 |
167 | if ($this->isDomainName($addr) &&
168 | $this->ipValidator($ipAddress = gethostbyname($addr)))
169 | return $ipAddress;
170 | return $addr;
171 | }
172 |
173 | /**
174 | * Check one hostname for response on 127.0.0.2
175 | * @param string $host
176 | * @return bool
177 | */
178 | public function isDnsblResponse($host)
179 | {
180 | $digOutput = $this->getDigResult('127.0.0.2', $this->getRandomDNSResolverIP(), $host, 'A');
181 | if ($this->digParser->isNoError($digOutput))
182 | return true;
183 | return false;
184 | }
185 |
186 | /**
187 | * Check all (use only alive rBLS - fast check!)
188 | * @param string $addr IP address or domain name
189 | * @param array $testResult
190 | * @return $this
191 | * @throws MxToolboxLogicException
192 | * @throws MxToolboxRuntimeException
193 | */
194 | public function checkAllDnsbl($addr, &$testResult)
195 | {
196 | $this->ipValidator($addr);
197 | $this->ipAddress = $this->getIpAddressFromDomainName($addr);
198 | if (count($testResult) > 0) {
199 | foreach ($testResult as &$blackList) {
200 | $digOutput = $this->getDigResult(
201 | $this->ipAddress,
202 | $this->getRandomDNSResolverIP(),
203 | $blackList['blHostName'], 'TXT'
204 | );
205 |
206 | if ($this->digParser->isNoError($digOutput)) {
207 | $blackList['blPositive'] = true;
208 | $blackList['blPositiveResult'] = $this->digParser->getPositiveUrlAddresses($digOutput);
209 | }
210 |
211 | $blackList['blQueryTime'] = $this->digParser->getQueryTime($digOutput);
212 | }
213 | return $this;
214 | }
215 | throw new MxToolboxRuntimeException(sprintf('Array is empty for dig checks in: %s\%s.', get_class(), __FUNCTION__));
216 | }
217 |
218 | /**
219 | * Reverse IP address 192.168.1.254 -> 254.1.168.192
220 | * @param string $addr
221 | * @return string
222 | */
223 | public function reverseIP($addr)
224 | {
225 | $revIpAddr = explode(".", $addr);
226 | return $revIpAddr[3] . '.' . $revIpAddr[2] . '.' . $revIpAddr[1] . '.' . $revIpAddr[0];
227 | }
228 |
229 | /**
230 | * Check if string is valid IPv4 address or domain
231 | * @param string $addr
232 | * @return boolean
233 | */
234 | public function ipValidator($addr)
235 | {
236 | if ($this->isDomainName($addr)) {
237 | if ($this->validateIPAddress(gethostbyname($addr)))
238 | return true;
239 | return false;
240 | }
241 |
242 | if ($this->validateIPAddress($addr))
243 | return true;
244 | return false;
245 | }
246 |
247 | /**
248 | * Check if a string represents domain name
249 | * @param string $addr
250 | * @return boolean
251 | */
252 | public function isDomainName($addr)
253 | {
254 | if (preg_match("/^[a-zA-Z0-9.\-]{2,256}\.[a-z]{2,6}$/", $addr))
255 | return true;
256 | return false;
257 | }
258 |
259 | /**
260 | * Validate if string is valid IP address
261 | * @param string $addr
262 | * @return boolean
263 | */
264 | private function validateIPAddress($addr)
265 | {
266 | if (filter_var($addr, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4))
267 | return true;
268 | throw new MxToolboxLogicException($addr . " isn't correct IP address or domain name.");
269 | }
270 |
271 | /**
272 | * Get random DNS IP address from array
273 | * @return string ''
274 | * @throws MxToolboxLogicException
275 | */
276 | public function getRandomDNSResolverIP()
277 | {
278 | if (!count($this->dnsResolvers) > 0)
279 | throw new MxToolboxLogicException('No DNS resolver here!');
280 | return $this->dnsResolvers[array_rand($this->dnsResolvers, 1)];
281 | }
282 |
283 | /**
284 | * Get MX records from domain name
285 | * @param string $hostName
286 | * @return boolean
287 | */
288 | private function getMxRecords($hostName)
289 | {
290 | if (preg_match("/^[a-zA-Z0-9.\-]{2,256}\.[a-z]{2,6}$/", trim($hostName))) {
291 | $ptr = dns_get_record($hostName, DNS_MX);
292 | if (isset($ptr[0]['target'])) {
293 | $mxRecords = array();
294 | foreach ($ptr as $mx)
295 | $mxRecords[] = $mx['target'];
296 | $this->mxRecords = $mxRecords;
297 | return true;
298 | }
299 | }
300 | return false;
301 | }
302 |
303 | /**
304 | * Check if IP address have a PTR record
305 | * @param string $addr IP address
306 | * @return boolean
307 | */
308 | private function checkExistPTR($addr)
309 | {
310 | if (!$this->ipValidator($addr))
311 | return false;
312 | $this->ipAddress = $this->getIpAddressFromDomainName($addr);
313 | $digResponse = $this->getDigResult($this->ipAddress, $this->getRandomDNSResolverIP(), 'in-addr.arpa', 'PTR');
314 | if ($this->digParser->isNoError($digResponse)) {
315 | $ptr = dns_get_record($this->reverseIP($this->ipAddress) . '.in-addr.arpa.', DNS_PTR);
316 | if (isset($ptr[0]['target'])) {
317 | $regs = array();
318 | $this->ptrRecord = $ptr[0]['target'];
319 | if (preg_match('/(?P[a-z0-9][a-z0-9\-]{1,63}\.[a-z\.]{2,6})$/i', $ptr[0]['target'], $regs))
320 | $this->domainName = $regs['domain'];
321 | return true;
322 | }
323 | }
324 | return false;
325 | }
326 |
327 | }
328 |
--------------------------------------------------------------------------------
/src/MxToolbox/NetworkTools/SmtpServerChecks.php:
--------------------------------------------------------------------------------
1 | netTool = $netTool;
66 | $this->netTool->ipValidator($addr);
67 |
68 | if (!$this->isEmail($mailFrom) || !$this->isEmail($mailRcptTo))
69 | throw new MxToolboxLogicException('Non valid email format.');
70 |
71 | if (empty($myHostName) || !$this->netTool->ipValidator($myHostName))
72 | throw new MxToolboxLogicException('Missing or bad argument myHostName.');
73 |
74 | $this->addr = $this->netTool->getIpAddressFromDomainName($addr);
75 | $this->myHostName = $myHostName;
76 | $this->emailFrom = $mailFrom;
77 | $this->emailRcptTo = $mailRcptTo;
78 | $this->setFinalResultsArray();
79 | }
80 |
81 | /**
82 | * Get SMTP diagnostics information.
83 | *
84 | * Return information in array(
85 | * ['rDnsMismatch']['state'] => boolean,
86 | * ['rDnsMismatch']['info'] => string,
87 | * ['validHostname']['state'] => boolean,
88 | * ['validHostname']['info'] => string,
89 | * ['bannerCheck']['state'] => boolean,
90 | * ['bannerCheck']['info'] => string,
91 | * ['tls']['state'] => boolean,
92 | * ['tls']['info'] => string,
93 | * ['openRelay']['state'] => boolean,
94 | * ['openRelay']['info'] => string,
95 | * ['errors']['state'] => boolean,
96 | * ['errors']['info'] => string,
97 | * ['allResponses'] => array()
98 | * ['allResponses'] => array(
99 | * [connection] => string,
100 | * [ehlo] => array(),
101 | * [mailFrom] => string,
102 | * [rcptTo] => string,
103 | * ),
104 | * );
105 | *
106 | * @return array
107 | */
108 | public function getSmtpServerDiagnostic()
109 | {
110 | if (!$this->setSmtpConnect($this->addr)) {
111 | $this->closeSmtpConnection();
112 | return $this->finalResults;
113 | }
114 | $this
115 | ->setEhloResponse()
116 | ->setFromResponse()
117 | ->setRcptToResponse()
118 | ->closeSmtpConnection()
119 | ->parseResults();
120 | return $this->finalResults;
121 | }
122 |
123 | /**
124 | * Connect to the SMTP server
125 | * @param string $addr IP address for test
126 | * @return boolean
127 | */
128 | private function setSmtpConnect($addr)
129 | {
130 | $this->smtpConnection = @stream_socket_client($addr . ':' . $this->smtpPort, $errno, $errstr,
131 | $this->connTimeout, STREAM_CLIENT_CONNECT);
132 | if (is_resource($this->smtpConnection)) {
133 | stream_set_timeout($this->smtpConnection, $this->connTimeout);
134 | $this->smtpResponses['connection'] = $this->readCommand();
135 | $info = stream_get_meta_data($this->smtpConnection);
136 | if ($info['timed_out']) {
137 | $this->finalResults['errors']['info'] = 'SMTP command (waiting timeout).';
138 | return false;
139 | }
140 | $this->finalResults['errors']['state'] = false;
141 | return true;
142 | }
143 | $this->finalResults['errors']['info'] = 'Unable to connect to ' . $addr . ':25 (Timeout | No route to host).';
144 | return false;
145 | }
146 |
147 | /**
148 | * Starting the conversation. Server wants to use the extended SMTP (ESMTP) protocol.
149 | * @return $this
150 | * @throws MxToolboxRuntimeException
151 | */
152 | private function setEhloResponse()
153 | {
154 | if (is_resource($this->smtpConnection)) {
155 | $this->writeCommand('EHLO', $this->myHostName);
156 | $this->smtpResponses['ehlo'] = array_filter(explode(self::CRLF, $this->readCommand()));
157 | return $this;
158 | }
159 | throw new MxToolboxRuntimeException('Invalid connection');
160 | }
161 |
162 | /**
163 | * Set MAIL FROM response
164 | * @return $this
165 | */
166 | private function setFromResponse()
167 | {
168 | if (is_resource($this->smtpConnection)) {
169 | $this->writeCommand('MAIL FROM:', $this->emailFrom);
170 | $this->smtpResponses['mailFrom'] = $this->readCommand();
171 | return $this;
172 | }
173 | throw new MxToolboxRuntimeException('Invalid connection');
174 | }
175 |
176 | /**
177 | * Set RCPT TO response
178 | * @return $this
179 | */
180 | private function setRcptToResponse()
181 | {
182 | if (is_resource($this->smtpConnection)) {
183 | $this->writeCommand('RCPT TO:', $this->emailFrom);
184 | $this->smtpResponses['rcptTo'] = $this->readCommand();
185 | return $this;
186 | }
187 | throw new MxToolboxRuntimeException('Invalid connection');
188 | }
189 |
190 | /**
191 | * Write command to the SMTP stream
192 | * @param string $command
193 | * @param string $param
194 | */
195 | private function writeCommand($command, $param = '')
196 | {
197 | fwrite($this->smtpConnection, $command . " " . $param . self::CRLF);
198 | }
199 |
200 | /**
201 | * Read command from the SMTP stream
202 | * @return mixed
203 | */
204 | private function readCommand()
205 | {
206 | return fread($this->smtpConnection, 4096);
207 | }
208 |
209 | /**
210 | * Close the SMTP connection
211 | * @return $this
212 | */
213 | private function closeSmtpConnection()
214 | {
215 | if (is_resource($this->smtpConnection)) {
216 | fwrite($this->smtpConnection, 'QUIT' . self::CRLF);
217 | fclose($this->smtpConnection);
218 | }
219 | return $this;
220 | }
221 |
222 | /**
223 | * Initialize final results array
224 | */
225 | private function setFinalResultsArray()
226 | {
227 | $this->finalResults['rDnsMismatch']['state'] = false;
228 | $this->finalResults['rDnsMismatch']['info'] = '';
229 | $this->finalResults['validHostname']['state'] = false;
230 | $this->finalResults['validHostname']['info'] = '';
231 | $this->finalResults['bannerCheck']['state'] = false;
232 | $this->finalResults['bannerCheck']['info'] = '';
233 | $this->finalResults['tls']['state'] = false;
234 | $this->finalResults['tls']['info'] = '';
235 | // $this->finalResults['connectTime']['state'] = false;
236 | // $this->finalResults['connectTime']['info'] = '';
237 | $this->finalResults['openRelay']['state'] = true;
238 | $this->finalResults['openRelay']['info'] = '';
239 | $this->finalResults['errors']['state'] = true;
240 | $this->finalResults['errors']['info'] = '';
241 | $this->finalResults['allResponses'] = false;
242 | return $this;
243 | }
244 |
245 | /**
246 | * Is string email format
247 | * @param string $email
248 | * @return bool
249 | */
250 | private function isEmail($email)
251 | {
252 | if (!filter_var($email, FILTER_VALIDATE_EMAIL))
253 | return false;
254 | return true;
255 | }
256 |
257 |
258 | /**
259 | * Parse all responses from the SMTP stream
260 | * @return $this
261 | */
262 | private function parseResults()
263 | {
264 | $parser = new SmtpDiagnosticParser();
265 | $info = $this->netTool->getDomainDetailInfo($this->addr);
266 |
267 | // Check TLS available
268 | // Reverse DNS Mismatch: If the A record of the hostname did not match the PTR = TRUE
269 | // Reverse DNS Hostname validation
270 | // Reverse DNS checks in SMTP Banner
271 | // Check open relay
272 | $this
273 | ->checkTls($parser)
274 | ->checkRDnsMismatch($info, $parser)
275 | ->checkValidHostname($info, $parser)
276 | ->checkSmtpBanner($info, $parser)
277 | ->checkOpenRelay($parser);
278 |
279 | // set all responses to final result
280 | if (count($this->smtpResponses) > 0)
281 | $this->finalResults['allResponses'] = $this->smtpResponses;
282 | return $this;
283 | }
284 |
285 | /**
286 | * Check if is TLS supported
287 | * @param SmtpDiagnosticParser $parser
288 | * @return $this
289 | */
290 | private function checkTls($parser)
291 | {
292 | $this->finalResults['tls']['state'] = $parser->isTls($this->smtpResponses['ehlo']);
293 | $this->finalResults['tls']['info'] = 'OK - TLS is supported.';
294 | if (!$this->finalResults['tls']['state'])
295 | $this->finalResults['tls']['info'] = 'ERR - TLS is not supported!';
296 | return $this;
297 | }
298 |
299 | /**
300 | * Check valid hostname
301 | * @param array $info
302 | * @param SmtpDiagnosticParser $parser
303 | * @return $this
304 | */
305 | private function checkValidHostname($info, SmtpDiagnosticParser $parser)
306 | {
307 | if (isset($info['ptrRecord']) && $this->netTool->isDomainName($info['ptrRecord']))
308 | $this->finalResults['validHostname']['state'] = $parser->isValidHostname(
309 | $this->smtpResponses['ehlo'], $info['ptrRecord']
310 | );
311 | $this->finalResults['validHostname']['info'] = 'OK - rDNS is a valid hostname.';
312 | if (!$this->finalResults['validHostname']['state'])
313 | $this->finalResults['validHostname']['info'] = 'ERR - rDNS is not a valid hostname!';
314 | return $this;
315 |
316 | }
317 |
318 | /**
319 | * Check rDNSMismatch
320 | * @param array $info
321 | * @param SmtpDiagnosticParser $parser
322 | * @return $this
323 | */
324 | private function checkRDnsMismatch($info, SmtpDiagnosticParser $parser)
325 | {
326 | if (isset($info['ptrRecord']) && $this->netTool->isDomainName($info['ptrRecord'])) {
327 | $this->finalResults['rDnsMismatch']['state'] = $parser->isReverseDnsMismatch(
328 | $this->addr, dns_get_record($info['ptrRecord'], DNS_A)
329 | );
330 | $this->finalResults['rDnsMismatch']['info'] = 'OK - ' . $this->addr . ' resolves to ' . $info['ptrRecord'];
331 | }
332 | if (!$this->finalResults['rDnsMismatch']['state'])
333 | $this->finalResults['rDnsMismatch']['info'] = 'ERR - Cannot resolve address: ' . $this->addr;
334 | return $this;
335 | }
336 |
337 | /**
338 | * Check PTR matches in SMTP banner
339 | * @param array $info
340 | * @param SmtpDiagnosticParser $parser
341 | * @return $this
342 | */
343 | private function checkSmtpBanner($info, SmtpDiagnosticParser $parser)
344 | {
345 | if (isset($info['ptrRecord']) && $this->netTool->isDomainName($info['ptrRecord'])) {
346 | $this->finalResults['bannerCheck']['state'] = $parser->isReverseDnsInBanner(
347 | $this->smtpResponses['connection'], $info['ptrRecord']
348 | );
349 | $this->finalResults['bannerCheck']['info'] = 'OK - Reverse DNS matches SMTP banner.';
350 | }
351 | if (!$this->finalResults['bannerCheck']['state'])
352 | $this->finalResults['bannerCheck']['info'] = 'ERR - Reverse DNS does not match SMTP banner!';
353 | return $this;
354 | }
355 |
356 | /**
357 | * Check open relay access
358 | * @param SmtpDiagnosticParser $parser
359 | * @return $this
360 | */
361 | private function checkOpenRelay(SmtpDiagnosticParser $parser)
362 | {
363 | if (isset($this->smtpResponses['rcptTo']))
364 | $this->finalResults['openRelay']['state'] = $parser->isOpenRelay($this->smtpResponses['rcptTo'][0]);
365 | $this->finalResults['openRelay']['info'] = 'OK - Relay access denied.';
366 | if (!$this->finalResults['openRelay']['state'])
367 | $this->finalResults['openRelay']['info'] = 'ERR - Relay access open!';
368 | return $this;
369 | }
370 |
371 | }
372 |
373 |
--------------------------------------------------------------------------------