Best way to execute commands to TCP/IP console from PHP - php

Before I start, excuse my english, I'm from Holland :)
I have a question regarding the use of PHP's fsockopen.
My Prerequisites
So basically, I have a Windows program running in the background which has a remote console over TCP/IP that I need to connect to so I can execute a few commands. I am able to connect to that console with KiTTY, and execute my commands without any problems.
My Solution
So the issue I have right now, is that I need to be able to execute these commands from the browser. I have searched the interwebs for best ways to do this and what I found was to use PHP's fsockopen to connect to my console. The code I tried is as follows:
$SOCKET = fsockopen("127.0.0.1", 12101, $errno, $errstr);
if($SOCKET){
echo "Connected!";
}
$firstRead = fread($SOCKET, 8000);
echo($firstRead);
And using fputs to send a command:
fputs($SOCKET, "HELP \r\n");
And after, reading out my response with this:
$response = fread($SOCKET, 8000);
echo $response;
The Problem(s)
But I have encountered a few weird problems when testing this.
As soon as I execute a command like "HELP", I can see from my KiTTY session that the command was executed and that I got a response, but when I read out the response with "fread" I get nothing. But when I use a loop to read it out like this, it reads something from the console at the second try almost everytime:
do {
$response = fread($SOCKET, 8000);
$i++;
} while (strlen($response) < 5 || $i < 5);
( Sometimes, it DOES read something from console on first try, but mostly it only reads something on second try ).
The Question
Now my question(s) is(are), why does it behave so strangely? And is it because I am doing something wrong? And is this really the best way to do this?
sidenote
When this works, I need to be able to call these PHP functions ( or something similar ) with a bunch of AJAX requests and get the response to show in the browser. This is an absolute MUST so please keep this in mind when writing a possible answer :)
Thanks everyone!

When you create a socket with fsockopen in PHP you might also want to specify if it is blocking or non-blocking, in case of a non-blocking socket the function socket_read will return false on error or if the connection was closed, or empty string until some data is received, in case of a blocking socket instead when you read on it, it will block until there is some data to read (or empty string if a timeout is hit).
The behavior you described seems to be non-blocking.
For changing the blocking type there are: socket_set_block and socket_set_nonblock.
When your code with sockets works, there won't be any problems with AJAX requests, but keep in mind to set a timeout in PHP socket, otherwise if the server is down or simply too slow the request will fail with error (a timeout from php if set_time_limit is exceeded, which is a fatal error, or a JavaScript one with the browser timeout constant).
Here are the links to manual of socket_read and socket_write, which I think are more appropriated of fread and fputs.

Related

Detecting peer disconnect (EOF) with PHP socket module

I'm having a weird issue with PHP's sockets library: I do not seem to be able to detect/distinguish server EOF, and my code is helplessly going into an infinite loop as a result.
Further explanation below; first of all, some context (there's nothing particularly fancy going on here):
<?php
$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
socket_connect($socket, '127.0.0.1', 8081);
for (;;) {
$read = [$socket];
$except = NULL;
$write = [];
print "Select <";
$n = socket_select($read, $write, $except, NULL);
print ">\n";
if (count($read)) {
print "New data: ";
#socket_recv($socket, $data, 1024, NULL);
$data = socket_read($socket, 1024);
print $data."\n";
}
print "Socket status: ".socket_strerror(socket_last_error())."\n";
}
The above code simply connects to a server and prints what it reads. It's a cut-down version of what I have in the small socket library I'm writing.
For testing, I'm currently using ncat -vvklp 8081 to bind a socket and be a server. With that running, I can fire up the code above and it connects and works - eg, I can type in the ncat window, and PHP receives it. (Sending data from PHP is working too, but I've excluded that code as it's not relevant.)
However, the moment I ^C ncat, the code above enters a hard infinite loop - and PHP says there's no error on the socket.
I am trying to figure out where the button is that whacks PHP upside the head and makes it realize that the peer has disconnected.
socket_get_status() is a great misnomer - it's an alias for stream_get_meta_data(), and it doesn't actually work on sockets!
feof() similarly spouts Warning: feof(): supplied resource is not a valid stream resource.
I can't find a socket_* function for detecting peer EOF.
One of the PHP manual notes for socket_read() initially dissuaded me from using that function so I used socket_recv() instead, but I eventually tried it just in case - but no dice; switching the receive call has no effect.
I have discovered that watching the socket for writing and then attempting to write to it will suddenly make PHP go "oh, wait, right" and start returning Broken pipe - but I'm not interested in writing to the server, I want to read from it!
Finally, regarding the commented part - I would far prefer to use PHP's builtin stream functionality, but the stream_* functions do not provide any means for handling asynchronous connect events (which I want to do, as I'm making multiple connections). I can do stream_socket_client(... STREAM_CLIENT_ASYNC_CONNECT ...) but then cannot find out when the connection has been established (6yo PHP bug #52811).
Okay, I figure I might as well turn the comments above into an answer. All credit goes to Ryan Vincent for helping my thick head figure this out :)
socket_recv will return 0 specifically if the peer has disconnected, or FALSE if any other network error has occurred.
For reference, in C, recv()'s return value is the length of the new data you've just received (which can be 0), or -1 to indicate an error condition (the value of which can be found in errno).
Using 0 to indicate an error condition (and just one arbitrary type of error condition, at that) is not standard and unique to PHP in all the wrong ways. Other network libraries don't work this way.
You need to to handle it like this.
$r = socket_recv($socket, $buf, $len);
if ($r === FALSE) {
// Find out what just happened with socket_last_error()
// (there's a great list of error codes in the comments at
// http://php.net/socket_last_error - considering/researching
// the ramifications of each condition is recommended)
} elseif ($r === 0) {
// The peer closed the connection. You need to handle this
// condition and clean up.
} else {
// You DO have data at this point.
// While unlikely, it's possible the remote peer has
// sent you data of 0 length; remember to use strlen($buf).
}

