Unable to establish TLS connection in PHP via fsockopen - php

I'm trying since a couple of days to establish a TLS connection to a SMTP server in PHP via fsockopen() on my newly installed Ubuntu server. I#ve tried almost everything and googled for hours but still I didn't get it working.
The PHP code looks as follows:
$fp = fsockopen("tls://smtp.xxxx.com", 25, $errno, $errstr, 30);
if (!$fp) {
echo "$errstr ($errno)<br />\n";
} else {
// some other stuff
}
The output is just (0), i.e., $errstr = null and $errno = 0.
OpenSSL is installed and enabled:
OpenSSL support: enabled
OpenSSL Library Version: OpenSSL 0.9.8o 01 Jun 2010
OpenSSL Header Version: OpenSSL 0.9.8o 01 Jun 2010
and the following stream socket transports are registered:
tcp, udp, unix, udg, ssl, sslv3, sslv2, tls.
The port is open as a telnet from the console works.
Any ideas what's wrong or how I could at least get some more debug output?
Thanks,
Markus

Your connection doesn't make much sense. By using the TLS handler, you want TLS to be established BEFORE any data goes. But port 25 is standard SMTP, which can only establish TLS AFTER you've initially connected via an unencrypted regular connection. Once that initial connection is established, then you can enable TLS with the STARTTLS command to tell the SMTP server to switch over.
If you want TLS from the get-go, then use port 465, which is ssl/tls from the start.

With gmail, the ssl connection port had ssl from the get-go, but the tls port, you connected plain, and had to start tls manually with a STARTTLS command. I'm guessing this is the same. Here's and example to gmail so you can see what's going on. The EHLO command shows the STARTTLS command while if you start with ssl from the begining, it goes strait to the AUTH XOAUTH command list.
<?php
function get($socket,$length=1024){
$send = '';
$sr = fgets($socket,$length);
while( $sr ){
$send .= $sr;
if( $sr[3] != '-' ){ break; }
$sr = fgets($socket,$length);
}
return $send;
}
function put($socket,$cmd,$length=1024){
fputs($socket,$cmd."\r\n",$length);
}
if (!($smtp = fsockopen("smtp.gmail.com", 587, $errno, $errstr, 15))) {
die("Unable to connect");
}
echo "<pre>\n";
echo get($smtp); // should return a 220 if you want to check
$cmd = "EHLO ${_SERVER['HTTP_HOST']}";
echo $cmd."\r\n";
put($smtp,$cmd);
echo get($smtp); // 250
$cmd = "STARTTLS";
echo $cmd."\r\n";
put($smtp,$cmd);
echo get($smtp); // 220
if(false == stream_socket_enable_crypto($smtp, true, STREAM_CRYPTO_METHOD_TLS_CLIENT)){
// fclose($smtp); // unsure if you need to close as I haven't run into a security fail at this point
die("unable to start tls encryption");
}
$cmd = "EHLO ".$_SERVER['HTTP_HOST'];
echo $cmd;
put($smtp,$cmd);
echo get($smtp); // 250
$cmd = "QUIT";
echo $cmd."\r\n";
put($smtp,$cmd);
echo get($smtp);
echo "</pre>";
fclose($smtp);

If it works from the command line but not from within Apache then there is probably some difference between the PHP configuration: do a diff between /etc/php/apache2/php.ini and /etc/php/cli/php.ini and see what might have changed.

Related

Why not connect PHP imap/pop3?

