openSRS_base.php 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380
  1. <?php
  2. namespace DNSManager\opensrs;
  3. class openSRS_base {
  4. // This values will be part of separate config file
  5. public $osrs_username = "";
  6. public $osrs_password = "";
  7. public $osrs_key = "";
  8. public $osrs_environment = ""; // 'TEST' / 'LIVE' / 'HRS'
  9. public $osrs_protocol = ""; // 'XCP' / 'TPP'
  10. public $osrs_host = "";
  11. public $osrs_port = "";
  12. public $osrs_sslPort = "";
  13. public $osrs_baseClassVersion = "";
  14. public $osrs_version = "";
  15. // Class internal
  16. protected $crypt_type = 'DES'; // 'DES'/'BLOWFISH'/'SSL'
  17. private $_socket = false;
  18. private $_socketErrorNum = false;
  19. private $_socketErrorMsg = false;
  20. private $_socketTimeout = 120; // seconds
  21. private $_socketReadTimeout = 120; // seconds
  22. private $_authenticated = false;
  23. private $CRLF= "\r\n";
  24. protected $_opsHandler;
  25. protected $_CBC = false;
  26. // Class constructor
  27. public function __construct ($data) {
  28. $this->osrs_username = $data['username'];
  29. $this->osrs_password = $data['password'];
  30. $this->osrs_key = $data['apikey'];
  31. $this->osrs_environment = $data['environment'];
  32. $this->osrs_protocol = $data['protocol'];
  33. $this->osrs_host = $data['host'];
  34. $this->osrs_port = '55000';
  35. $this->osrs_sslPort = '55443';
  36. $this->osrs_baseClassVersion = '2.8.0';
  37. $this->osrs_version = 'XML:0.1';
  38. $this->_verifySystemProperties ();
  39. $this->_opsHandler = new openSRS_ops;
  40. }
  41. // Class destructor
  42. public function __destruct () {
  43. }
  44. // Private functions
  45. private function _pullConfigVariables () {
  46. }
  47. private function _verifySystemProperties () {
  48. // Encryption verification
  49. if ('SSL' == $this->crypt_type) {
  50. if (!function_exists('version_compare') || version_compare('4.3', phpversion(), '>=')) {
  51. $error_message = "PHP version must be v4.3+ (current version is ". phpversion() .") to use \"SSL\" encryption";
  52. trigger_error ($error_message, E_USER_WARNING);
  53. die();
  54. } elseif (!function_exists('openssl_open')) {
  55. $error_message = "PHP must be compiled using --with-openssl to use \"SSL\" encryption";
  56. trigger_error ($error_message, E_USER_WARNING);
  57. die();
  58. }
  59. }
  60. }
  61. // Send a command to the server
  62. public function send_cmd($request) {
  63. // make or get the socket filehandle
  64. if (!$this->init_socket() ) {
  65. trigger_error ("oSRS Error - Unable to establish socket: (". $this->_socketErrorNum .") ". $this->_socketErrorMsg, E_USER_WARNING);
  66. die();
  67. }
  68. // Authenticate user
  69. $auth = $this->authenticate();
  70. if (!$auth) {
  71. if ($this->_socket) $this->close_socket();
  72. trigger_error ("oSRS Error - Authentication Error: ". $auth['error'], E_USER_WARNING);
  73. die();
  74. }
  75. $this->send_data($request);
  76. $data = $this->read_data();
  77. $num_matches = preg_match('/<item key="response_code">401<\/item>/', $data, $matches);
  78. if ($num_matches > 0)
  79. trigger_error("oSRS Error - Reseller username or osrs_key is incorrect, please check your config file.");
  80. return $data;
  81. }
  82. // Initialize a socket connection to the OpenSRS server
  83. private function init_socket() {
  84. if ($this->_socket) return true;
  85. if (!$this->osrs_environment) return false;
  86. if ($this->crypt_type == 'SSL') {
  87. $tempPortHand = $this->osrs_sslPort;
  88. $conType = 'ssl://';
  89. } else {
  90. $tempPortHand = $this->osrs_port;
  91. $conType = '';
  92. }
  93. $this->_socket = fsockopen($conType . $this->osrs_host, $tempPortHand, $this->_socketErrorNum, $this->_socketErrorMsg, $this->_socketTimeout);
  94. if (!$this->_socket) {
  95. return false;
  96. } else {
  97. return true;
  98. }
  99. }
  100. // Authenticate the connection with the username/private key
  101. private function authenticate() {
  102. if ($this->_authenticated || 'SSL' == $this->crypt_type) {
  103. return array('is_success' => true);
  104. }
  105. $promptXML = $this->read_data();
  106. $prompt = $this->_opsHandler->decode($promptXML);
  107. if (isSet($prompt['response_code'])) {
  108. if ($prompt['response_code'] == 555 ) {
  109. // the ip address from which we are connecting is not accepted
  110. return array(
  111. 'is_success' => false,
  112. 'error' => $prompt['response_text']
  113. );
  114. }
  115. } else if ( !preg_match('/OpenSRS\sSERVER/', $prompt['attributes']['sender']) ||
  116. substr($prompt['attributes']['version'],0,3) != 'XML' ) {
  117. return array(
  118. 'is_success' => false,
  119. 'error' => 'Unrecognized Peer'
  120. );
  121. }
  122. // first response is server version
  123. $cmd = array(
  124. 'protocol' => $this->osrs_protocol,
  125. 'action' => 'check',
  126. 'object' => 'version',
  127. 'attributes' => array(
  128. 'sender' => 'OpenSRS CLIENT',
  129. 'version' => $this->osrs_version,
  130. 'state' => 'ready'
  131. )
  132. );
  133. $xmlCMD = $this->_opsHandler->encode($cmd);
  134. $this->send_data($xmlCMD);
  135. $cmd = array(
  136. 'protocol' => $this->osrs_protocol,
  137. 'action' => 'authenticate',
  138. 'object' => 'user',
  139. 'attributes' => array(
  140. 'crypt_type' => strtolower($this->crypt_type),
  141. 'username' => $this->osrs_username,
  142. 'password' => $this->osrs_username
  143. )
  144. );
  145. $xmlCMD = $this->_opsHandler->encode($cmd);
  146. $this->send_data( $xmlCMD );
  147. $challenge = $this->read_data();
  148. // Sanity check to make sure that the osrs_key is all hex values
  149. $hex_check = ctype_xdigit($this->osrs_key);
  150. // Respond to the challenge with the MD5 checksum of the challenge.
  151. // ... and PHP's md5() doesn't return binary data, so
  152. // we need to pack that too
  153. if ($hex_check){
  154. $this->_CBC = new openSRS_crypt(pack('H*', $this->osrs_key), $this->crypt_type);
  155. $response = pack('H*',md5($challenge));
  156. $this->send_data($response);
  157. // Read the server's response to our login attempt (XML)
  158. $answerXML = $this->read_data();
  159. $answer = $this->_opsHandler->decode($answerXML);
  160. if (substr($answer['response_code'],0,1)== '2') {
  161. $this->_authenticated = true;
  162. return true;
  163. } else {
  164. return false;
  165. }
  166. } else {
  167. trigger_error("oSRS Error - Please check the osrs_key value in the config file, it contains a non hexidecimal character.");
  168. }
  169. }
  170. // Close the socket connection
  171. private function close_socket() {
  172. fclose($this->_socket);
  173. if ($this->_CBC) $this->_CBC->_openSRS_crypt(); /* destructor */
  174. $this->_CBC = false;
  175. $this->_authenticated = false;
  176. $this->_socket = false;
  177. }
  178. private function read_data() {
  179. $buf = $this->readData($this->_socket, $this->_socketReadTimeout);
  180. if (!$buf) {
  181. trigger_error ("oSRS Error - Read buffer is empty. Please make sure IP is whitelisted in RWI. Check the osrs_key and osrs_username in the config file as well.", E_USER_WARNING);
  182. $data = "";
  183. } else {
  184. $data = $this->_CBC ? $this->_CBC->decrypt($buf) : $buf;
  185. }
  186. return $data;
  187. }
  188. private function send_data($message) {
  189. if ($this->_CBC) $message = $this->_CBC->encrypt($message);
  190. return $this->writeData( $this->_socket, $message );
  191. }
  192. // Regex check for valid email
  193. private function check_email_syntax($email) {
  194. if (preg_match('/(@.*@)|(\.\.)|(@\.)|(\.@)|(^\.)/', $email) || !preg_match('/^\S+\@(\[?)[a-zA-Z0-9\-\.]+\.([a-zA-Z]{2,3}|[0-9]{1,3})(\]?)$/', $email)) {
  195. return false;
  196. } else {
  197. return true;
  198. }
  199. }
  200. /**
  201. * Writes a message to a socket (buffered IO)
  202. * @param int socket handle
  203. * @param string message to write
  204. */
  205. private function writeData(&$fh,$msg) {
  206. $header = "";
  207. $len = strlen($msg);
  208. switch ($this->crypt_type) {
  209. case 'SSL':
  210. $signature = md5(md5($msg.$this->osrs_key).$this->osrs_key);
  211. $header .= "POST / HTTP/1.0". $this->CRLF;
  212. $header .= "Content-Type: text/xml" . $this->CRLF;
  213. $header .= "X-Username: " . $this->osrs_username . $this->CRLF;
  214. $header .= "X-Signature: " . $signature . $this->CRLF;
  215. $header .= "Content-Length: " . $len . $this->CRLF . $this->CRLF;
  216. break;
  217. case 'BLOWFISH':
  218. case 'DES':
  219. default:
  220. $header .= "Content-Length: " . $len . $this->CRLF . $this->CRLF;
  221. break;
  222. }
  223. fputs($fh, $header);
  224. fputs($fh, $msg, $len );
  225. }
  226. /**
  227. * Reads header data
  228. * @param int socket handle
  229. * @param int timeout for read
  230. * @return hash hash containing header key/value pairs
  231. */
  232. private function readHeader($fh, $timeout=5) {
  233. $header = array();
  234. switch ($this->crypt_type) {
  235. case 'SSL':
  236. /* HTTP/SSL connection method */
  237. $http_log ='';
  238. $line = fgets($fh, 4000);
  239. $http_log .= $line;
  240. if (!preg_match('/^HTTP\/1.1 ([0-9]{0,3}) (.*)\r\n$/',$line, $matches)) {
  241. trigger_error ("oSRS Error - UNEXPECTED READ: Unable to parse HTTP response code. Please make sure IP is whitelisted in RWI.", E_USER_WARNING);
  242. return false;
  243. }
  244. $header['http_response_code'] = $matches[1];
  245. $header['http_response_text'] = $matches[2];
  246. while ($line != $this->CRLF) {
  247. $line = fgets($fh, 4000);
  248. $http_log .= $line;
  249. if (feof($fh)) {
  250. trigger_error ("oSRS Error - UNEXPECTED READ: Error reading HTTP header.", E_USER_WARNING);
  251. return false;
  252. }
  253. $matches = explode(': ', $line, 2);
  254. if (sizeof($matches) == 2) {
  255. $header[trim(strtolower($matches[0]))] = $matches[1];
  256. }
  257. }
  258. $header['full_header'] = $http_log;
  259. break;
  260. case 'BLOWFISH':
  261. case 'DES':
  262. default:
  263. /* socket (old-style) connection */
  264. $line = fgets($fh, 4000);
  265. if ($this->_opsHandler->socketStatus($fh)) {
  266. return false;
  267. }
  268. if (preg_match('/^\s*Content-Length:\s+(\d+)\s*\r\n/i', $line, $matches ) ) {
  269. $header{'content-length'} = (int)$matches[1];
  270. } else {
  271. trigger_error ("oSRS Error - UNEXPECTED READ: No Content-Length.", E_USER_WARNING);
  272. return false;
  273. }
  274. /* read the empty line */
  275. $line = fread($fh, 2);
  276. if ($this->_opsHandler->socketStatus($fh)) {
  277. return false;
  278. }
  279. if ($line!=$this->CRLF) {
  280. trigger_error ("oSRS Error - UNEXPECTED READ: No CRLF.", E_USER_WARNING);
  281. return false;
  282. }
  283. break;
  284. }
  285. return $header;
  286. }
  287. /**
  288. * Reads data from a socket
  289. * @param int socket handle
  290. * @param int timeout for read
  291. * @return mixed buffer with data, or an error for a short read
  292. */
  293. private function readData(&$fh, $timeout=5) {
  294. $len = 0;
  295. /* PHP doesn't have timeout for fread ... we just set the timeout for the socket */
  296. socket_set_timeout($fh, $timeout);
  297. $header = $this->readHeader($fh, $timeout);
  298. if (!$header || !isset($header{'content-length'}) || (empty($header{'content-length'}))) {
  299. if ($this->crypt_type == "SSL")
  300. trigger_error ("oSRS Error - UNEXPECTED ERROR: No Content-Length header provided! Please make sure IP is whitelisted in RWI.", E_USER_WARNING);
  301. else
  302. trigger_error ("oSRS Error - No Content-Length header returned. Please make sure IP is whitelisted in RWI. Check the API Key and Username.", E_USER_WARNING);
  303. }
  304. $len = (int)$header{'content-length'};
  305. $line = '';
  306. while (strlen($line) < $len) {
  307. $line .= fread($fh, $len);
  308. if ($this->_opsHandler->socketStatus($fh)) {
  309. return false;
  310. }
  311. }
  312. if ($line) {
  313. $buf = $line;
  314. } else {
  315. $buf = false;
  316. }
  317. if ('SSL' == $this->crypt_type) $this->close_socket();
  318. return $buf;
  319. }
  320. // Helper functions
  321. public function convertXML2array ($xml) {
  322. $array = $this->_opsHandler->decode($xml);
  323. return $array;
  324. }
  325. }