NetworkDetection.php 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389
  1. <?php
  2. namespace App\Utils;
  3. use Exception;
  4. use Http;
  5. use Illuminate\Http\Client\PendingRequest;
  6. use Log;
  7. class NetworkDetection
  8. {
  9. private static PendingRequest $basicRequest;
  10. public function ping(string $ip): ?string
  11. { // 用外部API进行Ping检测. TODO: 无权威外部API,功能缺失
  12. $testers = ['oiowebPing', 'xiaoapiPing', 'yum6Ping'];
  13. self::$basicRequest = Http::timeout(20)->withOptions(['http_errors' => false])->withUserAgent('Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36');
  14. foreach ($testers as $tester) {
  15. try {
  16. if (method_exists(self::class, $tester)) {
  17. $result = $this->$tester($ip);
  18. if ($result !== null) {
  19. return $result;
  20. }
  21. }
  22. } catch (Exception $e) {
  23. Log::error("[$tester] 网络延迟测试报错: ".$e->getMessage());
  24. continue;
  25. }
  26. }
  27. return null;
  28. }
  29. public function networkStatus(string $ip, int $port): ?array
  30. {
  31. $status = $this->networkCheck($ip, $port);
  32. if ($status) {
  33. $ret = [];
  34. foreach (['icmp', 'tcp'] as $protocol) {
  35. if ($status['in'][$protocol] && $status['out'][$protocol]) {
  36. $check = 1; // 正常
  37. }
  38. if ($status['in'][$protocol] && ! $status['out'][$protocol]) {
  39. $check = 2; // 国外访问异常
  40. }
  41. if (! $status['in'][$protocol] && $status['out'][$protocol]) {
  42. $check = 3; // 被墙
  43. }
  44. $ret[$protocol] = $check ?? 4; // 服务器宕机
  45. }
  46. return $ret;
  47. }
  48. return null;
  49. }
  50. private function networkCheck(string $ip, int $port): ?array
  51. { // 通过众多API进行节点阻断检测.
  52. $checkers = ['toolsdaquan', 'vps234', 'flyzy2005', 'idcoffer', 'ip112', 'upx8', 'rss', 'gd', 'vps1352', 'akile'];
  53. self::$basicRequest = Http::timeout(15)->retry(2)->withOptions(['http_errors' => false])->withoutVerifying()->withUserAgent('Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36');
  54. foreach ($checkers as $checker) {
  55. try {
  56. if (method_exists(self::class, $checker)) {
  57. $result = $this->$checker($ip, $port);
  58. if ($result !== null) {
  59. return $result;
  60. }
  61. }
  62. } catch (Exception $e) {
  63. Log::error("[$checker] 网络阻断测试报错: ".$e->getMessage());
  64. continue;
  65. }
  66. }
  67. return null;
  68. }
  69. private function oiowebPing(string $ip)
  70. {
  71. $msg = null;
  72. foreach ([1, 6, 14] as $line) {
  73. $response = self::$basicRequest->get("https://api.oioweb.cn/api/hostping.php?host=$ip&node=$line"); // https://api.iiwl.cc/api/ping.php?host=
  74. // 发送成功
  75. if ($response->ok()) {
  76. $message = $response->json();
  77. if ($message && $message['code']) {
  78. $msg .= "{$message['node']}:{$message['data']['Time']}<br>";
  79. }
  80. } else {
  81. return false;
  82. }
  83. }
  84. if ($msg) {
  85. return $msg;
  86. }
  87. Log::warning('【PING】检测'.$ip.'时,api.oioweb.cn无结果');
  88. // 发送错误
  89. return false;
  90. }
  91. private function xiaoapiPing(string $ip)
  92. { // 开发依据 https://xiaoapi.cn/?action=doc&id=3
  93. $response = self::$basicRequest->get("https://xiaoapi.cn/API/sping.php?url=$ip");
  94. // 发送成功
  95. if ($response->ok()) {
  96. return $response->body();
  97. }
  98. Log::warning("【PING】检测{$ip}时,xiaoapi.cn无结果");
  99. // 发送错误
  100. return false;
  101. }
  102. private function yum6Ping(string $ip)
  103. { // 来源 https://api.yum6.cn/ping.php?host=api.yum6.cn
  104. $response = self::$basicRequest->get("https://api.yum6.cn/ping.php?host=$ip");
  105. // 发送成功
  106. if ($response->ok()) {
  107. $msg = $response->json();
  108. if ($msg && $msg['state'] === '1000') {
  109. return "<h4>{$msg['ip']}</h4>线路【{$msg['node']}】<br> 最小值:{$msg['ping_time_min']}<br> 平均值:{$msg['ping_time_avg']}<br> 最大值:{$msg['ping_time_max']}";
  110. }
  111. }
  112. Log::warning('【PING】检测'.$ip.'时,api.yum6.cn无结果');
  113. return false; // 发送错误
  114. }
  115. private function toolsdaquan(string $ip, int $port): ?array
  116. { // 开发依据: https://www.toolsdaquan.com/ipcheck/
  117. $response_inner = self::$basicRequest->withHeaders(['Referer' => 'https://www.toolsdaquan.com/ipcheck/'])->get("https://www.toolsdaquan.com/toolapi/public/ipchecking/$ip/$port");
  118. $response_outer = self::$basicRequest->withHeaders(['Referer' => 'https://www.toolsdaquan.com/ipcheck/'])->get("https://www.toolsdaquan.com/toolapi/public/ipchecking2/$ip/$port");
  119. if ($response_inner->ok() && $response_outer->ok()) {
  120. return $this->common_detection($response_inner->json(), $response_outer->json(), $ip);
  121. }
  122. return null;
  123. }
  124. private function common_detection(array $inner, array $outer, string $ip): ?array
  125. {
  126. if (empty($inner) || empty($outer)) {
  127. Log::warning("【阻断检测】检测{$ip}时,接口返回异常");
  128. return null;
  129. }
  130. return [
  131. 'in' => [
  132. 'icmp' => $inner['icmp'] === 'success',
  133. 'tcp' => $inner['tcp'] === 'success',
  134. ],
  135. 'out' => [
  136. 'icmp' => $outer['outside_icmp'] === 'success',
  137. 'tcp' => $outer['outside_tcp'] === 'success',
  138. ],
  139. ];
  140. }
  141. private function akile(string $ip, int $port): ?array
  142. { // 开发依据: https://tools.akile.io/
  143. $response = self::$basicRequest->get("https://tools.akile.io/gping?address=$ip&port=$port");
  144. if ($response->ok()) {
  145. $data = $response->json();
  146. if ($data) {
  147. return [
  148. 'in' => [
  149. 'icmp' => $data['cn_icmp'],
  150. 'tcp' => $data['cn_tcp'],
  151. ],
  152. 'out' => [
  153. 'icmp' => $data['global_icmp'],
  154. 'tcp' => $data['global_tcp'],
  155. ],
  156. ];
  157. }
  158. }
  159. Log::warning("【阻断检测】检测{$ip}时,[akile]接口返回异常");
  160. return null;
  161. }
  162. private function gd(string $ip, int $port): ?array
  163. { // 开发依据: https://ping.gd/
  164. $response = self::$basicRequest->get("https://ping.gd/api/ip-test/$ip:$port");
  165. if ($response->ok()) {
  166. $data = $response->json();
  167. if ($data) {
  168. return [
  169. 'in' => [
  170. 'icmp' => $data[0]['result']['ping_alive'],
  171. 'tcp' => $data[0]['result']['telnet_alive'],
  172. ],
  173. 'out' => [
  174. 'icmp' => $data[1]['result']['ping_alive'],
  175. 'tcp' => $data[1]['result']['telnet_alive'],
  176. ],
  177. ];
  178. }
  179. }
  180. Log::warning("【阻断检测】检测{$ip}时,[ping.gd]接口返回异常");
  181. return null;
  182. }
  183. private function vps234(string $ip): ?array
  184. { // 开发依据: https://www.vps234.com/ipchecker/
  185. $response = self::$basicRequest->asForm()->post('https://www.vps234.com/ipcheck/getdata/', ['ip' => $ip]);
  186. if ($response->ok()) {
  187. $data = $response->json();
  188. if ($data) {
  189. if ($data['error'] === false && $data['data']['success']) {
  190. $result = $data['data']['data'];
  191. return [
  192. 'in' => [
  193. 'icmp' => $result['innerICMP'],
  194. 'tcp' => $result['innerTCP'],
  195. ],
  196. 'out' => [
  197. 'icmp' => $result['outICMP'],
  198. 'tcp' => $result['outTCP'],
  199. ],
  200. ];
  201. }
  202. Log::warning('【阻断检测】检测'.$ip.'时,[vps234]接口返回'.var_export($data, true));
  203. }
  204. Log::warning("【阻断检测】检测{$ip}时, [vps234]接口返回异常");
  205. }
  206. return null;
  207. }
  208. private function flyzy2005(string $ip, int $port): ?array
  209. { // 开发依据: https://www.flyzy2005.cn/tech/ip-check/
  210. $response_inner = self::$basicRequest->get("https://mini.flyzy2005.cn/ip_check.php?ip=$ip&port=$port");
  211. $response_outer = self::$basicRequest->get("https://mini.flyzy2005.cn/ip_check_outside.php?ip=$ip&port=$port");
  212. if ($response_inner->ok() && $response_outer->ok()) {
  213. return $this->common_detection($response_inner->json(), $response_outer->json(), $ip);
  214. }
  215. return null;
  216. }
  217. private function idcoffer(string $ip, int $port): ?array
  218. { // 开发依据: https://www.idcoffer.com/ipcheck
  219. $response_inner = self::$basicRequest->get("https://api.24kplus.com/ipcheck?host=$ip&port=$port");
  220. $response_outer = self::$basicRequest->get("https://api.idcoffer.com/ipcheck?host=$ip&port=$port");
  221. if ($response_inner->ok() && $response_outer->ok()) {
  222. $inner = $response_inner->json();
  223. $outer = $response_outer->json();
  224. if ($inner && $outer) {
  225. if ($inner['code'] && $outer['code']) {
  226. return [
  227. 'in' => [
  228. 'icmp' => $inner['data']['ping'],
  229. 'tcp' => $inner['data']['tcp'],
  230. ],
  231. 'out' => [
  232. 'icmp' => $outer['data']['ping'],
  233. 'tcp' => $outer['data']['tcp'],
  234. ],
  235. ];
  236. }
  237. Log::warning('【阻断检测】检测'.$ip.$port.'时,[idcoffer]接口返回'.var_export($inner, true).PHP_EOL.var_export($outer, true));
  238. }
  239. Log::warning("【阻断检测】检测{$ip}时,[idcoffer]接口返回异常");
  240. }
  241. return null;
  242. }
  243. private function ip112(string $ip, int $port = 443): ?array
  244. { // 开发依据: https://ip112.cn/
  245. $response_inner = self::$basicRequest->asForm()->post('https://api.ycwxgzs.com/ipcheck/index.php', ['ip' => $ip, 'port' => $port]);
  246. $response_outer = self::$basicRequest->asForm()->post('https://api.52bwg.com/ipcheck/ipcheck.php', ['ip' => $ip, 'port' => $port]);
  247. if ($response_inner->ok() && $response_outer->ok()) {
  248. $inner = $response_inner->json();
  249. $outer = $response_outer->json();
  250. if ($inner && $outer) {
  251. return [
  252. 'in' => [
  253. 'icmp' => str_contains($inner['icmp'], 'green'),
  254. 'tcp' => str_contains($inner['tcp'], 'green'),
  255. ],
  256. 'out' => [
  257. 'icmp' => str_contains($outer['icmp'], 'green'),
  258. 'tcp' => str_contains($outer['tcp'], 'green'),
  259. ],
  260. ];
  261. }
  262. }
  263. Log::warning("【阻断检测】检测{$ip}时,[ip112]接口返回异常");
  264. return null;
  265. }
  266. private function upx8(string $ip, int $port = 443): ?array
  267. { // 开发依据: https://blog.upx8.com/ipcha.html
  268. $response_inner = self::$basicRequest->asForm()->post('https://ip.upx8.com/check.php', ['ip' => $ip, 'port' => $port]);
  269. $response_outer = self::$basicRequest->asForm()->post('https://ip.7761.cf/check.php', ['ip' => $ip, 'port' => $port]);
  270. if ($response_inner->ok() && $response_outer->ok()) {
  271. $inner = $response_inner->json();
  272. $outer = $response_outer->json();
  273. if ($inner && $outer) {
  274. return [
  275. 'in' => [
  276. 'icmp' => $inner['icmp'] === '正常',
  277. 'tcp' => $inner['tcp'] === '正常',
  278. ],
  279. 'out' => [
  280. 'icmp' => $outer['icmp'] === '正常',
  281. 'tcp' => $outer['tcp'] === '正常',
  282. ],
  283. ];
  284. }
  285. }
  286. Log::warning("【阻断检测】检测{$ip}时,[upx8]接口返回异常");
  287. return null;
  288. }
  289. private function vps1352(string $ip, int $port): ?array
  290. { // 开发依据: https://www.51vps.info/ipcheck.html https://www.vps1352.com/ipcheck.html 有缺陷api,查不了海外做判断 备用
  291. $response = self::$basicRequest->asForm()->withHeaders(['Referer' => 'https://www.51vps.info'])->post('https://www.vps1352.com/check.php', ['ip' => $ip, 'port' => $port]);
  292. if ($response->ok()) {
  293. $data = $response->json();
  294. if ($data) {
  295. return [
  296. 'in' => [
  297. 'icmp' => $data['icmp'] === '开放',
  298. 'tcp' => $data['tcp'] === '开放',
  299. ],
  300. 'out' => [
  301. 'icmp' => true,
  302. 'tcp' => true,
  303. ],
  304. ];
  305. }
  306. }
  307. Log::warning("【阻断检测】检测{$ip}时, [vps1352.com]接口返回异常");
  308. return null;
  309. }
  310. private function rss(string $ip, int $port): ?array
  311. { // https://ip.rss.ink/index/check
  312. $client = self::$basicRequest->withHeaders(['X-Token' => '5AXfB1xVfuq5hxv4']);
  313. foreach (['in', 'out'] as $type) {
  314. foreach (['icmp', 'tcp'] as $protocol) {
  315. $response = $client->get('https://ip.rss.ink/netcheck/'.($type === 'in' ? 'cn' : 'global')."/api/check/$protocol?ip=$ip".($protocol === 'tcp' ? "&port=$port" : ''));
  316. if ($response->ok()) {
  317. $data = $response->json();
  318. $ret[$type][$protocol] = $data['msg'] === 'success';
  319. }
  320. }
  321. }
  322. if (! isset($ret)) {
  323. Log::warning("【阻断检测】检测{$ip}时, [rss]接口返回异常");
  324. }
  325. return $ret ?? null;
  326. }
  327. }