PHP XML API service
 
_Dejan_ 1. sep 2011 11:51:34 Pridružen od:
16. jun 2010
321 objav
227 23 3
#1

Pozdravljeni,

Potreboval bi malo pomoči oz. predlogov glede izdelave custom API-ja ki komunicira z mysql bazo in se nahaja na cpanel shared hostingu...

Na ta API se bodo povezovale custom embedded naprave:

72 MHz 32-bit ARM 7 Processor

16MB RAM and 4.5MB FLASH

GPRS/3G Modem



Ker nisem nikoli ničesar programiral v php-ju bi vas prosil če preverite primer kode in poveste kaj ni ok oz. kaj bi vi spremenili...

XML ki bi ga naprava poslala za prijavo v sistem bi bil:


<?xml version='1.0' encoding="iso-8859-1" ?>
<packet>
<mac_address>00:00:00:00:00:00</mac_address>
<serial>000000</serial>
<hostname>testdevice</hostname>
<model>devmodel</model>
<sw_version>1.00.0000</sw_version>
<hw_version>1.00</hw_version>
</packet>

Koda login skripte pa bi izgledala takole:


<?php
include('config.php');

function getClientIPAddress()
{
if (!empty($_SERVER['HTTP_CLIENT_IP']))
{
$ip=$_SERVER['HTTP_CLIENT_IP'];
}
elseif (!empty($_SERVER['HTTP_X_FORWARDED_FOR']))
{
$ip=$_SERVER['HTTP_X_FORWARDED_FOR'];
}
else
{
$ip=$_SERVER['REMOTE_ADDR'];
}
return $ip;
}

$db = mysql_connect($dbhost, $dbuser, $dbpasswd);
mysql_select_db ($dbname) or die ("Cannot connect to database");
mysql_query("SET NAMES 'utf8'");

libxml_use_internal_errors(true);
$request_xml = new SimpleXMLElement($HTTP_RAW_POST_DATA);
if (!$request_xml)
{
echo "Failed loading XML\n";
foreach(libxml_get_errors() as $error)
{
echo "\t", $error->message;
}
}
else
{
$sql = "SELECT id, active, REPLACE(UUID(),'-', '') AS session FROM devices WHERE mac_address='" . $request_xml->mac_address . "' AND serial='" . $request_xml->serial . "' AND hostname='" . $request_xml->hostname . "'";
$result = mysql_query($sql);
$device = mysql_fetch_object($result);

$response_xml = new SimpleXMLElement("<?xml version='1.0' encoding=\"iso-8859-1\" ?><packet></packet>");
$response_xml->addChild('device_id', $device->id);
$response_xml->addChild('active', $device->active);
$response_xml->addChild('session', $device->session);

if(is_null($device->id))
{
header('Content-Type: text/xml');
echo $response_xml->asXML();
}
else
{
if($device->active = 1)
{
mysql_query("UPDATE devices SET status='01', dt_last_online=Now(), session='" . $device->session . "', sw_version='" . $request_xml->sw_version . "', hw_version='" . $request_xml->hw_version . "', ip_address='" . getClientIPAddress() . "' WHERE id=" . $device->id . ";");
mysql_query("INSERT INTO events (dt, device_id, event, response, description) VALUES (Now(), " . $device->id . ", '0001', '01', '" . $device->session . "');");
header('Content-Type: text/xml');
echo $response_xml->asXML();
}
else
{
mysql_query("UPDATE devices SET status='02', dt_last_online=Now(), session='', sw_version='" . $request_xml->sw_version . "', hw_version='" . $request_xml->hw_version . "', ip_address='" . getClientIPAddress() . "' WHERE id=" . $device->id . ";");
mysql_query("INSERT INTO events (dt, device_id, event, response, description) VALUES (Now(), " . $device->id . ", '0001', '02', '');");
header('Content-Type: text/xml');
echo $response_xml->asXML();
}
}
}
mysql_close($db);
?>

Response bo XML dokument ki izgleda takole:


<?xml version='1.0' encoding="iso-8859-1" ?>
<packet>
<device_id>25638</device_id>
<status>1</status>
<session>c4cb465cd3a211e087f6006438a934f5</session>
</packet>

