65 if ( ! isset( $arrURL['path'] ) ) { |
69 if ( ! isset( $arrURL['path'] ) ) { |
66 $arrURL['path'] = '/'; |
70 $arrURL['path'] = '/'; |
67 } |
71 } |
68 |
72 |
69 if ( isset( $r['headers']['Host'] ) || isset( $r['headers']['host'] ) ) { |
73 if ( isset( $r['headers']['Host'] ) || isset( $r['headers']['host'] ) ) { |
70 if ( isset( $r['headers']['Host'] ) ) |
74 if ( isset( $r['headers']['Host'] ) ) { |
71 $arrURL['host'] = $r['headers']['Host']; |
75 $arrURL['host'] = $r['headers']['Host']; |
72 else |
76 } else { |
73 $arrURL['host'] = $r['headers']['host']; |
77 $arrURL['host'] = $r['headers']['host']; |
|
78 } |
74 unset( $r['headers']['Host'], $r['headers']['host'] ); |
79 unset( $r['headers']['Host'], $r['headers']['host'] ); |
75 } |
80 } |
76 |
81 |
77 /* |
82 /* |
78 * Certain versions of PHP have issues with 'localhost' and IPv6, It attempts to connect |
83 * Certain versions of PHP have issues with 'localhost' and IPv6, It attempts to connect |
79 * to ::1, which fails when the server is not set up for it. For compatibility, always |
84 * to ::1, which fails when the server is not set up for it. For compatibility, always |
80 * connect to the IPv4 address. |
85 * connect to the IPv4 address. |
81 */ |
86 */ |
82 if ( 'localhost' == strtolower( $connect_host ) ) |
87 if ( 'localhost' == strtolower( $connect_host ) ) { |
83 $connect_host = '127.0.0.1'; |
88 $connect_host = '127.0.0.1'; |
|
89 } |
84 |
90 |
85 $connect_host = $secure_transport ? 'ssl://' . $connect_host : 'tcp://' . $connect_host; |
91 $connect_host = $secure_transport ? 'ssl://' . $connect_host : 'tcp://' . $connect_host; |
86 |
92 |
87 $is_local = isset( $r['local'] ) && $r['local']; |
93 $is_local = isset( $r['local'] ) && $r['local']; |
88 $ssl_verify = isset( $r['sslverify'] ) && $r['sslverify']; |
94 $ssl_verify = isset( $r['sslverify'] ) && $r['sslverify']; |
89 if ( $is_local ) { |
95 if ( $is_local ) { |
90 /** |
96 /** |
91 * Filters whether SSL should be verified for local requests. |
97 * Filters whether SSL should be verified for local requests. |
92 * |
98 * |
93 * @since 2.8.0 |
99 * @since 2.8.0 |
|
100 * @since 5.1.0 The `$url` parameter was added. |
94 * |
101 * |
95 * @param bool $ssl_verify Whether to verify the SSL connection. Default true. |
102 * @param bool $ssl_verify Whether to verify the SSL connection. Default true. |
|
103 * @param string $url The request URL. |
96 */ |
104 */ |
97 $ssl_verify = apply_filters( 'https_local_ssl_verify', $ssl_verify ); |
105 $ssl_verify = apply_filters( 'https_local_ssl_verify', $ssl_verify, $url ); |
98 } elseif ( ! $is_local ) { |
106 } elseif ( ! $is_local ) { |
99 /** |
107 /** This filter is documented in wp-includes/class-http.php */ |
100 * Filters whether SSL should be verified for non-local requests. |
108 $ssl_verify = apply_filters( 'https_ssl_verify', $ssl_verify, $url ); |
101 * |
|
102 * @since 2.8.0 |
|
103 * |
|
104 * @param bool $ssl_verify Whether to verify the SSL connection. Default true. |
|
105 */ |
|
106 $ssl_verify = apply_filters( 'https_ssl_verify', $ssl_verify ); |
|
107 } |
109 } |
108 |
110 |
109 $proxy = new WP_HTTP_Proxy(); |
111 $proxy = new WP_HTTP_Proxy(); |
110 |
112 |
111 $context = stream_context_create( array( |
113 $context = stream_context_create( |
112 'ssl' => array( |
114 array( |
113 'verify_peer' => $ssl_verify, |
115 'ssl' => array( |
114 //'CN_match' => $arrURL['host'], // This is handled by self::verify_ssl_certificate() |
116 'verify_peer' => $ssl_verify, |
115 'capture_peer_cert' => $ssl_verify, |
117 //'CN_match' => $arrURL['host'], // This is handled by self::verify_ssl_certificate() |
116 'SNI_enabled' => true, |
118 'capture_peer_cert' => $ssl_verify, |
117 'cafile' => $r['sslcertificates'], |
119 'SNI_enabled' => true, |
118 'allow_self_signed' => ! $ssl_verify, |
120 'cafile' => $r['sslcertificates'], |
|
121 'allow_self_signed' => ! $ssl_verify, |
|
122 ), |
119 ) |
123 ) |
120 ) ); |
124 ); |
121 |
125 |
122 $timeout = (int) floor( $r['timeout'] ); |
126 $timeout = (int) floor( $r['timeout'] ); |
123 $utimeout = $timeout == $r['timeout'] ? 0 : 1000000 * $r['timeout'] % 1000000; |
127 $utimeout = $timeout == $r['timeout'] ? 0 : 1000000 * $r['timeout'] % 1000000; |
124 $connect_timeout = max( $timeout, 1 ); |
128 $connect_timeout = max( $timeout, 1 ); |
125 |
129 |
126 // Store error number. |
130 // Store error number. |
127 $connection_error = null; |
131 $connection_error = null; |
128 |
132 |
129 // Store error string. |
133 // Store error string. |
130 $connection_error_str = null; |
134 $connection_error_str = null; |
131 |
135 |
132 if ( !WP_DEBUG ) { |
136 if ( ! WP_DEBUG ) { |
133 // In the event that the SSL connection fails, silence the many PHP Warnings. |
137 // In the event that the SSL connection fails, silence the many PHP Warnings. |
134 if ( $secure_transport ) |
138 if ( $secure_transport ) { |
135 $error_reporting = error_reporting(0); |
139 $error_reporting = error_reporting( 0 ); |
136 |
140 } |
137 if ( $proxy->is_enabled() && $proxy->send_through_proxy( $url ) ) |
141 |
|
142 if ( $proxy->is_enabled() && $proxy->send_through_proxy( $url ) ) { |
138 $handle = @stream_socket_client( 'tcp://' . $proxy->host() . ':' . $proxy->port(), $connection_error, $connection_error_str, $connect_timeout, STREAM_CLIENT_CONNECT, $context ); |
143 $handle = @stream_socket_client( 'tcp://' . $proxy->host() . ':' . $proxy->port(), $connection_error, $connection_error_str, $connect_timeout, STREAM_CLIENT_CONNECT, $context ); |
139 else |
144 } else { |
140 $handle = @stream_socket_client( $connect_host . ':' . $arrURL['port'], $connection_error, $connection_error_str, $connect_timeout, STREAM_CLIENT_CONNECT, $context ); |
145 $handle = @stream_socket_client( $connect_host . ':' . $arrURL['port'], $connection_error, $connection_error_str, $connect_timeout, STREAM_CLIENT_CONNECT, $context ); |
141 |
146 } |
142 if ( $secure_transport ) |
147 |
|
148 if ( $secure_transport ) { |
143 error_reporting( $error_reporting ); |
149 error_reporting( $error_reporting ); |
144 |
150 } |
145 } else { |
151 } else { |
146 if ( $proxy->is_enabled() && $proxy->send_through_proxy( $url ) ) |
152 if ( $proxy->is_enabled() && $proxy->send_through_proxy( $url ) ) { |
147 $handle = stream_socket_client( 'tcp://' . $proxy->host() . ':' . $proxy->port(), $connection_error, $connection_error_str, $connect_timeout, STREAM_CLIENT_CONNECT, $context ); |
153 $handle = stream_socket_client( 'tcp://' . $proxy->host() . ':' . $proxy->port(), $connection_error, $connection_error_str, $connect_timeout, STREAM_CLIENT_CONNECT, $context ); |
148 else |
154 } else { |
149 $handle = stream_socket_client( $connect_host . ':' . $arrURL['port'], $connection_error, $connection_error_str, $connect_timeout, STREAM_CLIENT_CONNECT, $context ); |
155 $handle = stream_socket_client( $connect_host . ':' . $arrURL['port'], $connection_error, $connection_error_str, $connect_timeout, STREAM_CLIENT_CONNECT, $context ); |
|
156 } |
150 } |
157 } |
151 |
158 |
152 if ( false === $handle ) { |
159 if ( false === $handle ) { |
153 // SSL connection failed due to expired/invalid cert, or, OpenSSL configuration is broken. |
160 // SSL connection failed due to expired/invalid cert, or, OpenSSL configuration is broken. |
154 if ( $secure_transport && 0 === $connection_error && '' === $connection_error_str ) |
161 if ( $secure_transport && 0 === $connection_error && '' === $connection_error_str ) { |
155 return new WP_Error( 'http_request_failed', __( 'The SSL certificate for the host could not be verified.' ) ); |
162 return new WP_Error( 'http_request_failed', __( 'The SSL certificate for the host could not be verified.' ) ); |
156 |
163 } |
157 return new WP_Error('http_request_failed', $connection_error . ': ' . $connection_error_str ); |
164 |
|
165 return new WP_Error( 'http_request_failed', $connection_error . ': ' . $connection_error_str ); |
158 } |
166 } |
159 |
167 |
160 // Verify that the SSL certificate is valid for this request. |
168 // Verify that the SSL certificate is valid for this request. |
161 if ( $secure_transport && $ssl_verify && ! $proxy->is_enabled() ) { |
169 if ( $secure_transport && $ssl_verify && ! $proxy->is_enabled() ) { |
162 if ( ! self::verify_ssl_certificate( $handle, $arrURL['host'] ) ) |
170 if ( ! self::verify_ssl_certificate( $handle, $arrURL['host'] ) ) { |
163 return new WP_Error( 'http_request_failed', __( 'The SSL certificate for the host could not be verified.' ) ); |
171 return new WP_Error( 'http_request_failed', __( 'The SSL certificate for the host could not be verified.' ) ); |
|
172 } |
164 } |
173 } |
165 |
174 |
166 stream_set_timeout( $handle, $timeout, $utimeout ); |
175 stream_set_timeout( $handle, $timeout, $utimeout ); |
167 |
176 |
168 if ( $proxy->is_enabled() && $proxy->send_through_proxy( $url ) ) //Some proxies require full URL in this field. |
177 if ( $proxy->is_enabled() && $proxy->send_through_proxy( $url ) ) { //Some proxies require full URL in this field. |
169 $requestPath = $url; |
178 $requestPath = $url; |
170 else |
179 } else { |
171 $requestPath = $arrURL['path'] . ( isset($arrURL['query']) ? '?' . $arrURL['query'] : '' ); |
180 $requestPath = $arrURL['path'] . ( isset( $arrURL['query'] ) ? '?' . $arrURL['query'] : '' ); |
172 |
181 } |
173 $strHeaders = strtoupper($r['method']) . ' ' . $requestPath . ' HTTP/' . $r['httpversion'] . "\r\n"; |
182 |
|
183 $strHeaders = strtoupper( $r['method'] ) . ' ' . $requestPath . ' HTTP/' . $r['httpversion'] . "\r\n"; |
174 |
184 |
175 $include_port_in_host_header = ( |
185 $include_port_in_host_header = ( |
176 ( $proxy->is_enabled() && $proxy->send_through_proxy( $url ) ) || |
186 ( $proxy->is_enabled() && $proxy->send_through_proxy( $url ) ) || |
177 ( 'http' == $arrURL['scheme'] && 80 != $arrURL['port'] ) || |
187 ( 'http' == $arrURL['scheme'] && 80 != $arrURL['port'] ) || |
178 ( 'https' == $arrURL['scheme'] && 443 != $arrURL['port'] ) |
188 ( 'https' == $arrURL['scheme'] && 443 != $arrURL['port'] ) |
179 ); |
189 ); |
180 |
190 |
181 if ( $include_port_in_host_header ) { |
191 if ( $include_port_in_host_header ) { |
182 $strHeaders .= 'Host: ' . $arrURL['host'] . ':' . $arrURL['port'] . "\r\n"; |
192 $strHeaders .= 'Host: ' . $arrURL['host'] . ':' . $arrURL['port'] . "\r\n"; |
183 } else { |
193 } else { |
184 $strHeaders .= 'Host: ' . $arrURL['host'] . "\r\n"; |
194 $strHeaders .= 'Host: ' . $arrURL['host'] . "\r\n"; |
185 } |
195 } |
186 |
196 |
187 if ( isset($r['user-agent']) ) |
197 if ( isset( $r['user-agent'] ) ) { |
188 $strHeaders .= 'User-agent: ' . $r['user-agent'] . "\r\n"; |
198 $strHeaders .= 'User-agent: ' . $r['user-agent'] . "\r\n"; |
189 |
199 } |
190 if ( is_array($r['headers']) ) { |
200 |
191 foreach ( (array) $r['headers'] as $header => $headerValue ) |
201 if ( is_array( $r['headers'] ) ) { |
|
202 foreach ( (array) $r['headers'] as $header => $headerValue ) { |
192 $strHeaders .= $header . ': ' . $headerValue . "\r\n"; |
203 $strHeaders .= $header . ': ' . $headerValue . "\r\n"; |
|
204 } |
193 } else { |
205 } else { |
194 $strHeaders .= $r['headers']; |
206 $strHeaders .= $r['headers']; |
195 } |
207 } |
196 |
208 |
197 if ( $proxy->use_authentication() ) |
209 if ( $proxy->use_authentication() ) { |
198 $strHeaders .= $proxy->authentication_header() . "\r\n"; |
210 $strHeaders .= $proxy->authentication_header() . "\r\n"; |
|
211 } |
199 |
212 |
200 $strHeaders .= "\r\n"; |
213 $strHeaders .= "\r\n"; |
201 |
214 |
202 if ( ! is_null($r['body']) ) |
215 if ( ! is_null( $r['body'] ) ) { |
203 $strHeaders .= $r['body']; |
216 $strHeaders .= $r['body']; |
204 |
217 } |
205 fwrite($handle, $strHeaders); |
218 |
|
219 fwrite( $handle, $strHeaders ); |
206 |
220 |
207 if ( ! $r['blocking'] ) { |
221 if ( ! $r['blocking'] ) { |
208 stream_set_blocking( $handle, 0 ); |
222 stream_set_blocking( $handle, 0 ); |
209 fclose( $handle ); |
223 fclose( $handle ); |
210 return array( 'headers' => array(), 'body' => '', 'response' => array('code' => false, 'message' => false), 'cookies' => array() ); |
224 return array( |
211 } |
225 'headers' => array(), |
212 |
226 'body' => '', |
213 $strResponse = ''; |
227 'response' => array( |
214 $bodyStarted = false; |
228 'code' => false, |
|
229 'message' => false, |
|
230 ), |
|
231 'cookies' => array(), |
|
232 ); |
|
233 } |
|
234 |
|
235 $strResponse = ''; |
|
236 $bodyStarted = false; |
215 $keep_reading = true; |
237 $keep_reading = true; |
216 $block_size = 4096; |
238 $block_size = 4096; |
217 if ( isset( $r['limit_response_size'] ) ) |
239 if ( isset( $r['limit_response_size'] ) ) { |
218 $block_size = min( $block_size, $r['limit_response_size'] ); |
240 $block_size = min( $block_size, $r['limit_response_size'] ); |
|
241 } |
219 |
242 |
220 // If streaming to a file setup the file handle. |
243 // If streaming to a file setup the file handle. |
221 if ( $r['stream'] ) { |
244 if ( $r['stream'] ) { |
222 if ( ! WP_DEBUG ) |
245 if ( ! WP_DEBUG ) { |
223 $stream_handle = @fopen( $r['filename'], 'w+' ); |
246 $stream_handle = @fopen( $r['filename'], 'w+' ); |
224 else |
247 } else { |
225 $stream_handle = fopen( $r['filename'], 'w+' ); |
248 $stream_handle = fopen( $r['filename'], 'w+' ); |
|
249 } |
226 if ( ! $stream_handle ) { |
250 if ( ! $stream_handle ) { |
227 return new WP_Error( 'http_request_failed', sprintf( |
251 return new WP_Error( |
228 /* translators: 1: fopen() 2: file name */ |
252 'http_request_failed', |
229 __( 'Could not open handle for %1$s to %2$s.' ), |
253 sprintf( |
230 'fopen()', |
254 /* translators: 1: fopen(), 2: file name */ |
231 $r['filename'] |
255 __( 'Could not open handle for %1$s to %2$s.' ), |
232 ) ); |
256 'fopen()', |
|
257 $r['filename'] |
|
258 ) |
|
259 ); |
233 } |
260 } |
234 |
261 |
235 $bytes_written = 0; |
262 $bytes_written = 0; |
236 while ( ! feof($handle) && $keep_reading ) { |
263 while ( ! feof( $handle ) && $keep_reading ) { |
237 $block = fread( $handle, $block_size ); |
264 $block = fread( $handle, $block_size ); |
238 if ( ! $bodyStarted ) { |
265 if ( ! $bodyStarted ) { |
239 $strResponse .= $block; |
266 $strResponse .= $block; |
240 if ( strpos( $strResponse, "\r\n\r\n" ) ) { |
267 if ( strpos( $strResponse, "\r\n\r\n" ) ) { |
241 $process = WP_Http::processResponse( $strResponse ); |
268 $process = WP_Http::processResponse( $strResponse ); |
242 $bodyStarted = true; |
269 $bodyStarted = true; |
243 $block = $process['body']; |
270 $block = $process['body']; |
244 unset( $strResponse ); |
271 unset( $strResponse ); |
245 $process['body'] = ''; |
272 $process['body'] = ''; |
246 } |
273 } |
247 } |
274 } |
248 |
275 |
249 $this_block_size = strlen( $block ); |
276 $this_block_size = strlen( $block ); |
250 |
277 |
251 if ( isset( $r['limit_response_size'] ) && ( $bytes_written + $this_block_size ) > $r['limit_response_size'] ) { |
278 if ( isset( $r['limit_response_size'] ) && ( $bytes_written + $this_block_size ) > $r['limit_response_size'] ) { |
252 $this_block_size = ( $r['limit_response_size'] - $bytes_written ); |
279 $this_block_size = ( $r['limit_response_size'] - $bytes_written ); |
253 $block = substr( $block, 0, $this_block_size ); |
280 $block = substr( $block, 0, $this_block_size ); |
254 } |
281 } |
255 |
282 |
256 $bytes_written_to_file = fwrite( $stream_handle, $block ); |
283 $bytes_written_to_file = fwrite( $stream_handle, $block ); |
257 |
284 |
258 if ( $bytes_written_to_file != $this_block_size ) { |
285 if ( $bytes_written_to_file != $this_block_size ) { |
353 $certificate_hostnames = array(); |
385 $certificate_hostnames = array(); |
354 if ( ! empty( $cert['extensions']['subjectAltName'] ) ) { |
386 if ( ! empty( $cert['extensions']['subjectAltName'] ) ) { |
355 $match_against = preg_split( '/,\s*/', $cert['extensions']['subjectAltName'] ); |
387 $match_against = preg_split( '/,\s*/', $cert['extensions']['subjectAltName'] ); |
356 foreach ( $match_against as $match ) { |
388 foreach ( $match_against as $match ) { |
357 list( $match_type, $match_host ) = explode( ':', $match ); |
389 list( $match_type, $match_host ) = explode( ':', $match ); |
358 if ( $host_type == strtolower( trim( $match_type ) ) ) // IP: or DNS: |
390 if ( $host_type == strtolower( trim( $match_type ) ) ) { // IP: or DNS: |
359 $certificate_hostnames[] = strtolower( trim( $match_host ) ); |
391 $certificate_hostnames[] = strtolower( trim( $match_host ) ); |
360 } |
392 } |
361 } elseif ( !empty( $cert['subject']['CN'] ) ) { |
393 } |
|
394 } elseif ( ! empty( $cert['subject']['CN'] ) ) { |
362 // Only use the CN when the certificate includes no subjectAltName extension. |
395 // Only use the CN when the certificate includes no subjectAltName extension. |
363 $certificate_hostnames[] = strtolower( $cert['subject']['CN'] ); |
396 $certificate_hostnames[] = strtolower( $cert['subject']['CN'] ); |
364 } |
397 } |
365 |
398 |
366 // Exact hostname/IP matches. |
399 // Exact hostname/IP matches. |
367 if ( in_array( strtolower( $host ), $certificate_hostnames ) ) |
400 if ( in_array( strtolower( $host ), $certificate_hostnames ) ) { |
368 return true; |
401 return true; |
|
402 } |
369 |
403 |
370 // IP's can't be wildcards, Stop processing. |
404 // IP's can't be wildcards, Stop processing. |
371 if ( 'ip' == $host_type ) |
405 if ( 'ip' == $host_type ) { |
372 return false; |
406 return false; |
|
407 } |
373 |
408 |
374 // Test to see if the domain is at least 2 deep for wildcard support. |
409 // Test to see if the domain is at least 2 deep for wildcard support. |
375 if ( substr_count( $host, '.' ) < 2 ) |
410 if ( substr_count( $host, '.' ) < 2 ) { |
376 return false; |
411 return false; |
|
412 } |
377 |
413 |
378 // Wildcard subdomains certs (*.example.com) are valid for a.example.com but not a.b.example.com. |
414 // Wildcard subdomains certs (*.example.com) are valid for a.example.com but not a.b.example.com. |
379 $wildcard_host = preg_replace( '/^[^.]+\./', '*.', $host ); |
415 $wildcard_host = preg_replace( '/^[^.]+\./', '*.', $host ); |
380 |
416 |
381 return in_array( strtolower( $wildcard_host ), $certificate_hostnames ); |
417 return in_array( strtolower( $wildcard_host ), $certificate_hostnames ); |
382 } |
418 } |
383 |
419 |
384 /** |
420 /** |
385 * Determines whether this class can be used for retrieving a URL. |
421 * Determines whether this class can be used for retrieving a URL. |
386 * |
422 * |
387 * @static |
|
388 * @since 2.7.0 |
423 * @since 2.7.0 |
389 * @since 3.7.0 Combined with the fsockopen transport and switched to stream_socket_client(). |
424 * @since 3.7.0 Combined with the fsockopen transport and switched to stream_socket_client(). |
390 * |
425 * |
391 * @param array $args Optional. Array of request arguments. Default empty array. |
426 * @param array $args Optional. Array of request arguments. Default empty array. |
392 * @return bool False means this class can not be used, true means it can. |
427 * @return bool False means this class can not be used, true means it can. |
393 */ |
428 */ |
394 public static function test( $args = array() ) { |
429 public static function test( $args = array() ) { |
395 if ( ! function_exists( 'stream_socket_client' ) ) |
430 if ( ! function_exists( 'stream_socket_client' ) ) { |
396 return false; |
431 return false; |
|
432 } |
397 |
433 |
398 $is_ssl = isset( $args['ssl'] ) && $args['ssl']; |
434 $is_ssl = isset( $args['ssl'] ) && $args['ssl']; |
399 |
435 |
400 if ( $is_ssl ) { |
436 if ( $is_ssl ) { |
401 if ( ! extension_loaded( 'openssl' ) ) |
437 if ( ! extension_loaded( 'openssl' ) ) { |
402 return false; |
438 return false; |
403 if ( ! function_exists( 'openssl_x509_parse' ) ) |
439 } |
|
440 if ( ! function_exists( 'openssl_x509_parse' ) ) { |
404 return false; |
441 return false; |
|
442 } |
405 } |
443 } |
406 |
444 |
407 /** |
445 /** |
408 * Filters whether streams can be used as a transport for retrieving a URL. |
446 * Filters whether streams can be used as a transport for retrieving a URL. |
409 * |
447 * |