I'm sorry, I speak a little English.
Some days try, try and try. Read all stackoverflow questions and answers this, but no help.
I would like only one imap connection.
I try some webhost and not works.
Please help me.
// $foo = "{pop3.indamail.hu:110}"; // [CLOSED] IMAP connection broken (server response)
// $foo = "{pop3.indamail.hu:110/pop3}"; // Can not authenticate to POP3 server: POP3 connection broken in response
// $foo = "{imap.indamail.hu:143}"; // Can not authenticate to IMAP server: [CLOSED] IMAP connection broken (authenticate)
// $foo = "{imap.indamail.hu:143/imap}"; // Can not authenticate to IMAP server: [CLOSED] IMAP connection broken (authenticate)
// $foo = "{pop3.indamail.hu:110/pop3}INBOX"; // Can not authenticate to POP3 server: POP3 connection broken in response
// $foo = "{imap.indamail.hu:143/imap}INBOX"; // Can not authenticate to IMAP server: [CLOSED] IMAP connection broken (authenticate)
// $foo = "{imap.indamail.hu:143/imap/tls}INBOX"; // Unable to negotiate TLS with this server
// $foo = "{imap.indamail.hu:143/imap/notls}INBOX"; // Can not authenticate to IMAP server: [CLOSED] IMAP connection broken (authenticate)
// $foo = "{imap.indamail.hu:143/imap/novalidate-cert}INBOX"; // Can not authenticate to IMAP server: [CLOSED] IMAP connection broken (authenticate)
// $foo = "{imap.indamail.hu:993/imap}"; // [CLOSED] IMAP connection broken (server response)
// $foo = "{pop3.indamail.hu:995/pop3}"; // POP3 connection broken in response
// $foo = "{imap.indamail.hu:993/imap/ssl/novalidate-cert}"; // Can not authenticate to IMAP server: [CLOSED] IMAP connection broken (authenticate)
// $foo = "{pop3.indamail.hu:995/pop3/ssl/novalidate-cert}"; // Can not authenticate to POP3 server: POP3 connection broken in response
$mbox = imap_open ( $foo, "********#indamail.hu", "********" ) or die ( imap_last_error () );
The errors, please see the code.
I try, but ok-ok:
// $fp = fsockopen ( "imap.indamail.hu", 143, $errno, $errstr, 30 ); // ok
// $fp = fsockopen ( "imap.indamail.hu", 993, $errno, $errstr, 30 ); // ok
// $fp = fsockopen ( "pop3.indamail.hu", 110, $errno, $errstr, 30 ); // ok
// $fp = fsockopen ( "pop3.indamail.hu", 995, $errno, $errstr, 30 ); // ok
// $fp = fsockopen ( "imap.indamail.hu", 992, $errno, $errstr, 30 ); // Connection timed out (110)
if ( ! $fp ) {
echo "$errstr ($errno)";
} else {
echo "ok";
fclose ( $fp );
}
Important! Only access countries: HU, SK, AT, RO, HR. My IP-address is HU/Hungary. I read the manual and I think only this ports: pop3:110 and imap:143.
Please help me.
It works.
$e = '********#indamail.hu';
$s = '{imap.indamail.hu:143/authuser=' . $e . '}';
$mbox = imap_open ( $s, $e, '********' ) or die ( imap_last_error () );
No comment.

How to use fsockopen (or compatible) with SOCKS proxies in PHP?