Torej kaj bi vi spremenili in zakaj? Kje sem ga lomil? Za kakršnokoli pomoč vam bom hvaležen.


všeč(0) ni všeč(0) spam(0)
 
_Dejan_ 2. sep 2011 05:54:37 Pridružen od:
16. jun 2010
321 objav
227 23 3
#2

hm ali nihče ne zna pomagati svetovati kako naj se lotim zadeve oz. ali je tak način ok?

Ker ne morem urejati prvega posta bom dodal kodo v kateri sem v večini primerov "preprečil" SQL Injection me pa zanima ali je smiselno dodati še stripslashes() preden kličem mysqlrealescape_string() ???

Nova koda:


<?php
include('config.php');

error_reporting(0);
libxml_use_internal_errors(true);
libxml_clear_errors();

function getClientIPAddress()
{
if (!empty($_SERVER['HTTP_CLIENT_IP']))
{
$ip=$_SERVER['HTTP_CLIENT_IP'];
}
elseif (!empty($_SERVER['HTTP_X_FORWARDED_FOR']))
{
$ip=$_SERVER['HTTP_X_FORWARDED_FOR'];
}
else
{
$ip=$_SERVER['REMOTE_ADDR'];
}
return $ip;
}

$db = mysql_connect($dbhost, $dbuser, $dbpasswd);
mysql_select_db ($dbname) or die ("Cannot connect to database");
mysql_query("SET NAMES 'utf8'");

try {
$request_xml = new SimpleXMLElement($HTTP_RAW_POST_DATA);
} catch (Exception $e) {}

if (!$request_xml)
{
$response_xml = new SimpleXMLElement("<?xml version='1.0' encoding=\"iso-8859-1\" ?><error></error>");
foreach(libxml_get_errors() as $error)
{
$response_xml->addChild('message', trim($error->message));
}
libxml_clear_errors();
header('Content-Type: text/xml');
echo $response_xml->asXML();
}
else
{
$mac_address = mysql_real_escape_string($request_xml->mac_address);
$serial = mysql_real_escape_string($request_xml->serial);
$hostname = mysql_real_escape_string($request_xml->hostname);
$sw_version = mysql_real_escape_string($request_xml->sw_version);
$hw_version = mysql_real_escape_string($request_xml->hw_version);

$sql = "SELECT id, active, REPLACE(UUID(),'-', '') AS session FROM devices WHERE mac_address='" . $mac_address . "' AND serial='" . $serial . "' AND hostname='" . $hostname . "'";
$result = mysql_query($sql);
$device = mysql_fetch_object($result);

$response_xml = new SimpleXMLElement("<?xml version='1.0' encoding=\"iso-8859-1\" ?><packet></packet>");
$response_xml->addChild('device_id', $device->id);
$response_xml->addChild('active', $device->active);
$response_xml->addChild('session', $device->session);

if(is_null($device->id))
{
header('Content-Type: text/xml');
echo $response_xml->asXML();
}
else
{
if($device->active = 1)
{
mysql_query("UPDATE devices SET status='01', dt_last_online=Now(), session='" . $device->session . "', sw_version='" . $sw_version . "', hw_version='" . hw_version . "', ip_address='" . getClientIPAddress() . "' WHERE id=" . $device->id . ";");
mysql_query("INSERT INTO events (dt, device_id, event, response, description) VALUES (Now(), " . $device->id . ", '0001', '01', '" . $device->session . "');");
header('Content-Type: text/xml');
echo $response_xml->asXML();
}
else
{
mysql_query("UPDATE devices SET status='02', dt_last_online=Now(), session='', sw_version='" . $sw_version . "', hw_version='" . hw_version . "', ip_address='" . getClientIPAddress() . "' WHERE id=" . $device->id . ";");
mysql_query("INSERT INTO events (dt, device_id, event, response, description) VALUES (Now(), " . $device->id . ", '0001', '02', '');");
header('Content-Type: text/xml');
echo $response_xml->asXML();
}
}
}
mysql_close($db);
?>
všeč(0) ni všeč(0) spam(0)
 
Roky 2. sep 2011 06:12:20 Pridružen od:
9. apr 2008
2506 objav
3240 335 107
#3

Ne razumem sploh kje je težava. Iščeš koncept ... ali ti dejansko kaj ne dela?


