I have a PHP script that I use to send emails to my newsletter list in one of my sites.
The script uses STARTTLS for encrypted connections, using the following line to establish the SSL handshake:
stream_set_timeout($s, 35, 0);
if(false == stream_socket_enable_crypto($s, true, STREAM_CRYPTO_METHOD_TLS_CLIENT)){
$msg = "452 failed on tls connection";
} else {
$in_tls = true;
}
The socket $s is set to blocking and is already connected to the remote server after issuing a STARTTLS command and ready to start TLS handshake at this stage. As you can see, I am using stream_set_timeout before the handshake. According to the PHP docs it should abort the handshake after X seconds, but it doesn't seem to affect it.
Now this code works most of the time, but I sometimes run into servers where the TLS handshake would just block indefinitely, causing the script to hang.
I've tried looking into non-blocking solutions, but none of them worked for my PHP version (I use v5.1.6).
The only other option is to somehow monitor this line for timeout (I'm not sure if that's possible), or to somehow transfer the socket handle to another process I can run with a timeout control method.
Anyone knows how to solve this?
You could try to set a timeout on the stream, see php manual for stream_set_timeout($s)
When the stream times out, the 'timed_out' key of the array returned
by stream_get_meta_data() is set to TRUE, although no error/warning is
generated.
Also found this on PHP manual
In case anyone is puzzled, stream_set_timeout DOES NOT work for
sockets created with socket_create or socket_accept. Use
socket_set_option instead.
Instead of:
<?php
stream_set_timeout($socket,$sec,$usec);
?>
Use:
<?php
socket_set_option($socket, SOL_SOCKET, SO_RCVTIMEO, array('sec'=>$sec, 'usec'=>$usec));
socket_set_option($socket, SOL_SOCKET, SO_SNDTIMEO, array('sec'=>$sec, 'usec'=>$usec));
?>
Update: This allowed the OP to move past the issue
ini_set('default_socket_timeout', 1);
Related
$socket = socket_create(AF_UNIX, SOCK_STREAM, 0);
socket_connect($socket, CLAMD_SOCKET);
socket_send($socket, 'PING', 4, 0);
socket_recv($socket, $output, 4, 0);
// check to see if $output === PONG (which it is)
$scan = 'SCAN ' . $file;
socket_send($socket, $scan, strlen($scan), 0);
socket_recv($socket, $output, 100, 0);
// $output should contain the result from the socket, but instead it's blank
// var_dump says it's a 1 character string " "
This is my original code.
However, if I close the socket after the PING command and PONG is returned (which it is), then re-open it again, the SCAN command works and it returns the result as expected.
Is this how it's supposed to work, or do I need to do something else in between the commands? Is the socket not ready for another command to be sent?
I'm not really sure what to look for, I can't find anything in the PHP manual. It does work, but I feel as if having to close the socket and re-open it is the wrong way to do things.
I can't find anything in the PHP manual.
The behavior does not depend on PHP but on the protocol spoken between the client and the server - i.e. what protocol is expected and enforced by clamd. Therefore no information about this could be found in the PHP documentation, but one would need to look into the ClamAV documentation instead.
While the clamd documentation describes the commands it unfortunately lacks the information if multiple commands are allowed. A short test (for example with netcat) shows though, that clamd closes the connection after the response to the command, so no more commands on the same connection are possible.
It does work, but I feel as if having to close the socket and re-open it is the wrong way to do things.
It is definitely not the most efficient way of doing things, but the overhead on UNIX domain sockets or even local TCP sockets for creating a new connection is low - compared to establishing a TCP connection to a remote system.
Being able to send multiple commands and therefore get multiple responses would require a clear separation on where a command and response ends though. This is kind of documented for commands, but not for responses. So the only obvious option here is to read until the end of the connection, i.e. until connection close. This design makes it impossible though to send more commands on an established connection.
Once upon a time, there was a normalish error in PHP land:
Warning: ftp_nlist(): data_accept: SSL/TLS handshake failed in [path] on line 29
But here's the catch, "line 29" is not the connection or login, note how it referenced the ftp_nlist() function:
$ftp = ftp_ssl_connect($cred['host'], $cred['port'], 180);
if (!ftp_login($ftp, $cred['user'], $cred['pass'])) {die("Login Failed");}
ftp_pasv($ftp, true);
$files = ftp_nlist($ftp, '');
OpenSSL is compiled and enabled in phpinfo() as suggested here:
ftp_login() : SSL/TLS handshake failed
Other posts I've seen all seem to reference error in the ftp_ssl_connect() or ftp_login() commands which work for me. What can I check when ftp_login() returns true?
Or... are there any logs to get more details on what is wrong?
I'm using php 5.3.29. The code does work properly on my desktop (php 7), but I'm hoping I don't have to upgrade the server to 7 for this to work
12-28-2017 update:
Upgrading to 5.6 resolved, so looks like Martin is on point.
Although this question is quite old, but in case someone else hits this problem:
If your ftp_ssl_connect and ftp_login works fine but functions like ftp_nlist, ftp_put, ftp_fput don't works the problem might be that your FTP server is using port 21 for Connection but different port ranges for data transfer, that explains why you can connect and login but you can't upload or download data, and you need to allow the Out-going connections to those port range in your firewall
The ftp_nlist opens a data connection. That connection needs TLS/SSL handshake too.
As the control connection handshake succeeded, the problem indeed cannot be with an absent TLS/SSL support in PHP. Neither the problem can be with anything like the server and PHP not being able to find a cipher to agree on.
When TLS/SSL handshake on data connection fails after handshake on control connection succeeded, it's quite usually because the client (PHP) did not reuse TLS/SSL session from control connection on the data connection (see Why is session reuse useful in FTPS?). Some servers do require that. PHP supports the reuse only since 5.6.26. See PHP Bug 70195. So make sure you use that version of PHP at least.
I've seen this question asked at least a dozen times. None of the responses helped me.
The code:
$host = "127.0.0.1";
$port = 80;
//no timeout
set_time_limit(0);
//create socket
$socket=socket_create(AF_INET, SOCK_STREAM, 0) or die("Could not create socket\n");
if (!socket_set_option($socket, SOL_SOCKET, SO_REUSEADDR, 1)) {
echo socket_strerror(socket_last_error($socket));
exit;
}
$result = socket_bind($socket, $host, $port) or die("Could not bind socket\n");
The 'if' statement was added because one response suggested putting it in there. Didn't seem to do anything for me. I plan to take it out. Anyway, my error is:
Warning: socket_bind(): unable to bind address [10013]: An attempt was made to access a socket in a way forbidden by its access permissions.
And, yes, I get that its supposed to mean the port is in use by another process. I changed the port number about 30 times. I temporarily turned off my (Windows 8) firewall. I ran netstat and I see that these ports don't close so I have like 30 sockets with the same PID and name.
So, my question is: what am I doing wrong?
Thanks to those that responded, but I figured out the problem on my own. The tutorial I used said to navigate to the server.php file. Don't laugh, but I navigated there via the browser. So, that message was popping up on my browser.
I ran the same file in the command prompt and I was either getting that error or it looked like it was hanging there depending on the port I chose. It wasn't crashing... it just had nothing to output. I gave it something to output (yay echo statements!!).
Anyway, thanks again.
To bind to a port below 1024 the process needs privileged rights. Typical only root could do this.
For a more detailed discussion in this topic please read here: Is there a way for non-root processes to bind to "privileged" ports on Linux?
Most frequently the reason that you'll get this error is that the socket you're trying to use is already in use by another program. You're trying to open a socket to listen on port 80, what port is apache listening on? If it's also port 80, there's your problem.
I'm trying to make a simple listener on port 8195. When I try the following code block in PHP CLI conditions, it only shows 'Test' once, then hangs. If I delete the file 'votifier.run', the file designed to be the on/off switch, it still continues to hang. It never shows 'Client connected'.
Furthermore, if I try to connect to the host via Telnet on port 8195 while the script is running, I simply get a connection failed message. It's like it's looking for one connection and just not giving up.
// Set the IP and port to listen to
$address = 'localhost';
$port = 8195;
// Create a TCP Stream socket
$sock = socket_create(AF_INET, SOCK_STREAM, 0);
// Bind the socket to an address/port
socket_bind($sock, $address, $port);
// Start listening for connections
socket_listen($sock);
// Loop continuously
while ( file_exists('votifier.run') ) {
echo 'Test';
$client = socket_accept($sock);
if( $client ) {
echo 'Client connected';
// Don't hang on slow connections
socket_set_timeout($client, 5);
// Send them our version
socket_write("VOTIFIER MCWEBLINK\n");
// Read the 256 byte block
$block = socket_read($client, 256);
...
The answer:
socket_accept() will usually hang until a connection is made. If a connection attempt was made, the script would continue, but because the socket was being created on localhost, it would only accept connections to it from localhost.
The fix is to use your external IP rather than 'localhost' or '127.0.0.1'. Then you can Telnet to it.
I'm just guessing here, but could it be that the address you are trying to bind to should not be a hostname?
If the socket is of the AF_INET family, the address is an IP in dotted-quad notation (e.g. 127.0.0.1).
EDIT
Ok, I've taken your script and tried to reproduce your error but couldn't. There are a couple of flaws in it but none that would cause a telnet client's connection attempt to fail.
Since none of the aforementioned applies, let's go thru the checklist one by one:
sockets module loaded/compiled
localhost does resolve to 127.0.0.1
the port isn't taken by any other application running
there's no rule of any sort of firewall that would prevent communication between the telnet client and your server
the machine which you connect from is allowed to connect to the server host (try the same host if it isn't)
the file that's being checked in the while-loop does exist
you are sure that there isn't another fatal error within your script that would prevent the snippet you posted from running
These are all the possible error sources I can think of, atm. Try fixing up the minor flaws first, then go thru the checklist.
if( $client ) {
echo 'Client connected';
// Don't hang on slow connections
socket_set_option(
$client,
SOL_SOCKET,
SO_RCVTIMEO | SO_SNDTIMEO,
array('sec' => 5, 'usec' => 0)
);
// Send them our version
socket_write($client, "VOTIFIER MCWEBLINK\n");
^^^^^^^
// Read the 256 byte block
$block = socket_read($client, 256);
You should be using threads. If the client never sends anything your code will block in the read() method. Each accepted socket should be completely handled in a new thread.
You may want to check this:
PHP Votifier example for Minecraft Topsites
It explains how the code works, it's the basic function that makes the encryption, fills up the 256 blank spaces and sends the packet too. You can work a little with it as you may want to improve it.
You can see a live demo of the running php for the plugin here: http://topg.org/test_votifier
I have a small application I created to analyze the network connection. It runs from a browser and connects to a local PHP/Apache server. It then asks PHP to send a ping packet through a raw socket. THe problem is that if the host I am trying to ping isn't alive or won't answer to pings, we never get an answer from the server.
I beleave the socket request lives until apache is restarted. I have been getting mixed results from my application lately and I am blaming apache using too many sockets. Currently I have set the AJAX call's timeout and I was happy with it. But I really need to make PHP do the timeouting so that I won't have 500,000 sockets open to an unreachable host.
Some sample code:
$sockconn = #socket_connect($socket, $target, null);
if(!$sockconn)
{
$raw['error'] = socket_strerror(socket_last_error());
$raw['status'] = false;
return $raw;
}
This is the function that won't timeout. I need to get it to timeout. Also PHP script execution time DOES NOT affect sockets.
I am clueless.
You can set timeouts for reading and sending using the following options:
socket_set_option($socket, SOL_SOCKET, SO_RCVTIMEO, array('sec' => 1, 'usec' => 0));
socket_set_option($socket, SOL_SOCKET, SO_SNDTIMEO, array('sec' => 1, 'usec' => 0));
Alternatively, you can use non-blocking sockets and periodically poll the socket to see if the remote host responded.
Try setting default_socket_timeout.