unable to call ssh2_connect() callbacks - php

I am using the ssh2_connect() method to establish a connection with a remote server. A connection is establishing correctly if I am providing the right hostname and port number. When unable to connect due to wrong credentials, I am trying to call a callback-function but the way I try it isn't calling the callback after connection failure.
This i the code I tried:
$callbacks = array(
'ignore' => array($this, 'callbackSshDisconnect'),
'debug' => array($this, 'callbackSshDisconnect'),
'macerror' => array($this, 'callbackSshDisconnect'),
'disconnect' => array($this, 'callbackSshDisconnect'),
);
ssh2_connect($hostName,$port,array('hostkey', 'ssh-rsa'),$callbacks);
public function callbackSshDisconnect($reason, $message, $language) {
$this->log('disconnected');
$this->log($reason);die;
}
What I am doing wrong?

When ssh2_connect fails because of wrong host, port, and so on, it doesn't call any callbacks.
What is does, instead, is returning false
PHP documentation says :
Returns a resource on success, or FALSE on error.
$result = ssh2_connect($hostName,$port,array('hostkey' => 'ssh-rsa'),$callbacks);
if ($result === false)
exit("ssh2_connect failed");
Also, reading the doc, your
array('hostkey', 'ssh-rsa')
should be
array('hostkey' => 'ssh-rsa')

