Completed
Push — master ( 7c2697...ec0f51 )
by Sam
01:44
created

StreamSocketProvider::getHost()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
c 0
b 0
f 0
rs 10
cc 1
eloc 2
nc 1
nop 0
1
<?php
2
3
namespace Jalle19\CertificateParser\Provider;
4
5
use Jalle19\CertificateParser\Exception\DomainMismatchException;
6
use Jalle19\CertificateParser\Exception\NameResolutionException;
7
use Jalle19\CertificateParser\Exception\CertificateNotFoundException;
8
use Jalle19\CertificateParser\Exception\UnknownErrorException;
9
10
/**
11
 * Class StreamSocketProvider
12
 * @package Jalle19\CertificateParser\Provider
13
 */
14
class StreamSocketProvider implements ProviderInterface
15
{
16
17
    const DEFAULT_TIMEOUT_SECONDS = 15;
18
    const DEFAULT_PORT            = 443;
19
20
    /**
21
     * @var int
22
     */
23
    private $timeout;
24
25
    /**
26
     * @var string
27
     */
28
    private $hostname;
29
30
    /**
31
     * @var int
32
     */
33
    private $port;
34
35
36
    /**
37
     * StreamSocketProvider constructor.
38
     *
39
     * @param string $hostname
40
     * @param int    $port    (optional)
41
     * @param int    $timeout (optional)
42
     */
43
    public function __construct($hostname, $port = self::DEFAULT_PORT, $timeout = self::DEFAULT_TIMEOUT_SECONDS)
44
    {
45
        $this->hostname = $hostname;
46
        $this->port     = $port;
47
        $this->timeout  = $timeout;
48
    }
49
50
51
    /**
52
     * @inheritdoc
53
     */
54
    public function getRawCertificate()
55
    {
56
        $streamContext = stream_context_create([
57
            'ssl' => [
58
                'capture_peer_cert' => true,
59
                'verify_peer'       => false,
60
            ],
61
        ]);
62
63
        try {
64
            $client = stream_socket_client(
65
                $this->getRequestUrl(),
66
                $errorNumber,
67
                $errorDescription,
68
                $this->timeout,
69
                STREAM_CLIENT_CONNECT,
70
                $streamContext
71
            );
72
73
            $response = stream_context_get_params($client);
74
75
            return $response['options']['ssl']['peer_certificate'];
76
        } catch (\Throwable $e) {
77
            $errorMessage = $e->getMessage();
78
79
            // Check for name resolution errors
80
            if (strpos($errorMessage, 'getaddrinfo failed') !== false) {
81
                throw new NameResolutionException();
82
            }
83
84
            // Check for unknown SSL protocol (usually means no SSL is configured on the endpoint)
85
            if (strpos($errorMessage, 'GET_SERVER_HELLO:unknown protocol') !== false) {
86
                throw new CertificateNotFoundException();
87
            }
88
89
            // Check for domain mismatches
90
            if (strpos($errorMessage, 'did not match expected') !== false) {
91
                throw new DomainMismatchException();
92
            }
93
94
            throw new UnknownErrorException($errorMessage);
95
        }
96
    }
97
98
99
    /**
100
     * @return string
101
     */
102
    private function getRequestUrl()
103
    {
104
        return 'ssl://' . $this->hostname . ':' . $this->port;
105
    }
106
107
}
108