VPN licensing server
Ви не можете вибрати більше 25 тем Теми мають розпочинатися з літери або цифри, можуть містити дефіси (-) і не повинні перевищувати 35 символів.

401 lines
12KB

  1. <?php
  2. require "vendor/autoload.php";
  3. require "config.php";
  4. class LicenseRegistration {
  5. private $db = null;
  6. private $m_request_body = "";
  7. private $m_input = FALSE;
  8. function __construct()
  9. {
  10. // Takes raw data from the request
  11. $this->m_request_body = file_get_contents('php://input');
  12. $this->m_input = json_decode(($this->m_request_body));
  13. $this->db = new MeekroDB(DB_HOST, DB_USER, DB_PASS,DB_NAME); // from config file
  14. //enable exception
  15. $this->db->error_handler = false;
  16. $this->db->nonsql_error_handler = false;
  17. $this->db->throw_exception_on_error = true;
  18. $this->db->throw_exception_on_nonsql_error = true;
  19. }
  20. private function is_valid_input()
  21. {
  22. //if it's not a valid json
  23. if ( $this->m_input != FALSE &&
  24. property_exists($this->m_input, "license") &&
  25. property_exists($this->m_input, "mid")
  26. ){
  27. if ( strlen($this->m_input->license) >= 10 &&
  28. strlen($this->m_input->mid) >=10
  29. ){
  30. return true;
  31. }
  32. }
  33. $this->record_possible_hack();
  34. return false;
  35. }
  36. private function is_legit_code()
  37. {
  38. $code = $this->m_input->license;
  39. $f = $this->db->query("SELECT * FROM volume WHERE code='$code'");
  40. return count($f) == 1 ;
  41. }
  42. //record possible hack input
  43. //save ip, date, request header, and invalid inputs
  44. private function record_possible_hack()
  45. {
  46. $dump = new DumpHTTPRequestToString();
  47. $req = $dump->execute();
  48. $err = $this->db->insert("hack", array(
  49. "ip" => $this->get_client_ip(),
  50. "input" => $this->m_request_body,
  51. "request" => $req
  52. ));
  53. }
  54. private function fresh_registration()
  55. {
  56. $config = $this->get_license_config();
  57. //inser it into database
  58. $license = [
  59. 'code' => $this->m_input->license,
  60. 'mid' => $this->m_input->mid,
  61. 'seat' => 1,
  62. 'quota' => $this->get_license_quota() ,
  63. 'config' => intVal($config["id"]),
  64. 'enabled' => 1,
  65. 'ip' => $this->get_client_ip()
  66. ];
  67. try {
  68. $this->db->insert("register", $license);
  69. $this->send_config($config, $license);
  70. }catch (MeekroDBException $e){
  71. $this->send_error("Failed to add registration, please try again later" );
  72. }
  73. }
  74. private function get_exact_match()
  75. {
  76. $code = $this->m_input->license;
  77. $mid = $this->m_input->mid;
  78. $match = $this->db->query("SELECT * FROM register WHERE code='$code' AND mid='$mid';");
  79. if (count($match) !=1 ){
  80. return FALSE;
  81. }else{
  82. return $match[0];
  83. }
  84. }
  85. private function get_license_by_code()
  86. {
  87. $code = $this->m_input->license;
  88. $matches = $this->db->query("SELECT * FROM register WHERE code='$code';");
  89. if (count($matches) < 1 ){
  90. return FALSE;
  91. }else{
  92. return $matches;
  93. }
  94. }
  95. private function quota_available($matches)
  96. {
  97. return count($matches) < $this->get_license_quota();
  98. }
  99. private function allocate_seat($matches){
  100. $seat = $this->find_empty_seat($matches);
  101. $config = $this->get_license_config();
  102. $license = array(
  103. 'code' => $this->m_input->license,
  104. 'mid' => $this->m_input->mid,
  105. 'seat' => $seat,
  106. 'quota' => $this->get_license_quota() ,
  107. 'config' => intVal($config["id"]),
  108. 'enabled' => 1,
  109. 'ip' => $this->get_client_ip()
  110. );
  111. try {
  112. $this->db->insert("register",$license);
  113. $this->send_config($config, $license);
  114. }catch (MeekroDBException $e){
  115. $this->send_error("Allocate Seats $seat failed");
  116. }
  117. }
  118. private function find_empty_seat($matches)
  119. {
  120. for ($i=1; $i <= $this->get_license_quota(); $i++) {
  121. if ( $this->seats_occupied($matches, $i) )
  122. continue;
  123. return $i;
  124. }
  125. }
  126. private function seats_occupied($matches, $seat)
  127. {
  128. for ($i=0; $i< count($matches); $i++){
  129. if ( $matches[$i]["seat"] == $seat )
  130. return true;
  131. }
  132. return false;
  133. }
  134. //main routine
  135. public function start_registration()
  136. {
  137. if (! $this->is_valid_input() ){
  138. $this->send_error("Sorry, your input is invalid");
  139. return; //stop everything
  140. }
  141. if (! $this->is_legit_code()) {
  142. $this->send_error("Sorry, Your license is not valid");
  143. return;
  144. }
  145. //check mid see if a matched license code and mid already exist
  146. $license = $this->get_exact_match();
  147. if ( $license != FALSE ){ //we found the match both license-code and machine-id
  148. //get existing config
  149. if ( $license['enabled'] == 1 ) {
  150. $existing_config = $this->get_config_by_id($license["config"]);
  151. $this->send_config($existing_config, $license);
  152. }else{
  153. $this->send_error("Sorry, Your license is disabled");
  154. }
  155. }else{
  156. $licenses = $this->get_license_by_code();
  157. if ( $licenses == FALSE ) {//not found
  158. $this->fresh_registration();
  159. }else{//we found, existing records
  160. if ( $this->quota_available($licenses) ){
  161. $this->allocate_seat($licenses);
  162. }else{
  163. $this->send_error("Sorry, Your license has been fully allocated");
  164. }
  165. }
  166. }
  167. }
  168. private function get_config_by_id($id)
  169. {
  170. $f= $this->db->query("SELECT * FROM servers WHERE id=$id");
  171. return $f[0];
  172. }
  173. private function send_config($config, $license)
  174. {
  175. $msg = "" ;
  176. if ($license["quota"] >1){
  177. $msg = "Seat " . $license["seat"] . "/" . $license["quota"] . " has been allocated to you";
  178. }else{
  179. $msg = "Single user license registered sucessfully";
  180. }
  181. $result= array(
  182. "success" => true,
  183. "type" => $config["type"],
  184. "host" => $config["host"],
  185. "port" => $config["port"],
  186. "username" => $config["user"],
  187. "password" => $config["pass"],
  188. "license" => $license["code"],
  189. "mid" => $license["mid"],
  190. "proxyDNS" => true,
  191. "enable" => true,
  192. "errMsg" => $msg,
  193. "link" => "https://hk-01.biukop.com/",
  194. "linkText" => "Check My IP",
  195. );
  196. $myJSON = json_encode($result);
  197. echo $myJSON;
  198. }
  199. private function send_error($msg){
  200. echo $msg;
  201. die();
  202. }
  203. private function decode_pass($encrypt)
  204. {
  205. $buf = base64_decode($encrypt);
  206. if ( substr($buf, -3, 2) != "#!" )
  207. return "invalid-encrypt";
  208. $magic_digit = intVal(substr($buf, 2 , 1));
  209. $pass = substr($buf,5, -$magic_digit);
  210. return $pass;
  211. }
  212. private function encode_pass($pass) {
  213. // prefix: "xxyxx"; Y is digit
  214. $magic_digit = $this->rand_digit();
  215. $prefix = $this->build_prefix($magic_digit);
  216. $suffix = $this->build_suffix($magic_digit);
  217. //surffix yyyyy#!xx , max 9
  218. return base64_encode($prefix . $pass . $suffix);
  219. }
  220. private function rand_char() {
  221. $pool="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789~!@#$%^&*()_+:{}|<>?[]\;,./";
  222. $idx = rand(0, strlen($pool) -1 );
  223. return substr($pool, $idx, 1);
  224. }
  225. private function rand_digit() {
  226. return rand(3,9);
  227. }
  228. private function build_prefix($magic_digit)
  229. {
  230. return $this->rand_char() . $this->rand_char() .
  231. "$magic_digit" .
  232. $this->rand_char() . $this->rand_char();
  233. }
  234. private function build_suffix($magic_digit)
  235. { //$magic_digit >=3
  236. $suffix = "";
  237. for ($i=1; $i<= $magic_digit-3; $i++) {
  238. $suffix .= $this->rand_char();
  239. }
  240. $suffix .= "#!";
  241. $suffix .= $this->rand_char();
  242. return $suffix;
  243. }
  244. private function get_client_ip ()
  245. {
  246. //whether ip is from share internet
  247. if (!empty($_SERVER['HTTP_CLIENT_IP']))
  248. {
  249. $ip_address = $_SERVER['HTTP_CLIENT_IP'];
  250. }
  251. //whether ip is from proxy
  252. elseif (!empty($_SERVER['HTTP_X_FORWARDED_FOR']))
  253. {
  254. $ip_address = $_SERVER['HTTP_X_FORWARDED_FOR'];
  255. }
  256. //whether ip is from remote address
  257. else
  258. {
  259. $ip_address = $_SERVER['REMOTE_ADDR'];
  260. }
  261. return $ip_address;
  262. }
  263. private function get_license_quota()
  264. {
  265. $code = $this->m_input->license;
  266. $f = $this->db->query("SELECT * FROM volume WHERE code='$code'");
  267. return intVal($f[0]["max"]);
  268. }
  269. private function get_license_config()
  270. {
  271. $code = $this->m_input->license;
  272. $f = $this->db->query("SELECT * FROM servers WHERE code='$code'");
  273. return $f[0];
  274. }
  275. public function test_password() {
  276. $encrypt = $this->encode_pass("superforex");
  277. $pass1 = $this->decode_pass($encrypt);
  278. $pass = $this->decode_pass("Y2Q1ZDFzdXBlcmZvcmV4MTIjITU=");
  279. if ($pass != $pass1){
  280. echo "wrong";
  281. }
  282. }
  283. public function test_license_quota() {
  284. $this->m_input = new stdClass();
  285. $this->m_input->license = "VOL-XDTG-ADQE-DQERG-QERFDA";
  286. $six = $this->get_license_quota();
  287. if ($six != 6)
  288. echo "wrong: license quota";
  289. }
  290. public function test_get_config() {
  291. $this->m_input = new stdClass();
  292. $this->m_input->license = "VOL-XDTG-ADQE-DQERG-QERFDA";
  293. $config = $this->get_license_config();
  294. }
  295. public function test_fresh_registration() {
  296. $this->m_input = new stdClass();
  297. $this->m_input->license = "VOL-XDTG-ADQE-DQERG-QERFDA";
  298. $this->m_input->mid = "AFDAFDSAFDSAFSDAFSAFSDA";
  299. $this->fresh_registration();
  300. }
  301. public function test_licenses() {
  302. $this->m_input = new stdClass();
  303. $this->m_input->license = "VOL-XDTG-ADQE-DQERG-QERFDA";
  304. $this->m_input->mid = "2-AFDAFDSAFDSAFSDAFSAFSDA";
  305. $this->start_registration();
  306. }
  307. public function test() {
  308. $this->m_input = new stdClass();
  309. $this->m_input->license = "VOL";
  310. $this->m_input->mid = "2-AFD";
  311. $this->start_registration();
  312. }
  313. }
  314. class DumpHTTPRequestToString {
  315. public function execute() {
  316. $data = sprintf(
  317. "%s %s %s %s\n\nHTTP headers:\n",
  318. $_SERVER['REQUEST_METHOD'],
  319. $_SERVER['REQUEST_URI'],
  320. $_SERVER['SERVER_PROTOCOL'],
  321. $_SERVER['REMOTE_ADDR']
  322. );
  323. foreach ($this->getHeaderList() as $name => $value) {
  324. $data .= $name . ': ' . $value . "\n";
  325. }
  326. $data .= "\nRequest body:\n" . file_get_contents('php://input');
  327. return $data;
  328. }
  329. private function getHeaderList() {
  330. $headerList = [];
  331. foreach ($_SERVER as $name => $value) {
  332. if (preg_match('/^HTTP_/',$name)) {
  333. // convert HTTP_HEADER_NAME to Header-Name
  334. $name = strtr(substr($name,5),'_',' ');
  335. $name = ucwords(strtolower($name));
  336. $name = strtr($name,' ','-');
  337. // add to list
  338. $headerList[$name] = $value;
  339. }
  340. }
  341. return $headerList;
  342. }
  343. }
  344. $bb = new LicenseRegistration();
  345. $bb->start_registration();
  346. //$bb->test();
  347. ?>