GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Completed
Pull Request — master (#119)
by
unknown
01:12
created

Downloader::setIpAddress()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
nc 2
nop 1
dl 0
loc 11
rs 9.9
c 0
b 0
f 0
1
<?php
2
3
namespace Spatie\SslCertificate;
4
5
use Spatie\SslCertificate\Exceptions\CouldNotDownloadCertificate;
6
use Spatie\SslCertificate\Exceptions\InvalidIpAddress;
7
use Throwable;
8
9
class Downloader
10
{
11
    /** @var int */
12
    protected $port = 443;
13
14
    /** @var string */
15
    protected $ipAddress = null;
16
17
    /** @var bool */
18
    protected $usingIpAddress = false;
19
20
    /** @var int */
21
    protected $timeout = 30;
22
23
    /** @var bool */
24
    protected $enableSni = true;
25
26
    /** @var bool */
27
    protected $capturePeerChain = false;
28
29
    /** @var bool */
30
    protected $verifyPeer = true;
31
32
    /** @var bool */
33
    protected $verifyPeerName = true;
34
35
    public function usingPort(int $port)
36
    {
37
        $this->port = $port;
38
39
        return $this;
40
    }
41
42
    public function usingSni(bool $sni)
43
    {
44
        $this->enableSni = $sni;
45
46
        return $this;
47
    }
48
49
    public function withFullChain(bool $fullChain)
50
    {
51
        $this->capturePeerChain = $fullChain;
52
53
        return $this;
54
    }
55
56
    public function withVerifyPeer(bool $verifyPeer)
57
    {
58
        $this->verifyPeer = $verifyPeer;
59
60
        return $this;
61
    }
62
63
    public function withVerifyPeerName(bool $verifyPeerName)
64
    {
65
        $this->verifyPeerName = $verifyPeerName;
66
67
        return $this;
68
    }
69
70
    public function setTimeout(int $timeOutInSeconds)
71
    {
72
        $this->timeout = $timeOutInSeconds;
73
74
        return $this;
75
    }
76
77
    public function setIpAddress(string $ipAddress)
78
    {
79
        if (! filter_var($ipAddress, FILTER_VALIDATE_IP)) {
80
            throw InvalidIpAddress::couldNotValidate($ipAddress);
81
        }
82
83
        $this->ipAddress = $ipAddress;
84
        $this->usingIpAddress = true;
85
86
        return $this;
87
    }
88
89
    public function getCertificates(string $hostName): array
90
    {
91
        $response = $this->fetchCertificates($hostName);
92
        $remoteAddress = $response['remoteAddress'];
93
94
        $peerCertificate = $response['options']['ssl']['peer_certificate'];
95
96
        $peerCertificateChain = $response['options']['ssl']['peer_certificate_chain'] ?? [];
97
98
        $fullCertificateChain = array_merge([$peerCertificate], $peerCertificateChain);
99
100
        $certificates = array_map(function ($certificate) use ($remoteAddress) {
101
            $certificateFields = openssl_x509_parse($certificate);
102
103
            $fingerprint = openssl_x509_fingerprint($certificate);
104
            $fingerprintSha256 = openssl_x509_fingerprint($certificate, 'sha256');
105
106
            return new SslCertificate(
107
                $certificateFields,
108
                $fingerprint,
109
                $fingerprintSha256,
110
                $remoteAddress
111
            );
112
        }, $fullCertificateChain);
113
114
        return array_unique($certificates);
115
    }
116
117
    public function forHost(string $hostName): SslCertificate
118
    {
119
        $hostName = (new Url($hostName))->getHostName();
120
121
        $certificates = $this->getCertificates($hostName);
122
123
        return $certificates[0] ?? false;
124
    }
125
126
    public static function downloadCertificateFromUrl(string $url, int $timeout = 30): SslCertificate
127
    {
128
        return (new static())
129
            ->setTimeout($timeout)
130
            ->forHost($url);
131
    }
132
133
    protected function fetchCertificates(string $hostName): array
134
    {
135
        $hostName = (new Url($hostName))->getHostName();
136
137
        $sslOptions = [
138
            'capture_peer_cert' => true,
139
            'capture_peer_cert_chain' => $this->capturePeerChain,
140
            'SNI_enabled' => $this->enableSni,
141
            'peer_name' => $hostName,
142
            'verify_peer' => $this->verifyPeer,
143
            'verify_peer_name' => $this->verifyPeerName,
144
        ];
145
146
        $streamContext = stream_context_create([
147
            'ssl' => $sslOptions,
148
        ]);
149
150
        if ($this->usingIpAddress) {
151
            $connectTo = $this->ipAddress;
152
        } else {
153
            $connectTo = $hostName;
154
        }
155
156
        try {
157
            $client = stream_socket_client(
158
                "ssl://{$connectTo}:{$this->port}",
159
                $errorNumber,
160
                $errorDescription,
161
                $this->timeout,
162
                STREAM_CLIENT_CONNECT,
163
                $streamContext
164
            );
165
        } catch (Throwable $thrown) {
166
            $this->handleRequestFailure($connectTo, $thrown);
167
        }
168
169
        if (! $client) {
170
            if ($this->usingIpAddress) {
171
                throw CouldNotDownloadCertificate::unknownError(
172
                    $hostName,
173
                    "Could not connect to `{$connectTo}` or it does not have a certificate matching `${hostName}`."
174
                );
175
            } else {
176
                throw CouldNotDownloadCertificate::unknownError($hostName, "Could not connect to `{$connectTo}`.");
177
            }
178
        }
179
180
        $response = stream_context_get_params($client);
181
182
        $response['remoteAddress'] = stream_socket_get_name($client, true);
183
184
        fclose($client);
185
186
        return $response;
187
    }
188
189
    protected function handleRequestFailure(string $hostName, Throwable $thrown)
190
    {
191
        if (str_contains($thrown->getMessage(), 'getaddrinfo failed')) {
192
            throw CouldNotDownloadCertificate::hostDoesNotExist($hostName);
193
        }
194
195
        if (str_contains($thrown->getMessage(), 'error:14090086')) {
196
            throw CouldNotDownloadCertificate::noCertificateInstalled($hostName);
197
        }
198
199
        throw CouldNotDownloadCertificate::unknownError($hostName, $thrown->getMessage());
200
    }
201
}
202