I am writing a program that connects to a tcp server with stream_socket_client in php. My problem is that if I echo the contents of the $client object before I perform any additional fwrites, the page hangs. Currently it will work if I send all of my requests before calling stream_get_contents, but once I call stream_get_contents, its like the client no longer responds? I would be thankful for any help given.
**EDIT
This is the API I am using:
https://www.onlinenic.com/cp_english/template_api/download/ONLINENIC_API2.0.pdf
**EDIT
//see full code below
//------Client creation code, precho function
function getTESTClient($address, $port)
{
$client = stream_socket_client("$address:$port", $errno, $errorMessage);
if ($client === false) {
throw new UnexpectedValueException("Failed to connect: $errorMessage");
}
return $client;
}
function precho($s)
{
echo "<pre>";
echo $s;
echo "</pre>";
}
//-------Problem code
//$loginRequest, $domainAvailableRequest, and $logoutRequest - see full code below
//-----------------------------------------
$address = "www.somesite.com";//This is where I would fill in the address
$port = "12345";//This is where I would fill in the port
$client = getTESTClient($address, $port);
//-----------------------------------------
fwrite($client, $loginRequest);
precho(htmlspecialchars(stream_get_contents($client)));
fwrite($client, $domainAvailableRequest);
precho(htmlspecialchars(stream_get_contents($client)));
fwrite($client, $logoutRequest);
precho(htmlspecialchars(stream_get_contents($client)));
fclose($client);
//-------Alternative working code, if I read all responses at once...
//*But I want to be able to read each response individually...
fwrite($client, $loginRequest);
fwrite($client, $domainAvailableRequest);
fwrite($client, $logoutRequest);
precho(htmlspecialchars(stream_get_contents($client)));
fclose($client);
Full Code:
<?php
function getDomainType($ext)
{
$domaintypes = ['com'=>0, 'net'=>0, 'org'=>807, 'biz'=>800, 'info'=>805, 'us'=>806, 'in'=>808,
'mobi'=>903, 'eu'=>902, 'asia'=>905, 'me'=>906, 'name'=>804, 'tel'=>907, 'cc'=>[600,610], 'tv'=>400,
'tw'=>302, 'uk'=>901, 'co'=>908, 'xxx'=>930, 'pw'=>940, 'club'=>740, 'bike'=>2001, 'clothing'=>2002,
'guru'=>2003, 'holdings'=>2004, 'plumbing'=>2005, 'singles'=>2006, 'ventures'=>2007, 'camera'=>2008,
'equipment'=>2009, 'estate'=>2010, 'gallery'=>2011, 'graphics'=>2012, 'lighting'=>2013, 'photography'=>2014,
'construction'=>2015, 'contractors'=>2016, 'site'=>950, 'online'=>951, 'sex'=>936,
'directory'=>2017, 'kitchen'=>2018, 'land'=>2019, 'technology'=>2020, 'today'=>2021, 'diamonds'=>2022,
'enterprises'=>2023, 'tips'=>2024, 'voyage'=>2025, 'careers'=>2026, 'photos'=>2027,
'recipes'=>2028, 'shoes'=>2029, 'cab'=>2030, 'company'=>2031, 'domains'=>2032, 'limo'=>2033,
'academy'=>2034, 'center'=>2035, 'computer'=>2036, 'management'=>2037, 'systems'=>2038,
'builders'=>2039, 'email'=>2040, 'solutions'=>2041, 'support'=>2042, 'training'=>2043, 'camp'=>2044,
'education'=>2045, 'glass'=>2046, 'institute'=>2047, 'repair'=>2048, 'coffee'=>2049, 'florist'=>2050,
'house'=>2051, 'international'=>2052, 'solar'=>2053, 'marketing'=>2054, 'viajes'=>2055, 'farm'=>2056,
'codes'=>2057, 'cheap'=>2058, 'zone'=>2059, 'agency'=>2060, 'bargains'=>2061, 'boutique'=>2062,
'cool'=>2063, 'watch'=>2064, 'works'=>2065, 'expert'=>2066, 'foundation'=>2067, 'exposed'=>2068,
'villas'=>2069, 'flights'=>2070, 'rentals'=>2071, 'cruises'=>2072, 'vacations'=>2073, 'condos'=>2074,
'properties'=>2075, 'maison'=>2076, 'tienda'=>2077, 'dating'=>2078, 'events'=>2079, 'partners'=>2080,
'productions'=>2081, 'community'=>2082, 'catering'=>2083, 'cards'=>2084, 'cleaning'=>2085, 'tools'=>2086,
'industries'=>2087, 'parts'=>2088, 'supplies'=>2089, 'supply'=>2090, 'report'=>2091, 'vision'=>2092,
'fish'=>2093, 'services'=>2094, 'capital'=>2095, 'engineering'=>2096, 'exchange'=>2097, 'gripe'=>2098,
'associates'=>2099, 'lease'=>2100, 'media'=>2101, 'pictures'=>2102, 'reisen'=>2103, 'toys'=>2104,
'university'=>2105, 'town'=>2106, 'wtf'=>2107, 'fail'=>2108, 'financial'=>2109, 'limited'=>2110,
'care'=>2111, 'clinic'=>2112, 'surgery'=>2113, 'dental'=>2114, 'tax'=>2115, 'cash'=>2116,
'fund'=>2117, 'investments'=>2118, 'furniture'=>2119, 'discount'=>2120, 'fitness'=>2121, 'schule'=>2122,
'sexy'=>2500, 'tattoo'=>2501, 'link'=>2502, 'guitars'=>2503, 'gift'=>2504, 'pics'=>2505, 'photo'=>2506,
'christmas'=>2507, 'blackfriday'=>2508, 'hiphop'=>2509, 'juegos'=>2510, 'audio'=>2511, 'click'=>2512,
'hosting'=>2513, 'property'=>2514, 'top'=>770, 'porn'=>932, 'adult'=>934, 'city'=>2129, 'ceo'=>742];
if(isset($domaintypes[strtolower($ext)]))
{
return $domaintypes[strtolower($ext)];
}
return "unknown";
}
function getTCPClient()
{
}
function getTESTClient($address, $port)
{
$client = stream_socket_client("$address:$port", $errno, $errorMessage);
if ($client === false) {
throw new UnexpectedValueException("Failed to connect: $errorMessage");
}
return $client;
}
function uniqueInvoiceID()
{
$legend = "0123456789";
$len = 22;
$result = "";
for($i=0;$i<$len;$i++)
{
$result .= $legend[rand(0,9)];
}
return "client".$result;
}
function precho($s)
{
echo "<pre>";
echo $s;
echo "</pre>";
}
$clid = "135610";//OnlineNic's test account username
$clpass = "654123";//OnlineNic's test account password
$cltrid = uniqueInvoiceID();//Client Record ID/Invoice
echo $cltrid."<br><br>";
//md5(clid + md5(clpass) + cltrid + “login”)
$checkSumLogin = md5($clid.md5($clpass).$cltrid."login");//Checksum
$checkSumLogout = md5($clid.md5($clpass).$cltrid."logout");//Checksum
$loginRequest = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>
<request>
<category>client</category>
<action>Login</action>
<params>
<param name=\"clid\">$clid</param>
</params>
<cltrid>$cltrid</cltrid>
<chksum>$checkSumLogin</chksum>
</request>";
$logoutRequest = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>
<request>
<category>client</category>
<action>Logout</action>
<params>
<param name=\"clid\">$clid</param>
</params>
<cltrid>$cltrid</cltrid>
<chksum>$checkSumLogout</chksum>
</request>";
$checkDomain = "somedomain.com";
$ext = "com";
$dType = getDomainType($ext);
$requestID = uniqueInvoiceID();
$domainAvailChecksum = md5($clid.md5($clpass).$requestID."checkdomain".$dType.$checkDomain);
$domainAvailableRequest = "<?xml version=\"1.0\"?>
<request>
<category>domain</category>
<action>CheckDomain</action>
<params>
<param name=\"domaintype\">$dType</param>
<param name=\"domain\">$checkDomain</param>
</params>
<cltrid>$requestID</cltrid>
<chksum>$domainAvailChecksum</chksum>
</request>";
$client = getTESTClient("tcp://ote.onlinenic.com", "30009");
fwrite($client, $loginRequest);
precho(htmlspecialchars(stream_get_contents($client)));
fwrite($client, $domainAvailableRequest);
precho(htmlspecialchars(stream_get_contents($client)));
fwrite($client, $logoutRequest);
precho(htmlspecialchars(stream_get_contents($client)));
fclose($client);
?>
The second parameter to stream_get_contents is $maxlength, the maximum number of bytes to read. When not set (or the default of -1 is passed), the entire stream is read, up to the end of the stream.
This means that stream_get_contents will continue to read all the data from the stream until there is no more, when the stream is closed by the other end. (stream_get_contents does not know the details of your protocol, so it can not know that it is "supposed" to stop reading after "one response" from the server; it doesn't have any concept of what that even means.)
This means, if you have a query-response protocol on your TCP connection, you will have to either stream_get_contents() exactly the length of data you want to receive (if you know exactly what the response size will be), or you will have to read the data from the server and process it it yourself.
How exactly you do that is going to depend greatly on your protocol. If it is line-oriented, you can use fgets($client) to retrieve one line at a time. If it is a binary protocol, you can use stream_get_contents($client, $sizeOfYourPacket).
Since you have an XML-based protocol, you will probably want to create a new function that handles reading responses and that understands XML, so that it knows how much data to read and when it can send the response (possibly already processed) back to your application.
Without actually having credentials, it's a little hard to see how exactly the server behaves, so beyond this, you're going to have to experiment.
I want to authenticate to another site using HTTP Digest authorization in PHP script.
My function has as parameter just content of the WWW-Authenticate header and I want to generate correct response (Authorization header). I have found many examples that explain how to implement this the other way (browser authenticate to my script) but not this way. I am missing function that is able to parse WWW-Authenticate header content a generate response. Is there some standard function or common library that implements this?
Ok, no answer yet, I have investigated python implementation that lied around here and rewrite it to PHP. It is the simplest possible piece of code. Supports only md5 hashing, but works for me:
function H($param) {
return md5($param);
}
function KD($a,$b) {
return H("$a:$b");
}
function parseHttpDigest($digest) {
$data = array();
$parts = explode(", ", $digest);
foreach ($parts as $element) {
$bits = explode("=", $element);
$data[$bits[0]] = str_replace('"','', $bits[1]);
}
return $data;
}
function response($wwwauth, $user, $pass, $httpmethod, $uri) {
list($dummy_digest, $value) = split(' ', $wwwauth, 2);
$x = parseHttpDigest($value);
$realm = $x['realm'];
$A1 = $user.":".$realm.":".$pass;
$A2 = $httpmethod.":".$uri;
if ($x['qop'] == 'auth') {
$cnonce = time();
$ncvalue = 1;
$noncebit = $x['nonce'].":".$ncvalue.":".$cnonce.":auth:".H($A2);
$respdig = KD(H($A1), $noncebit);
}else {
# FIX: handle error here
}
$base = 'Digest username="'.$user.'", realm="';
$base .= $x['realm'].'", nonce="'.$x['nonce'].'",';
$base .= ' uri="'.$uri.'", cnonce="'.$cnonce;
$base .= '", nc="'.$ncvalue.'", response="'.$respdig.'", qop="auth"';
return $base;
}
Usage:
# TEST
$www_header = 'Digest realm="TEST", nonce="356f2dbb8ce08174009d53c6f02c401f", algorithm="MD5", qop="auth"';
print response($www_header, "user", "password", "POST", "/my_url_query");
Don't know of a ready-made client-side implementation in PHP; you have to implement the RFC as if your script were the browser, authenticating to a remote server. Wikipedia's page on HTTP Digest has a nice example.
(it's not that hard - a couple of MD5 hashes. Some gotchas I encontered when building the server-side: string delimiter is ":" (colon), request method is also a part of the hash)