všeč(0) ni všeč(0) spam(0)
rok.meglic@gmail.com - 031 492 148
 
_Dejan_ 2. sep 2011 06:24:42 Pridružen od:
16. jun 2010
321 objav
227 23 3
#4

Roky:

Ne razumem sploh kje je težava. Iščeš koncept ... ali ti dejansko kaj ne dela?




V bistvu zadeva dela, zanima me predvsem če sem glede security-a kje mimo vsekal in je skripta potencialno nevarna, oz. če sem kje kaj spregledal in bo zadeva požirala preveč resoursev. Z 1 clientom to ne morem testirat, ko pa bo laufalo na produkcijskem strežniku in gor 100-200 naprav pa je tudi težko narediti kakšne večje spremembe :)


všeč(0) ni všeč(0) spam(0)
 
Roky 2. sep 2011 07:16:09 Pridružen od:
9. apr 2008
2506 objav
3240 335 107
#5

#1 Validacija

Naredi dodatno validacijo, v smilu intval tam kjer veš da je integer, regex kjer veš da je mac address itd.


$mac_address = mysql_real_escape_string($request_xml->mac_address);
$serial = mysql_real_escape_string($request_xml->serial);
$hostname = mysql_real_escape_string($request_xml->hostname);
$sw_version = mysql_real_escape_string($request_xml->sw_version);
$hw_version = mysql_real_escape_string($request_xml->hw_version);

#2 Pri prvem blogu kar dobiš noter HTTP RAW POST XML. Kako pa veš, da ni kakšen čudn xml? Raje ga prej preveri


validateXMLFile($xmlString) {
libxml_use_internal_errors(true);

$doc = new DOMDocument();
$doc->loadXML($xmlString);

$errors = libxml_get_errors();
if (empty($errors)) {
return true;
}

$error = $errors[0];
if ($error->level < 3) {
return true;
}

$errorsMsg = '';
foreach ($errors as $error) {
$errorsMsg .= trim($error->message) . "\n";
}

return $errorsMsg;
}

if ($validationStatus !== true) {
echo 'Napaka z XML-jem, ni validiran';
exit; // al karkol
}

Ostalo je kul.


všeč(2) ni všeč(0) spam(0)
rok.meglic@gmail.com - 031 492 148
 
blackmamba 2. sep 2011 07:30:59 Pridružen od:
4. mar 2008
559 objav
392 25 11
#6

Lahko bi uvedel kak error reporting sistem, vsaj za beleženje mysql napak.



Pri "UPDATE DEVICES" manjka $ pred hw_version


hw_version='" . hw_version . "'

Poglej si še mysqlfreeresult.



Kako pogosto naprave pošiljajo requeste, boš vedel ti. Če vsaka naprava naredi request 1x na dan ali 1x na sekundo je razlika več kot občutna :)



Če te skrbi, pripravi testno okolje in simuliraj 500 naprav. (mogoče ab)



edit: in tako kot pravi Roky: dodatna validacija regex in intval()!



LP


všeč(3) ni všeč(0) spam(0)
 
_Dejan_ 2. sep 2011 08:43:23 Pridružen od:
16. jun 2010
321 objav
227 23 3
#7

Hvala obema za odgovore.

1.)Kar se tiče validacije sem v kodo dodal funkcije za validacijo:


function validate_macaddress($val)
{
if(preg_match('/^([0-9a-fA-F][0-9a-fA-F]:){5}([0-9a-fA-F][0-9a-fA-F])$/', $val))
{
return $val;
}
else
{
return "00:00:00:00:00:00"
}
}

function validate_serial($val)
{
if(preg_match('/^[0-9]{6}$/', $val))
{
return $val;
}
else
{
return "000000"
}
}

function validate_hostname($val)
{
if(preg_match('/(?=^.{1,254}$)(^(?:(?!\d+\.|-)[a-zA-Z0-9_\-]{1,63}(?<!-)\.?)+(?:[a-zA-Z]{2,})$)/', $val))
{
return $val;
}
else
{
return "default"
}
}

function validate_sw_version($val)
{
if(preg_match('/^(\d+)((\.{1}\d+)*)(\.{0})$/', $val))
{
return $val;
}
else
{
return "0.00.0000"
}
}