There's nothing you are doing wrong apart from the typo: array('hostkey', 'ssh-rsa') should be array('hostkey' => 'ssh-rsa'). The function ssh_connect() just returns false for a connection failure; callbacks simply are not initiated yet when the wrong credentials are being used.
There are solutions (e.g. as suggested by rubo77 below) but the one I find will give you most control, and allow you to do what you want (e.g. tracking MAC errors), is to use the phpseclib library (http://phpseclib.sourceforge.net/ssh/intro.html) for ssh connections and control. It gives very fine control of commands, and also includes logging.
It's not the simplest solution, but you can control as finely as if you are at the keyboard/terminal directly.
You have control over timeouts and can use callback on commands. But if you want better control, use read() and write() and you can monitor for disconnects or other issues. Check the documentation on logging with phpseclib: you can either log and parse the log, or call getLastError().
Log files will show 'Connection closed by server' on disconnects for example, but will also tell you if you're using an unsupported authentication mode on login etc.
Or for more, read the code: here, for example, are the disconnect reasons:
$this->disconnect_reasons = array(
1 => 'NET_SSH2_DISCONNECT_HOST_NOT_ALLOWED_TO_CONNECT',
2 => 'NET_SSH2_DISCONNECT_PROTOCOL_ERROR',
3 => 'NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED',
4 => 'NET_SSH2_DISCONNECT_RESERVED',
5 => 'NET_SSH2_DISCONNECT_MAC_ERROR',
6 => 'NET_SSH2_DISCONNECT_COMPRESSION_ERROR',
7 => 'NET_SSH2_DISCONNECT_SERVICE_NOT_AVAILABLE',
8 => 'NET_SSH2_DISCONNECT_PROTOCOL_VERSION_NOT_SUPPORTED',
9 => 'NET_SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE',
10 => 'NET_SSH2_DISCONNECT_CONNECTION_LOST',
11 => 'NET_SSH2_DISCONNECT_BY_APPLICATION',
12 => 'NET_SSH2_DISCONNECT_TOO_MANY_CONNECTIONS',
13 => 'NET_SSH2_DISCONNECT_AUTH_CANCELLED_BY_USER',
14 => 'NET_SSH2_DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE',
15 => 'NET_SSH2_DISCONNECT_ILLEGAL_USER_NAME'
);

Even if you would use working code, like this:
$callbacks = array(
'ignore' => 'callback',
'debug' => 'callback',
'macerror' => 'callback',
'disconnect' =>'callback',
);
$session = ssh2_connect($hostName, $port, array('hostkey' => 'ssh-rsa'), $callbacks);
if($session === false) {
print('Connection failed' . PHP_EOL);
exit(1);
}
if(!ssh2_auth_pubkey_file(
$session,
"user",
'/home/user/.ssh/id_rsa.pub',
'/home/user/.ssh/id_rsa'
)) {
print('Failed to authenticate session' . PHP_EOL);
exit(2);
}
// Check this great answer:
// http://stackoverflow.com/a/12094837/171318
$stream = ssh2_exec($session, "ls -al");
stream_set_blocking($stream, true);
$stream = ssh2_fetch_stream($stream, SSH2_STREAM_STDIO);
echo stream_get_contents($stream);
function callback($reason, $message, $language) {
print("callback:" . PHP_EOL);
print(" " . $message . PHP_EOL);
print(" " . $reason . PHP_EOL);
}
the callbacks won't get called in a usual application. This is because they were called just under certain circumstances named in the manual page
The disconnect callback for example get's only called if a SSH2_MSG_DISCONNECT packet is received. Such a packet is not received unless the server closes the connection which will usually not happen.

When you are unable to connect due to wrong credentials then you won't get any callbacks anyway, ssh2_connect() just returns false in this case.
If you really want to catch the reason of the failure, you can use output buffer handler to extract the error message (assuming, you have error reporting set on):
set_error_handler(function($errno, $errstr, $errfile, $errline, array $errcontext) {
list($command,$errormessage)=explode(":",$errstr,2);
echo "Error ($errno):", $errormessage."<br><br>";
});
$connection = ssh2_connect($hostName,$port);
restore_error_handler();

Related

aws php sdk - function works on local host and in console, but not on browser when on server

I'm having an issue where my php function works when I call it from the server's command line, but not on the web page. It also works on the web page when I have it hosted locally through WAMP.
Any ideas what might be the issue?
function getCFTemplateSummary($CFUrl){
//init client
$client = new CloudFormationClient(array(
'profile' => 'default',
'region' => 'us-east-1',
'version' => '2010-05-15',
'http' => [ 'verify' => false ]
));
try {
$result = $client->getTemplateSummary(['TemplateURL' => $CFUrl]);
}catch(\Aws\CloudFormation\Exception\CloudFormationException $e){
echo $e->getMessage();
}catch(\Aws\Exception\AwsException $e){
echo $e->getMessage();
}
echo "<pre>";
print_r($result);
echo "inside the function";
echo "</pre>";
return $result;
}
I was able to trace down the issue to this by trial and error commenting ...but not sure how to proceed debugging as I don't see any error messages.
$result = $client->getTemplateSummary(['TemplateURL' => $CFUrl]);
Try catching Exception instead of \Aws\CloudFormation\CloudFormationException, it's possible that a different Exception (e.g., incorrectly configured AWS credentials) is being thrown from your server that you aren't explicitly catching in your current code.

PHP ZF2 Mysql has gone away

Hi I have a php daemon that handle request from rabbitmq
After a day, it can no longer execute due to error MySQL has gone away.
PHP Warning: PDOStatement::execute(): MySQL server has gone away in /var/www/daemon/www/vendor/zendframework/zendframework/library/Zend/Db/Adapter/Driver/Pdo/Statement.php on line 239
PHP Warning: PDOStatement::execute(): Error reading result set\'s header in /var/www/daemon/www/vendor/zendframework/zendframework/library/Zend/Db/Adapter/Driver/Pdo/Statement.php on line 239
I didn't use doctrine, instead I send my \Zend\Db\Adapter\Adapter to a db wrapper class with below function.
public static function executeScalar($statement, $parameters, \Zend\Db\Adapter\Adapter $dbAdapter)
{
$dbResult = new DbResult();
if (! $statement) {
$dbResult->addError('No statement given');
return $dbResult;
}
$stmt = $dbAdapter->createStatement();
$stmt->prepare($statement);
foreach ($parameters as $key => &$param) {
$stmt->getResource()->bindParam($key + 1, $param[0], $param[1]);
}
try {
$result = $stmt->execute();
$dbResult->setResult($result);
} catch (\Zend\Db\Adapter\ExceptionInterface $e) {
$dbResult->addError('DB Error');
$message = $e->getPrevious() ? $e->getPrevious()->getMessage() : $e->getMessage();
$dbResult->addError($message);
} catch (\Zend\Db\Adapter\Exception $e) {
$dbResult->addError('DB Error');
$dbResult->addError($e->getMessage());
} catch (\PDOException $e) {
$dbResult->addError('DB Error');
$dbResult->addError($e->getMessage());
} catch (\Exception $e) {
$dbResult->addError('DB Error');
$dbResult->addError($e->getMessage());
}
$stmt->getResource()->closeCursor();
return $dbResult;
}
DbResult is my own db result wrapper class it mainly check whether it return empty, what's the error, how many rows, etc.
Here is my database.local.php configuration
return array(
'service_manager' => array(
'factories' => array(
'mysql' => function ($sm)
{
return new Zend\Db\Adapter\Adapter(array(
'driver' => 'PdoMysql',
'hostname' => 'localhost',
'database' => 'daemon',
'username' => 'daemon',
'password' => 'password',
'driver_options' => array(
\PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES \'UTF8\'',
\PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION,
\PDO::ATTR_EMULATE_PREPARES => true,
\PDO::MYSQL_ATTR_LOCAL_INFILE => true
)
));
},
)
)
)
So everytime I want to execute a sql I do this inside controller or any other class ( Just an example )
$service = $this->getServiceLocator();
$dbAdapter = $service->get('mysql');
$get = \Db\Database::executeScalar('SELECT * FROM mytable WHERE id <= ?', array(10), $dbAdapter);
It seems I cannot catch the warning, and is there a way to force reconnect or perhaps I just do a disconnect after each request ?
Will this works, to handle the error ?
On every new request I do this
$dbAdapter->getDriver()->getConnection()->connect();
At the end of request I do this
$dbAdapter->getDriver()->getConnection()->disconnect();
Yes, I check the persistent connection option, but I also not fond of it.
I find the problem, it cause by mysql server close idle connection after 'wait timeout'. when mysql closing the idle connection, PDO will not receive any event so the next time you initiate a query it will return Mysql has gone away error.
For http request this is acceptable since after the server response the request it will stop/exit php execution which close all connection to database.
For daemon/service this is not acceptable since most of the time it waiting for client request (idle). My solution is to close the connection everytime it finish handling client request. e.g :
while (true) {
//listen to rabbitmq queue
//...
//do something base on client request from rabbitmq queue
//...
//close the connection whether it use database or not
//connection will be reconnected when we call $service->get('mysql');
$service = $this->getServiceLocator();
$dbAdapter = $service->get('mysql');
$dbAdapter->getDriver()->getConnection()->disconnect();
}
You can create a persistent connection to your database but be warned that creating a persistent connection should not be the first solution to look for. Be sure to do some research on the subject before trying it. You can find some documentation here :
http://php.net/manual/en/pdo.connections.php#example-954
On the other hand, you should look for the queries sent so the reason of the gone away message is not caused by the mysql server recieving a packet too large (ex: inserting a large blob). Because if it is, the connection will still close unexpectedly.

PHP function shuts down on SoapClient::__doRequest connection timeout

I am attempting to write a background function that communicates with an external API to retrieve & update information on about 500,000 records on our server. This takes time to run. The problem I'm running into is this PHP fatal error from a function called inside a foreach loop, which shuts down the function entirely:
SoapClient::__doRequest(): connect() failed: Connection timed out in /path/to/function on line ABC
Here is a mock version of the function, with an indication of the line where the fatal error is being thrown, shutting down the function:
private function APIUpdateFunction($table,$record,$fields,$data) {
$soap = new SoapClient("https://www.endpoint.com/API/stuff.whatever");
$result = $soap->UpdateRecord(array(
"credentials" => API_CREDS,
"table" => $table,
"record" => $record,
"fields" => $fields,
"data" => $data
)); // THIS IS WHERE THE FATAL ERROR IS BEING THROWN
}
What I would like to do is create a workaround where IF the function shuts down as a result of this connection timeout, I would like to either:
A) Perform a new set of actions, such as restarting the function itself
or
B) Bypass the error entirely, and check in the next step for a response value. For example, if $result = NULL, the remaining items in this function would be bypassed and return a FALSE value.
What is the solution?
You can control the time your script takes to connect to the remote host/service and the time for how long the socket connection should wait for a response from the server.
The first option can be configured by specifying the timeout when you create the Soap Client:
$client = new SoapClient($wsdl, array("connection_timeout"=>15));
The second option can be configured using:
ini_set('default_socket_timeout', 180);
Now if you want to catch a timeout error, either during the host connection or the socket connection, you should catch a SoapFault exception, for example:
private function APIUpdateFunction($table,$record,$fields,$data)
{
try {
$soap = new SoapClient("https://www.endpoint.com/API/stuff.whatever");
$result = $soap->UpdateRecord(array(
"credentials" => API_CREDS,
"table" => $table,
"record" => $record,
"fields" => $fields,
"data" => $data
)); // THIS IS WHERE THE FATAL ERROR IS BEING THROWN
} catch (SoapFault $e) {
// There was an error with the Soap Client
// Do something here
}
}