I've coded a non-evil, non-spammy IRC bot in PHP, using fsockopen and related functions. It works. However, the problem is that I need to support proxies (preferably SOCKS5, but HTTP is also OK if that is somehow easier, which I doubt). This is not supported by fsockopen.
I've gone through all search results for "PHP fsockopen proxy" and related queries. I know of all the things that don't work, so please don't link to one of them.
The PHP manual page for fsockopen mentions the function stream_socket_client() as
similar but provides a richer set of options, including non-blocking connection and the ability to provide a stream context.
This sounded promising at first, supposedly allowing me to just replace the fsockopen call with stream_socket_client and specify a proxy, maybe via a "stream context"... but it doesn't. Or does it? I'm very confused by the manual.
Please note that it must be a PHP code solution; I cannot pay for "Proxifier" or use any other external software to "wrap around" this.
All the things I've tried seem to always result in me getting a bunch of empty output from the server, and then the socket is forcefully closed. Note that the proxy I'm trying with works when I use HexChat (a normal IRC client), with the same network, so it's not the proxies themselves that are at fault.
As far as I know there is no default option to set a SOCKS or HTTP proxy for fsockopen or stream_socket_client (we could create a context and set a proxy in HTTP options, but that doesn't apply to stream_socket_client). However we can establish a connection manually.
Connecting to HTTP proxies is quite simple:
The client connects to the proxy server and submits a CONNECT request.
The server responds 200 if the request is accepted.
The server then proxies all requests between the client and destination host.
<!- -!>
function connect_to_http_proxy($host, $port, $destination) {
$fp = fsockopen($host, $port, $errno, $errstr);
if ($errno == 0) {
$connect = "CONNECT $destination HTTP/1.1\r\n\r\n";
fwrite($fp, $connect);
$rsp = fread($fp, 1024);
if (preg_match('/^HTTP\/\d\.\d 200/', $rsp) == 1) {
return $fp;
}
echo "Request denied, $rsp\n";
return false;
}
echo "Connection failed, $errno, $errstr\n";
return false;
}
This function returns a file pointer resource if the connection is successful, else FALSE. We can use that resource to communicate with the destination host.
$proxy = "138.204.48.233";
$port = 8080;
$destination = "api.ipify.org:80";
$fp = connect_to_http_proxy($proxy, $port, $destination);
if ($fp) {
fwrite($fp, "GET /?format=json HTTP/1.1\r\nHost: $destination\r\n\r\n");
echo fread($fp, 1024);
fclose($fp);
}
The communication protocol for SOCKS5 proxies is a little more complex:
The client connects to the proxy server and sends (at least) three bytes: The first byte is the SOCKS version, the second is the number of authentication methods, the next byte(s) is the authentication method(s).
The server responds with two bytes, the SOCKS version and the selected authentication method.
The client requests a connection to the destination host. The request contains the SOCKS version, followed by the command (CONNECT in this case), followed by a null byte. The fourth byte specifies the address type, and is followed by the address and port.
The server finally sends ten bytes (or seven or twenty-two, depending on the destination address type). The second byte contains the status and it should be zero, if the request is successful.
The server proxies all requests.
<!- -!>
More details: SOCKS Protocol Version 5.
function connect_to_socks5_proxy($host, $port, $destination) {
$fp = fsockopen($host, $port, $errno, $errstr);
if ($errno == 0) {
fwrite($fp, "\05\01\00");
$rsp = fread($fp, 2);
if ($rsp === "\05\00" ) {
list($host, $port) = explode(":", $destination);
$host = gethostbyname($host); //not required if $host is an IP
$req = "\05\01\00\01" . inet_pton($host) . pack("n", $port);
fwrite($fp, $req);
$rsp = fread($fp, 10);
if ($rsp[1] === "\00") {
return $fp;
}
echo "Request denied, status: " . ord($rsp[1]) . "\n";
return false;
}
echo "Request denied\n";
return false;
}
echo "Connection failed, $errno, $errstr\n";
return false;
}
This function works the same way as connect_to_http_proxy. Although both functions are tested, it would be best to use a library; the code is provided mostly for educational purposes.
SSL support and authentication.
We can't create an SSL connection with fsockopen using the ssl:// or tls:// protocol, because that would attempt to create an SSL connection with the proxy server, not the destination host. But it is possible to enable SSL with stream_socket_enable_crypto and create a secure communication channel with the destination, after the connenection with the proxy server has been established. This requires to disable peer verification, which can be done with stream_socket_client using a custom context. Note that disabling peer verification may be a security issue.
For HTTP proxies we can add authentication with the Proxy-Authenticate header. The value of this header is the authentication type, followed by the username and password, base64 encoded (Basic Authentication).
For SOCKS5 proxies the authentication process is - again - more complex. It seems we have to change the authentication code fron 0x00 (NO AUTHENTICATION REQUIRED) to 0x02 (USERNAME/PASSWORD authentication). It is not clear to me how to create a request with the authentication values, so I can not provide an example.
function connect_to_http_proxy($host, $port, $destination, $creds=null) {
$context = stream_context_create(
['ssl'=> ['verify_peer'=> false, 'verify_peer_name'=> false]]
);
$soc = stream_socket_client(
"tcp://$host:$port", $errno, $errstr, 20,
STREAM_CLIENT_CONNECT, $context
);
if ($errno == 0) {
$auth = $creds ? "Proxy-Authorization: Basic ".base64_encode($creds)."\r\n": "";
$connect = "CONNECT $destination HTTP/1.1\r\n$auth\r\n";
fwrite($soc, $connect);
$rsp = fread($soc, 1024);
if (preg_match('/^HTTP\/\d\.\d 200/', $rsp) == 1) {
return $soc;
}
echo "Request denied, $rsp\n";
return false;
}
echo "Connection failed, $errno, $errstr\n";
return false;
}
$host = "proxy IP";
$port = "proxy port";
$destination = "chat.freenode.net:6697";
$credentials = "user:pass";
$soc = connect_to_http_proxy($host, $port, $destination, $credentials);
if ($soc) {
stream_socket_enable_crypto($soc, true, STREAM_CRYPTO_METHOD_ANY_CLIENT);
fwrite($soc,"USER test\nNICK test\n");
echo fread($soc, 1024);
fclose($soc);
}

PHP udp server: unable to connect to remote host

I get this error message, when try connect to tiny UDP server. The source code is minimized and looks like so:
//server.php
$server_host = '127.0.0.1';
$server_port = 21665;
$poll_interval = 0.5;
$socket = socket_create(AF_INET, SOCK_DGRAM, 0);
socket_bind($socket, $server_host, $server_port);
$clients = [$socket];
while(true) {
$read = $clients;
$write = [];
$except = [];
if (socket_select($read, $write, $except, $poll_interval) < 1){
continue;
}
if (in_array($socket, $read)) {
echo "Client submitted request!\n";
//request parsing
}
}
So, when I run the server with $ php server.php it hangs for ever, as it should. When however I try to connect to it through telnet to post a request I get an error message:
$ telnet 127.0.0.1 21665
Trying 127.0.0.1 ...
Telnet: unable to connect to remote host: Connection refused
What I'm doing wrong and how can I fix it?
What I'm doing wrong
You are using telnet (a TCP client) to try to connect to a UDP port.
how can I fix it
Use a client that supports UDP, for example netcat as explained in this answer: telnet counterpart for udp
nc -u <host> <port>
Start typing and press enter.

PHP UDP fsockopen fread (RCON)

I want to get a message back after succesfully connected with an UDP address via fsockopen. Connection with a RCON ModManager (game server).
What to expect?
If I use Telnet:
Telnet 31.204.131.9 15502 , I see:
ModManager Rcon v8.5
Digest seed: iJrrQAkv
Now in PHP:
<?php
$_ip = '31.204.131.9' ;
$_port = '15502';
if (($socket = fsockopen ('udp://'.$_ip, $_port, $errno, $errstr, 30))) {
// till here it works, got connected
// Digest seed?
if(fwrite($socket, "GET / HTTP/1.1\r\n")) { // writing works }
echo fread($socket, 1024); // NOTHING
fclose($socket);
}
?>
You can try it.. IP and Port valid. Thnx in advance!!
When you use telnet you use tcp protocol, not udp. Try
<?php
$_ip = '31.204.131.9' ;
$_port = '15502';
if (($socket = fsockopen ($_ip, $_port, $errno, $errstr, 30))) {
// till here it works, got connected
// Digest seed?
if(fwrite($socket, "GET / HTTP/1.1\r\n")) { echo "writing works\n"; }
echo fread($socket, 1024); // NOTHING
fclose($socket);
}

PHP socket server answer eof char to linux telnet

I want to talk with php server with socket by telnet.
I wrote 'echo' server (i send string to server, server send it to me)
i use chr(0) on end of output string to send information that string is sent
socket_write($client, $output.chr(0));
but telnet haven't see it and i cant send new string
TELNET
telnet 127.0.0.1 9000
Trying 127.0.0.1...
Connected to 127.0.0.1.
Escape character is '^]'.
hello
hello_
PHP
<?php
set_time_limit (0);
$address = '127.0.0.1';
$port = 9000;
$sock = socket_create(AF_INET, SOCK_STREAM, 0);
socket_bind($sock, $address, $port) or die('Could not bind');
socket_listen($sock);
while(true) {
$client = socket_accept($sock);
$input = trim(socket_read($client, 1024));
if ($input == 'off') break;
$output = $input.chr(0);
socket_write($client, $output);
}
socket_close($client);
socket_close($sock);
?>
what i'm doing wrong?
You should end the lines with carriage return or "\r\n", instead of just the \0 character. The telnet client watches out for them too I think.
The real problem however is your use of $client = socket_accept(..) within the loop. You must only establish the connection once, before the while. Otherwise you will reset the connected stream.

Categories