function validate_hw_version($val)
{
if(preg_match('/^(\d+)((\.{1}\d+)*)(\.{0})$/', $val))
{
return $val;
}
else
{
return "0.00"
}
}

In nato spremenil validacijo spremeljivk:


$mac_address = mysql_real_escape_string(validate_macaddress($request_xml->mac_address));
$serial = mysql_real_escape_string(validate_serial($request_xml->serial));
$hostname = mysql_real_escape_string(validate_hostname($request_xml->hostname));
$sw_version = mysql_real_escape_string(validate_sw_version($request_xml->sw_version));
$hw_version = mysql_real_escape_string(validate_hw_version($request_xml->hw_version));

2.)Bom dodal preverjanje XML-ja.

3.)Sem popravil manjkajoči $ v query-u in na koncu predno zaprem bazo dodal mysqlfreeresult().

4.)login requestov bo zelo malo oz. samo takrat ko bo naprava izgubila povezavo s strežnikom ali se resetirala. Bodo pa še drugi requesti in sicer pošiljanje statusa naprave ki bo vsakih 10-30s(Odvisno od konfiguracije) in pa pošiljanje statusa modula priklopljenega na napravo(max. 4 moduli) vsakih 5-20sekund. dosti manjši interval pomoje itak ni možen zaradi samega pinga gprs/3g omrežja :) Ti xml-ji bodo tudi malo večji kot login ker bodo vsebovali več podatkov...


všeč(0) ni všeč(0) spam(0)
 
_Dejan_ 2. sep 2011 10:43:09 Pridružen od:
16. jun 2010
321 objav
227 23 3
#8

@Roky: Sem šele zdele opazil glede validateXMLFile ...

v kodi že preverim če je struktura XML-ja pravilna:


try {
$request_xml = new SimpleXMLElement($HTTP_RAW_POST_DATA);
} catch (Exception $e) {}

if (!$request_xml)
{
//NAPAKA XML NIMA PRAVILNE STRUKTURE IN KOT REZULTAT VRNE ERROR XML DOKUMENT
$response_xml = new SimpleXMLElement("<?xml version='1.0' encoding=\"iso-8859-1\" ?><error></error>");
foreach(libxml_get_errors() as $error)
{
$response_xml->addChild('message', trim($error->message));
}
libxml_clear_errors();
header('Content-Type: text/xml');
echo $response_xml->asXML();
}
else
{
//XML STRUKTURA JE OK
}

Če pogledaš sem imel to narejeno v drugem postu... tako da trenutna koda izgleda takole:


<?php
include('config.php');

libxml_use_internal_errors(true);
libxml_clear_errors();

function getClientIPAddress()
{
if (!empty($_SERVER['HTTP_CLIENT_IP']))
{
$ip=$_SERVER['HTTP_CLIENT_IP'];
}
elseif (!empty($_SERVER['HTTP_X_FORWARDED_FOR']))
{
$ip=$_SERVER['HTTP_X_FORWARDED_FOR'];
}
else
{
$ip=$_SERVER['REMOTE_ADDR'];
}
return $ip;
}

function validate_macaddress($val)
{
if(preg_match('/^([0-9a-fA-F][0-9a-fA-F]:){5}([0-9a-fA-F][0-9a-fA-F])$/', $val))
{
return $val;
}
else
{
return "00:00:00:00:00:00";
}
}

function validate_serial($val)
{
if(preg_match('/^[0-9]{6}$/', $val))
{
return $val;
}
else
{
return "000000";
}
}

function validate_hostname($val)
{
if(preg_match('/(?=^.{1,254}$)(^(?:(?!\d+\.|-)[a-zA-Z0-9_\-]{1,63}(?<!-)\.?)+(?:[a-zA-Z]{2,})$)/', $val))
{
return $val;
}
else
{
return "default";
}
}

function validate_sw_version($val)
{
if(preg_match('/^(\d+)((\.{1}\d+)*)(\.{0})$/', $val))
{
return $val;
}
else
{
return "0.00.0000";
}
}

function validate_hw_version($val)
{
if(preg_match('/^(\d+)((\.{1}\d+)*)(\.{0})$/', $val))
{
return $val;
}
else
{
return "0.00";
}
}