PHP fputs "waits" until the end of the script

Currently I am trying to develop a PHP script used as a publicly available part of a client/server application. The php script should be used to authenticate users with a one-time token.
The other part of the application is a java program, which offers a telnet socket for other applications to connect to. Authentication is done through this telnet connection.
The java part is already working - but I still have a huge problem with the PHP part.
In php, I have opened a connection to the telnet port of the java program, which works so far. After the connection is initialized, the java program waits for input from the PHP script in order to authenticate the user.
After the authentication process has been finished, it returns a String to the PHP script (or any other program connected to its telnet server) which the PHP script should output.
Before I explain my problem, this is the part of the PHP script where the actual communication happens:
$tnconn = fsockopen("localhost", 53135, $errno, $errstr, 2);
if(!$tnconn) {
echo "SERVER_UNAVAILABLE";
die();
} else {
$data = $p_ip." ".$p_name." ".$p_token;
fputs($tnconn, $data);
while (true) {
if(($telnet_response = fgets($tnconn)) == false) {
break;
}
}
}
echo $telnet_response;
It seems like the fputs() statement is executed after the loop even tho it should happen before it starts - else the java application couldn't get the data that is passed to the php script, but it is needed to authenticate users.
Right after the data was received, the telnet server would output the String to indicate whether authentication was successful or not.
I tried temporarily removing the loop and the data was successfully passed with fputs() so I assume php waits until the whole script is finished and then executes the function.
How can I make it send the data before the loop?
Thank you in advance.
The issue is probably that you need to send a \n at the end of your data string so the telnet server knows you have sent a full sequence of data. Otherwise it is most likely sitting there waiting for more input.
Try:
$data = $p_ip." ".$p_name." ".$p_token . "\n";

PHP exec - echo output line by line during progress

I'm trying to find a way in which I can echo out the output of an exec call, and then flush that to the screen while the process is running. I have written a simple PHP script which accepts a file upload and then converts the file if it is not the appropriate file type using FFMPEG. I am doing this on a windows machine. Currently my command looks like so:
$cmd = "ffmpeg.exe -i ..\..\uploads\\".$filename." ..\..\uploads\\".$filename.".m4v 2>&1";
exec( $cmd, $output);
I need something like this:
while( $output ) {
print_r( $output);
ob_flush(); flush();
}
I've read about using ob_flush() and flush() to clear the output buffer, but I only get output once the process has completed. The command works perfectly, It just doesn't update the Page while converting. I'd like to have some output so the person knows what's going on.
I've set the time out
set_time_limit( 10 * 60 ); //5 minute time out
and would be very greatful if someone could put me in the right direction. I've looked at a number of solutions which come close one Stackoverflow, but none seem to have worked.
Since the exec call is a blocking call you have no way of using buffers to get status.
Instead you could redirect the output in the system call to a log file. Let the client query the server for progress update in which case the server could parse the last lines of the log file to get information about current progress and send it back to the client.
exec() is blocking call, and will NOT return control to PHP until the external program has terminated. That means you cannot do anything to dump the output on a line-by-line basis because PHP is suspended while the external app is running.
For what you want, you need to use proc_open, which returns a filehandle you can read from in a loop. e.g.
$fh = proc_open('.....');
while($line = fgets($fh)) {
print($line);
flush();
}
There are two problems with this approach:
The first is that, as #Marc B notes, the fact that exec will block until it's finished. You'll have to devise some way of measuring progress.
The second is that using ob_flush() in this way amounts to holding the connection between server & client open and dribbling the data out a little at a time. This is not something that the HTTP protocol was designed for and while it might work sometimes, it's not going to work consistently - different browsers and different servers will time out differently. The better way to do it is via AJAX calls: using Javascript's setTimeout() function (or setInterval()), make a call to the server periodically and have the server send back a progress report.

