11<?php namespace peer \http ;
22
33use io \IOException ;
4- use peer \SSLSocket ;
5- use peer \TLSSocket ;
4+ use peer \CryptoSocket ;
5+ use peer \URL ;
6+ use util \Objects ;
67
78/**
89 * Transport via SSL sockets
1213 * @see xp://peer.http.HttpConnection
1314 */
1415class SSLSocketHttpTransport extends SocketHttpTransport {
16+ private static $ crypto = [
17+ 'tls ' => STREAM_CRYPTO_METHOD_TLS_CLIENT ,
18+ 'tlsv10 ' => STREAM_CRYPTO_METHOD_TLSv1_0_CLIENT,
19+ 'tlsv11 ' => STREAM_CRYPTO_METHOD_TLSv1_1_CLIENT,
20+ 'tlsv12 ' => STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT,
21+ 'sslv3 ' => STREAM_CRYPTO_METHOD_SSLv3_CLIENT,
22+ 'sslv23 ' => STREAM_CRYPTO_METHOD_SSLv23_CLIENT,
23+ 'sslv2 ' => STREAM_CRYPTO_METHOD_SSLv2_CLIENT
24+ ];
25+
26+ static function __static () {
27+
28+ // See https://github.com/php/php-src/commit/5c05f5e6d3d31a03c152fe90697bdc3e33193ced, PHP 7.4+
29+ if (defined ('STREAM_CRYPTO_METHOD_TLSv1_3_CLIENT ' )) {
30+ self ::$ crypto ['tlsv13 ' ]= STREAM_CRYPTO_METHOD_TLSv1_3_CLIENT;
31+ }
32+ }
1533
1634 /**
1735 * Creates a socket - overridden from parent class
@@ -20,32 +38,16 @@ class SSLSocketHttpTransport extends SocketHttpTransport {
2038 * @param string $arg
2139 * @return peer.Socket
2240 */
23- protected function newSocket (\peer \URL $ url , $ arg ) {
24- if ('tls ' === $ arg ) {
25- return new TLSSocket ($ url ->getHost (), $ url ->getPort (443 ), null );
41+ protected function newSocket (URL $ url , $ arg ) {
42+ $ socket = new CryptoSocket ($ url ->getHost (), $ url ->getPort (443 ));
43+ if (null === $ arg ) {
44+ $ socket ->cryptoImpl = STREAM_CRYPTO_METHOD_ANY_CLIENT ;
45+ } else if ('v ' === $ arg [0 ]) {
46+ $ socket ->cryptoImpl = self ::$ crypto ['ssl ' .$ arg ];
2647 } else {
27- sscanf ($ arg , 'v%d ' , $ version );
28- return new SSLSocket ($ url ->getHost (), $ url ->getPort (443 ), null , $ version );
29- }
30- }
31-
32- /**
33- * Enable cryptography on a given socket
34- *
35- * @param peer.Socket $s
36- * @param [:int] $methods
37- * @return void
38- * @throws io.IOException
39- */
40- protected function enable ($ s , $ methods ) {
41- $ handle = $ s ->getHandle ();
42- foreach ($ methods as $ name => $ method ) {
43- if (stream_socket_enable_crypto ($ handle , true , $ method )) {
44- $ this ->cat && $ this ->cat ->debug ('@@@ Enabling ' , $ name , 'cryptography ' );
45- return ;
46- }
48+ $ socket ->cryptoImpl = self ::$ crypto [$ arg ];
4749 }
48- throw new IOException ( ' Cannot establish secure connection, tried ' . implode ( ' , ' , array_keys ( $ methods ))) ;
50+ return $ socket ;
4951 }
5052
5153 /**
@@ -54,44 +56,41 @@ protected function enable($s, $methods) {
5456 * @param peer.Socket $s Connection to proxy
5557 * @param peer.http.HttpRequest $request
5658 * @param peer.URL $url
59+ * @return void
5760 * @throws io.IOException
5861 */
5962 protected function proxy ($ s , $ request , $ url ) {
60- static $ methods = [
61- 'tls:// ' => STREAM_CRYPTO_METHOD_TLS_CLIENT ,
62- 'tlsv10:// ' => STREAM_CRYPTO_METHOD_TLSv1_0_CLIENT,
63- 'tlsv11:// ' => STREAM_CRYPTO_METHOD_TLSv1_1_CLIENT,
64- 'tlsv12:// ' => STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT,
65- 'sslv3:// ' => STREAM_CRYPTO_METHOD_SSLv3_CLIENT,
66- 'sslv23:// ' => STREAM_CRYPTO_METHOD_SSLv23_CLIENT,
67- 'sslv2:// ' => STREAM_CRYPTO_METHOD_SSLv2_CLIENT,
68- ];
69-
7063 $ connect = sprintf (
7164 "CONNECT %1 \$s:%2 \$d HTTP/1.1 \r\nHost: %1 \$s:%2 \$d \r\n\r\n" ,
7265 $ url ->getHost (),
7366 $ url ->getPort (443 )
7467 );
7568 $ this ->cat && $ this ->cat ->info ('>>> ' , substr ($ connect , 0 , strpos ($ connect , "\r" )));
7669 $ s ->write ($ connect );
70+
71+ // Verify we are actually talking to a HTTP proxy
7772 $ handshake = $ s ->read ();
73+ if (4 !== ($ r = sscanf ($ handshake , "HTTP/%*d.%*d %d %[^ \r] " , $ status , $ message ))) {
74+ throw new IOException ('Proxy did not answer with valid HTTP: ' .$ handshake );
75+ }
7876
79- if (4 === ($ r = sscanf ($ handshake , "HTTP/%*d.%*d %d %[^ \r] " , $ status , $ message ))) {
80- while ($ line = $ s ->readLine ()) { $ handshake .= $ line ."\n" ; }
81- $ this ->cat && $ this ->cat ->info ('<<< ' , $ handshake );
77+ // Verify proxy answers with a 200 status code
78+ while ($ line = $ s ->readLine ()) $ handshake .= $ line ."\n" ;
79+ $ this ->cat && $ this ->cat ->info ('<<< ' , $ handshake );
80+ if (200 !== $ status ) {
81+ throw new IOException ('Cannot connect through proxy: # ' .$ status .' ' .$ message );
82+ }
8283
83- if (200 === $ status ) {
84- stream_context_set_option ($ s ->getHandle (), 'ssl ' , 'peer_name ' , $ url ->getHost ());
85- if (isset ($ methods [$ this ->socket ->_prefix ])) {
86- $ this ->enable ($ s , [$ this ->socket ->_prefix => $ methods [$ this ->socket ->_prefix ]]);
87- } else {
88- $ this ->enable ($ s , $ methods );
89- }
90- } else {
91- throw new IOException ('Cannot connect through proxy: # ' .$ status .' ' .$ message );
84+ // Enable cryptography
85+ stream_context_set_option ($ s ->getHandle (), 'ssl ' , 'peer_name ' , $ url ->getHost ());
86+ if (!stream_socket_enable_crypto ($ s ->getHandle (), true , $ this ->socket ->cryptoImpl )) {
87+ $ methods = '' ;
88+ foreach (self ::$ crypto as $ name => $ flag ) {
89+ if ($ flag === $ this ->socket ->cryptoImpl & $ flag ) $ methods .= ', ' .$ name ;
9290 }
93- } else {
94- throw new IOException ('Proxy did not answer with valid HTTP: ' .$ handshake );
91+ throw new IOException ('Cannot establish secure connection, tried ' .substr ($ methods , 2 ));
9592 }
93+
94+ $ this ->cat && $ this ->cat ->debug ('@@@ Enabled cryptography: ' .Objects::stringOf (stream_get_meta_data ($ s ->getHandle ())['crypto ' ]));
9695 }
9796}
0 commit comments