$db = mysql_connect($dbhost, $dbuser, $dbpasswd);
mysql_select_db ($dbname) or die ("Cannot connect to database");
mysql_query("SET NAMES 'utf8'");

try {
$request_xml = new SimpleXMLElement($HTTP_RAW_POST_DATA);
} catch (Exception $e) {}

if (!$request_xml)
{
$response_xml = new SimpleXMLElement("<?xml version='1.0' encoding=\"iso-8859-1\" ?><error></error>");
foreach(libxml_get_errors() as $error)
{
$response_xml->addChild('message', trim($error->message));
}
libxml_clear_errors();
header('Content-Type: text/xml');
echo $response_xml->asXML();
}
else
{
$mac_address = mysql_real_escape_string(validate_macaddress($request_xml->mac_address));
$serial = mysql_real_escape_string(validate_serial($request_xml->serial));
$hostname = mysql_real_escape_string(validate_hostname($request_xml->hostname));
$sw_version = mysql_real_escape_string(validate_sw_version($request_xml->sw_version));
$hw_version = mysql_real_escape_string(validate_hw_version($request_xml->hw_version));

$sql = "SELECT id, active, REPLACE(UUID(),'-', '') AS session FROM devices WHERE mac_address='" . $mac_address . "' AND serial='" . $serial . "' AND hostname='" . $hostname . "'";
$result = mysql_query($sql);
$device = mysql_fetch_object($result);

$response_xml = new SimpleXMLElement("<?xml version='1.0' encoding=\"iso-8859-1\" ?><packet></packet>");
$response_xml->addChild('device_id', $device->id);
$response_xml->addChild('active', $device->active);
$response_xml->addChild('session', $device->session);

if(is_null($device->id))
{}
else
{
if($device->active = 1)
{
mysql_query("UPDATE devices SET status='01', dt_last_online=Now(), session='" . $device->session . "', sw_version='" . $sw_version . "', hw_version='" . $hw_version . "', ip_address='" . getClientIPAddress() . "' WHERE id=" . $device->id . ";");
mysql_query("INSERT INTO events (dt, device_id, event, response, description) VALUES (Now(), " . $device->id . ", '0001', '01', '" . $device->session . "');");
}
else
{
mysql_query("UPDATE devices SET status='02', dt_last_online=Now(), session='', sw_version='" . $sw_version . "', hw_version='" . $hw_version . "', ip_address='" . getClientIPAddress() . "' WHERE id=" . $device->id . ";");
mysql_query("INSERT INTO events (dt, device_id, event, response, description) VALUES (Now(), " . $device->id . ", '0001', '02', '');");
}
}
header('Content-Type: text/xml');
echo $response_xml->asXML();
}
mysql_free_result($result);
mysql_close($db);
?>
všeč(0) ni všeč(0) spam(0)
 
Roky 2. sep 2011 14:37:25 Pridružen od:
9. apr 2008
2506 objav
3240 335 107
#9

Aha, OK, pol je pa to to.


všeč(0) ni všeč(0) spam(0)
rok.meglic@gmail.com - 031 492 148
 
_Dejan_ 28. nov 2011 13:35:31 Pridružen od:
16. jun 2010
321 objav
227 23 3
#10

Imam še eno vprašanje in sicer preko socketa pošljem serverju request:


POST /API/ HTTP/1.0
Host: www.domena.com
Content-type: text/xml
Content-length: 116
Connection: Close

<UserAuthOn><IdentType value='KEYBOARD'/><IdentCode value='12345678'/><Tstamp value='1977-1-1 1:1:37'/></UserAuthOn>

Vendar ne dobim pravilne povratne informacije. Od serverja dobim:


HTTP/1.1 200 OK
Server: nginx
Date: Mon, 28 Nov 2011 13:23:55 GMT
Content-Type: text/xml
Connection: close
X-Powered-By: PHP/5.3.8

Torej brez "Content-Length:" in vsebine. Preko Firefox REST Clienta dela BP. Kakšna ideja kaj je narobe v mojem requestu?


všeč(0) ni všeč(0) spam(0)
 
 

🔒 Za odgovor na to temo se moraš prijaviti.

Prijavi se