AWS SQS Delete Messages Using Receipt Handle

I am trying to set up SQS and after receiving the message, I need to delete it from the queue.
Creating Client -
$client = Aws\Sqs\SqsClient::factory(array(
'key' => '******',
'secret' => '******',
'region' => 'ap-southeast-1'
));
Sending Message
public static function SendMessage()
{
if(!isset(self::$queueUrl))
self::getQueueUrl();
$command = "This is a command";
$commandstring = json_encode($command);
self::$client->sendMessage(array(
'QueueUrl' => self::$queueUrl,
'MessageBody' => $commandstring,
));
}
Receiving Message
public static function RecieveMessage()
{
if(!isset(self::$queueUrl))
self::getQueueUrl();
$result = self::$client->receiveMessage(array(
'QueueUrl' => self::$queueUrl,
));
// echo "Message Recieved >> ";
print_r($result);
foreach ($result->getPath('Messages/*/Body') as $messageBody) {
// Do something with the message
echo $messageBody;
//print_r(json_decode($messageBody));
}
foreach ($result->getPath('Messages/*/ReceiptHandle') as $ReceiptHandle) {
self::$client->deleteMessage(self::$queueUrl, $ReceiptHandle);
}
}
When I try to delete the message using the Receipt Handle in the receive message code, I get error from Guzzle -
Catchable fatal error: Argument 2 passed to Guzzle\Service\Client::getCommand() must be an array, string given,
Now after searching a lot for it, I was able to find similar questions which state that they were using wrong SDK version. I am still not able to narrow it down though. I am using the zip version of the latest sdk 2.6.15
Why don't you give this a try this:
self::$client->deleteMessage(array(
'QueueUrl' => self::$queueUrl,
'ReceiptHandle' => $ReceiptHandle,
));
The Basic formatting example in the API docs for SqsClient::deleteMessage() (and other operations) should help. All of the methods that execute operations take exactly one parameter, which is an associative array of the operation's parameters. You should read through the SDK's Getting Started Guide (if you haven't already), which talks about how to perform operations in general.

JAXL based chat client. Need help connecting to Gtalk or other server to test

I wish to send a message to XMPP based chat servers using php.
I am using JAXL, which seems the best (of limited) options for pure PHP server based chat.
However, I have yet to establish any connect, let alone send a message.
I am having a hard time working out if the problem is my code, my server (which is shared server, but has Cpanel, and a very helpfull host), or my settings.
The code I am using to try to connect to GTalk is;
$client = new JAXL(array(
'jid' => 'name#gmail.com',
'pass' => 'password',
'host'=> 'talk.google.com',
'port'=> 5222,
'domain'=> 'gmail.com', //unsure if this is the right setting.
'force_tls' => true,
'auth_type' => #$argv[3] ? $argv[3] : 'PLAIN',
));
//
// required XEP's
//
$client->require_xep(array(
'0199' // XMPP Ping
));
//
// add necessary event callbacks here
//
$client->add_cb('on_auth_success', function() {
global $client;
_info("got on_auth_success cb, jid ".$client->full_jid->to_string());
// fetch roster list
$client->get_roster();
// fetch vcard
$client->get_vcard();
// set status
$client->set_status("available!", "dnd", 10);
});
$client->add_cb('on_connect_error', function() {
echo 'Connect Error';
});
$client->add_cb('on_auth_failure', function() {
echo 'Auth Error';
});
$client->add_cb('on_auth_success', function() {
global $client;
echo 'connected';
$client->send_chat_msg('test2#domain.com', 'webtest');
$client->shutdown();
});
//
// finally start configured xmpp stream
//
$client->start(array(
'--with-debug-shell' => true,
'--with-unix-sock' => true
));
echo "done\n";
Triggering the php (from a browser) then results in the server getting stuck. (no "done" message, just constant loading till a timeout from the browser)
The server logs show;
strict mode enabled, adding exception handlers. Set 'strict'=>TRUE inside JAXL config to disable this[0m
error handler called with 8, Undefined index: priv_dir,
And then lots of;
unable to connect tcp://talk.google.com:5222 with error no: 110, error str: Connection timed out
So I would appreciate help with any of the following;
Any specific problems with my code
Any issues with the gtalk connection settings at the start
Alternative recommendations to investigate this issue.
Or any general advice from people that have successfully used JAXL.
Thanks,
Thomas Wrobel
Ok problem might be TCP port is closed for your hosting , try to open it with your hosting first, also try to run your code locally to see if it's work fine .
Some people reported the issue was fixed by override method connect from file XMLStream.php by this one
/**
* Connect to XMPP Host
*
* #param integer $timeout
* #param boolean $persistent
* #param boolean $sendinit
*/
public function connect($timeout = 30, $persistent = false, $sendinit = true) {
$this->sent_disconnect = false;
$starttime = time();
do {
$this->disconnected = false;
$this->sent_disconnect = false;
if($persistent) {
$conflag = STREAM_CLIENT_CONNECT | STREAM_CLIENT_PERSISTENT;
} else {
$conflag = STREAM_CLIENT_CONNECT;
}
$conntype = 'tcp';
if($this->use_ssl) $conntype = 'ssl';
$this->log->log("Connecting to $conntype://{$this->host}:{$this->port}");
try {
$this->socket = #stream_socket_client("$conntype://{$this->host}:{$this->port}", $errno, $errstr, $timeout, $conflag);
} catch (Exception $e) {
throw new XMPPHP_Exception($e->getMessage());
}
if(!$this->socket) {
$this->log->log("Could not connect.", XMPPHP_Log::LEVEL_ERROR);
$this->disconnected = true;
# Take it easy for a few seconds
sleep(min($timeout, 5));
}
} while (!$this->socket/* && (time() - $starttime) < $timeout*/);
if ($this->socket) {
stream_set_blocking($this->socket, 0);
if($sendinit) $this->send($this->stream_start);
} else {
throw new XMPPHP_Exception("Could not connect before timeout.");
}
}

Categories