php script can be stopped?

I'm trying to make a paypal IPN system, this is a system of paypal to automatically check money transfers. They provide a basic system script to do it.
The system is easy, you get $_POST[] on your script, and then open a socket versus paypal, and they response to you valid or invalid word in the socket.
My problem is that opening the socket, 50% of times i'm getting connection lost. When the script connect, I don't have any problem. So I changed it to 20 trys, instead 1:
<?
//...
mail("mi#mail.com", "subject", "executing", "some headers"); //mailme when this is execute
$try = 20;
do{
$fp = #fsockopen ('ssl://www.paypal.com', 443, $errno, $errstr, 15);
$try--;
}while($try>0 && !$fp);
if (!$fp) { // HTTP ERROR
mail("mi#mail.com", "subject", "error_message_not_connecting", "some headers");
} else {
mail("mi#mail.com", "subject", "connected_reading_socket", "some headers");
//fputs(..); and the loop reading working.
}
?>
In my test, it works now 100% of severals trys. But in real transfers, it doesn't work 20-30% of times. I'm getting the 1st mail, but never the second one in that fails.
I'm thinking.. If paypal only open the connection to my server 1 second, can the php script stop after some trys, and stop going on? or any idea what is wrong here?
Sending the mail can fail too, especially if you have network issues. You should log the failure conditions, for both mail() as well as your fsockopen, so you can revisit them afterwards.
Also, your fsockopen can get stuck. You have a 15 second timeout and you try 20 times, so your script will work for 20*15=300 seconds = 5 minutes, which is probably longer than your PHP script timeout -> PHP would abort your script mid-process, right? Max execution time is only 30 seconds by default in PHP.
A PHP script can be stopped with exit;.
You can pause the php script proccessing with sleep(nr_sec).
I used to get similar problems. Strange behavior when usin sockets.
Better use CURL instead, it's more stable.
http://leepeng.blogspot.com/2006/04/standard-paypal-php-integration.html
I found the error. A php can be stopped when a users close the conection to the server (usually by click stop button on browser, or in this case a socket closed by paypal).
There are 3 ways to stop a script.
1-by finish the script
2-by user closeing the conection to the server
3-by timeout
I used the function ignore_user_abort(true), and I dont have more problems.
http://php.net/manual/en/function.ignore-user-abort.php

Writing IRC Services for UnrealIRCD

Well, I've been searching for material for this one, and failed to find anything. I am familiar with the IRC protocol, and I want to write IRC Services for my server (running on UnrealIRCD)
I prefer PHP, as I am most proficient there. Can anyone point me at some good tutorials or even explain here how to start? (I only need the basic protocol/syntax and server settings, I'll do the actual functions and abilities myself (hopefully :P)).
Thanks in advance.
A friend of mine me did this one already for InspIRCd. It never was finished, but the basics were working. We simply connected to the IRC server via a Socket-Connection (fsockopen in the first version, socket_create in the second one).
For communication we implemented an interface to InspIRCd with contained the specific lines as stated in the protocol. These methods could then be called by the core.
Reading uncompiled C source code of the Anope services got me pointed in the right direction, although it's a bit of a trial and error as to where to look inside it. I can't remember since it was quite a while ago when I tried my own PHP services, but if each client the services connects requires a PING/PONG reply to keep the connection alive, then won't PHP kinda fall down? The threading isn't that great, Linux only as far as I can remember.
EDIT: Good Lord, I didn't realize how old this question was. That being said, someone might use it so...answer below.
I've done this in PHP so I can tell you it can be done (source code not released).
That being said, I'll throw a basic core at you here:
$socket = socket_create(AF_INET, SOCK_STREAM, 6);
// Prevent fast reconnects if server down but try to connect every ten seconds.
while ( !socket_connect($socket,ADDRESS,PORT) ) {
sleep(10);
}
// set non blocking to avoid 'waiting' for data
socket_set_nonblock($socket);
// INTENTIONAL INFINITE LOOP
while(1) {
$read = array($socket);
// check if there's anything to read.
$num_changed_sockets = #socket_select($read, null, null, 0, 1);
if ( $num_changed_sockets > '0' ) {
// process the socket data here
processSocket(socket_read($socket,10000,PHP_NORMAL_READ));
}
}
Hope this helps you get started!

Categories