NetworkDetection.php 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352
  1. <?php
  2. namespace App\Components;
  3. use Http;
  4. use Log;
  5. class NetworkDetection
  6. {
  7. public const success = '✔️ 通讯正常';
  8. public const oversea_blocked = '🛑 海外阻断';
  9. public const china_blocked = '🛑 国内阻断';
  10. public const disconnected = '❌ 断连';
  11. /**
  12. * 用外部API进行Ping检测.
  13. *
  14. * @param string $ip 被检测的IP或者域名
  15. *
  16. * @return bool
  17. */
  18. public function ping(string $ip)
  19. {
  20. $round = 0;
  21. // 依次尝试接口
  22. while (true) {
  23. switch ($round) {
  24. case 0:
  25. $ret = $this->oiowebPing($ip);
  26. break;
  27. default:
  28. return false;
  29. }
  30. if ($ret !== false) {
  31. return $ret;
  32. }
  33. $round++;
  34. }
  35. }
  36. private function oiowebPing(string $ip)
  37. {
  38. $msg = null;
  39. foreach ([1, 6, 14] as $line) {
  40. $url = "https://api.oioweb.cn/api/hostping.php?host={$ip}&node={$line}"; // https://api.iiwl.cc/api/ping.php?host=
  41. $response = Http::timeout(15)->get($url);
  42. // 发送成功
  43. if ($response->ok()) {
  44. $message = $response->json();
  45. if ($message && $message['code']) {
  46. $msg .= "{$message['node']}:{$message['data']['Time']}<br>";
  47. }
  48. } else {
  49. return false;
  50. }
  51. }
  52. if ($msg) {
  53. return $msg;
  54. }
  55. Log::warning('【PING】检测'.$ip.'时,api.oioweb.cn无结果');
  56. // 发送错误
  57. return false;
  58. }
  59. /**
  60. * 通过众多API进行节点阻断检测.
  61. *
  62. * @param string $ip 被检测的IP
  63. * @param bool $is_icmp TRUE 为ICMP,FALSE 为tcp
  64. * @param int|null $port 检测端口,默认为空
  65. *
  66. * @return bool|string
  67. */
  68. public function networkCheck(string $ip, bool $is_icmp, int $port = null)
  69. {
  70. $round = 1;
  71. // 依次尝试接口
  72. while (true) {
  73. switch ($round) {
  74. case 0:
  75. $ret = $this->idcWiki($ip, $is_icmp, $port);
  76. break;
  77. case 1:
  78. $ret = $this->flyzy2005($ip, $is_icmp, $port);
  79. break;
  80. case 2:
  81. $ret = $this->vps234($ip, $is_icmp);
  82. break;
  83. case 3:
  84. $ret = $this->idcoffer($ip, $is_icmp, $port);
  85. break;
  86. case 4:
  87. $ret = $this->gd($ip, $is_icmp, $port);
  88. break;
  89. case 5:
  90. $ret = $this->ip112($ip, $is_icmp, $port);
  91. break;
  92. default:
  93. return false;
  94. }
  95. if ($ret !== false) {
  96. return $ret;
  97. }
  98. $round++;
  99. }
  100. }
  101. private function idcWiki(string $ip, bool $is_icmp, int $port = null)
  102. {
  103. if ($is_icmp) {
  104. $type_string = 'icmp/';
  105. $checkName = 'ICMP';
  106. } else {
  107. $type_string = 'tcp_ack/';
  108. $checkName = 'TCP';
  109. }
  110. if ($port) {
  111. $port = '/'.$port;
  112. $type_string = 'tcp_port/';
  113. }
  114. $url = "https://api.50network.com/china-firewall/check/ip/{$type_string}{$ip}{$port}";
  115. $response = Http::timeout(15)->get($url);
  116. if ($response->ok()) {
  117. $message = $response->json();
  118. if (! $message) {
  119. Log::warning('【'.$checkName.'阻断检测】检测'.$ip.'时,接口返回异常访问链接:'.$url);
  120. return false;
  121. }
  122. if (! $message['success']) {
  123. if (isset($message['error']) && $message['error'] === 'execute timeout (3s)') {
  124. return false;
  125. }
  126. Log::warning('【'.$checkName.'阻断检测】检测'.$ip.$port.'时,返回'.var_export($message, true));
  127. return false;
  128. }
  129. if ($message['firewall-enable'] && $message['firewall-disable']) {
  130. return self::success; // 正常
  131. }
  132. if ($message['firewall-enable'] && ! $message['firewall-disable']) {
  133. return self::oversea_blocked; // 国外访问异常
  134. }
  135. if (! $message['firewall-enable'] && $message['firewall-disable']) {
  136. return self::china_blocked; // 被墙
  137. }
  138. return self::disconnected; // 服务器宕机
  139. }
  140. return false;
  141. }
  142. private function flyzy2005(string $ip, bool $is_icmp, int $port = null)
  143. {
  144. $cn = "https://mini.flyzy2005.cn/ip_check.php?ip={$ip}&port={$port}";
  145. $us = "https://mini.flyzy2005.cn/ip_check_outside.php?ip={$ip}&port={$port}";
  146. $checkName = $is_icmp ? 'icmp' : 'tcp';
  147. $response_cn = Http::timeout(15)->get($cn);
  148. $response_us = Http::timeout(15)->get($us);
  149. if ($response_cn->ok() && $response_us->ok()) {
  150. $cn = $response_cn->json();
  151. $us = $response_us->json();
  152. if (! $cn || ! $us) {
  153. Log::warning("【{$checkName}阻断检测】检测{$ip}时,接口返回异常访问链接:{$cn} | {$us}");
  154. return false;
  155. }
  156. if ($cn[$checkName] === 'success' && $us['outside_'.$checkName] === 'success') {
  157. return self::success; // 正常
  158. }
  159. if ($cn[$checkName] === 'success' && $us['outside_'.$checkName] !== 'success') {
  160. return self::oversea_blocked; // 国外访问异常
  161. }
  162. if ($cn[$checkName] !== 'success' && $us['outside_'.$checkName] === 'success') {
  163. return self::china_blocked; // 被墙
  164. }
  165. return self::disconnected; // 服务器宕机
  166. }
  167. return false;
  168. }
  169. private function vps234(string $ip, bool $is_icmp)
  170. {
  171. $url = 'https://www.vps234.com/ipcheck/getdata/';
  172. $checkName = $is_icmp ? 'ICMP' : 'TCP';
  173. $response = Http::timeout(15)->withoutVerifying()->asForm()->post($url, ['ip' => $ip]);
  174. if ($response->ok()) {
  175. $message = $response->json();
  176. if (! $message) {
  177. Log::warning('【'.$checkName.'阻断检测】检测'.$ip.'时,接口返回异常访问链接:'.$url);
  178. return false;
  179. }
  180. if (! $message['data']['success']) {
  181. Log::warning('【'.$checkName.'阻断检测】检测'.$ip.'时,返回'.var_export($message, true));
  182. return false;
  183. }
  184. if ($message['data']['data']['inner'.$checkName] && $message['data']['data']['out'.$checkName]) {
  185. return self::success; // 正常
  186. }
  187. if ($message['data']['data']['inner'.$checkName] && ! $message['data']['data']['out'.$checkName]) {
  188. return self::oversea_blocked; // 国外访问异常
  189. }
  190. if (! $message['data']['data']['inner'.$checkName] && $message['data']['data']['out'.$checkName]) {
  191. return self::china_blocked; // 被墙
  192. }
  193. return self::disconnected; // 服务器宕机
  194. }
  195. return false;
  196. }
  197. private function idcoffer(string $ip, bool $is_icmp, int $port = null)
  198. { // 来源:https://www.idcoffer.com/ipcheck
  199. $cn = "https://api.24kplus.com/ipcheck?host={$ip}&port={$port}";
  200. $us = "https://api.idcoffer.com/ipcheck?host={$ip}&port={$port}";
  201. $checkName = $is_icmp ? 'ping' : 'tcp';
  202. $response_cn = Http::timeout(15)->get($cn);
  203. $response_us = Http::timeout(15)->get($us);
  204. if ($response_cn->ok() && $response_us->ok()) {
  205. $cn = $response_cn->json();
  206. $us = $response_us->json();
  207. if (! $cn || ! $us) {
  208. Log::warning("【{$checkName}阻断检测】检测{$ip}时,接口返回异常访问链接:{$cn} | {$us}");
  209. return false;
  210. }
  211. if (! $cn['code'] || ! $us['code']) {
  212. Log::warning('【'.$checkName.'阻断检测】检测'.$ip.$port.'时,返回'.var_export($cn, true).var_export($us, true));
  213. return false;
  214. }
  215. if ($cn['data'][$checkName] && $us['data'][$checkName]) {
  216. return self::success; // 正常
  217. }
  218. if ($cn['data'][$checkName] && ! $us['data'][$checkName]) {
  219. return self::oversea_blocked; // 国外访问异常
  220. }
  221. if (! $cn['data'][$checkName] && $us['data'][$checkName]) {
  222. return self::china_blocked; // 被墙
  223. }
  224. return self::disconnected; // 服务器宕机
  225. }
  226. return false;
  227. }
  228. private function gd(string $ip, bool $is_icmp, int $port = 443)
  229. { // 来源:https://ping.gd/
  230. $url = "https://ping.gd/api/ip-test/{$ip}:".($port ?? 443);
  231. $checkName = $is_icmp ? 'ping_alive' : 'telnet_alive';
  232. $response = Http::timeout(15)->get($url);
  233. if ($response->ok()) {
  234. $message = $response->json();
  235. if (! $message) {
  236. Log::warning("【{$checkName}阻断检测】检测{$ip}时,接口返回异常访问链接:{$url}");
  237. return false;
  238. }
  239. if ($message[0]['result'][$checkName] && $message[1]['result'][$checkName]) {
  240. return self::success; // 正常
  241. }
  242. if ($message[0]['result'][$checkName] && ! $message[1]['result'][$checkName]) {
  243. return self::oversea_blocked; // 国外访问异常
  244. }
  245. if (! $message[0]['result'][$checkName] && $message[1]['result'][$checkName]) {
  246. return self::china_blocked; // 被墙
  247. }
  248. return self::disconnected; // 服务器宕机
  249. }
  250. return false;
  251. }
  252. private function ip112(string $ip, bool $is_icmp, int $port = 443)
  253. { // 来源:https://ip112.cn/
  254. $cn = 'https://api.zhujiquanzi.com/ipcheck/ipcheck.php';
  255. $us = 'https://api.52bwg.com/ipcheck/ipcheck.php';
  256. $checkName = $is_icmp ? 'icmp' : 'tcp';
  257. $response_cn = Http::asForm()->post($cn, ['ip' => $ip, 'port' => $port]);
  258. $response_us = Http::asForm()->post($us, ['ip' => $ip, 'port' => $port]);
  259. if ($response_cn->ok() && $response_us->ok()) {
  260. $cn = $response_cn->json();
  261. $us = $response_us->json();
  262. if (! $cn || ! $us) {
  263. Log::warning("【{$checkName}阻断检测】检测{$ip}时,接口返回异常访问链接:{$cn} | {$us}");
  264. return false;
  265. }
  266. if (str_contains($cn[$checkName], 'green') && str_contains($us[$checkName], 'green')) {
  267. return self::success; // 正常
  268. }
  269. if (str_contains($cn[$checkName], 'green') && ! str_contains($us[$checkName], 'green')) {
  270. return self::oversea_blocked; // 国外访问异常
  271. }
  272. if (! str_contains($cn[$checkName], 'green') && str_contains($us[$checkName], 'green')) {
  273. return self::china_blocked; // 被墙
  274. }
  275. return self::disconnected; // 服务器宕机
  276. }
  277. return false;
  278. }
  279. }