Browse Source

refactor: new NTP check model

dev
Dnomd343 3 years ago
parent
commit
8da689fbc8
  1. 27
      functions/Curl.php
  2. 4
      functions/DNS.php
  3. 11
      functions/Domain.php
  4. 206
      models/ntpCheck.php

27
functions/Curl.php

@ -4,11 +4,30 @@ class Curl {
public $ua = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.131 Safari/537.36 Edg/92.0.902.67'; public $ua = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.131 Safari/537.36 Edg/92.0.902.67';
public function get($url, $timeOut = 30) { // curl模拟Get 默认30s超时 public function get($url, $timeOut = 30) { // curl模拟Get 默认30s超时
return $this->run(array(
[CURLOPT_URL, $url],
[CURLOPT_RETURNTRANSFER, 1],
[CURLOPT_CONNECTTIMEOUT, $timeOut],
[CURLOPT_USERAGENT, $this->ua]
));
}
public function post($url, $data, $timeOut = 30) { // curl模拟Post 默认30s超时
return $this->run(array(
[CURLOPT_URL, $url],
[CURLOPT_RETURNTRANSFER, 1],
[CURLOPT_CONNECTTIMEOUT, $timeOut],
[CURLOPT_USERAGENT, $this->ua],
[CURLOPT_POST, 1],
[CURLOPT_POSTFIELDS, $data]
));
}
private function run($configs) { // 发起curl请求
$curl = curl_init(); $curl = curl_init();
curl_setopt($curl, CURLOPT_URL, $url); foreach ($configs as $config) {
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1); curl_setopt($curl, $config[0], $config[1]);
curl_setopt($curl, CURLOPT_CONNECTTIMEOUT, $timeOut); }
curl_setopt($curl, CURLOPT_USERAGENT, $this->ua);
$content = curl_exec($curl); $content = curl_exec($curl);
curl_close($curl); curl_close($curl);
return $content; return $content;

4
functions/DNS.php

@ -40,7 +40,7 @@ class DNS {
return $ipAddr; return $ipAddr;
} }
private function ip2long6($ipv6) { // 压缩IPv6地址为long public function ip2long6($ipv6) { // 压缩IPv6地址为long
$ip_n = inet_pton($ipv6); $ip_n = inet_pton($ipv6);
$bits = 15; $bits = 15;
while ($bits >= 0) { while ($bits >= 0) {
@ -51,7 +51,7 @@ class DNS {
return gmp_strval(gmp_init($ipv6long, 2), 10); return gmp_strval(gmp_init($ipv6long, 2), 10);
} }
private function long2ip6($ipv6long) { // 解压long为IPv6地址 public function long2ip6($ipv6long) { // 解压long为IPv6地址
$bin = gmp_strval(gmp_init($ipv6long, 10), 2); $bin = gmp_strval(gmp_init($ipv6long, 10), 2);
if (strlen($bin) < 128) { if (strlen($bin) < 128) {
$pad = 128 - strlen($bin); $pad = 128 - strlen($bin);

11
functions/Domain.php

@ -5,6 +5,17 @@ class Domain { // 域名相关功能
preg_match('/^(?=^.{3,255}$)[a-zA-Z0-9][-a-zA-Z0-9]{0,62}(\.[a-zA-Z0-9][-a-zA-Z0-9]{0,62})+$/', $domain, $match); preg_match('/^(?=^.{3,255}$)[a-zA-Z0-9][-a-zA-Z0-9]{0,62}(\.[a-zA-Z0-9][-a-zA-Z0-9]{0,62})+$/', $domain, $match);
return (count($match) != 0); return (count($match) != 0);
} }
public function isHost($host) { // 判断host是否合法
preg_match('/^(?=^.{3,255}$)[a-zA-Z0-9][-a-zA-Z0-9]{0,62}(\.[a-zA-Z0-9][-a-zA-Z0-9]{0,62})+$/', $host, $match);
if (count($match) !== 0) { // 域名
if (!is_numeric(substr($host, -1))) { return true; } // 域名最后一位不为数字
}
if (filter_var($host, FILTER_VALIDATE_IP)) { // IP地址
return true;
}
return false;
}
} }
class extractDomain { class extractDomain {

206
models/ntpCheck.php

@ -1,28 +1,28 @@
<?php <?php
class ntpList { class ntpServer { // 获取NTP服务器列表
private $ntpDB = './db/ntpServer.db'; // NTP服务器数据库 private $ntpDB = './db/ntpServer.db'; // NTP服务器数据库
private function getListName($list_id) { // 获取对应组的名称 private function getListName($listId) { // 获取对应组的名称
$db = new SqliteDB($this->ntpDB); $db = new SqliteDB($this->ntpDB);
$res = $db->query('SELECT * FROM `ntp_list` WHERE id=' . $list_id . ';'); $res = $db->query('SELECT * FROM `ntp_list` WHERE id=' . $listId . ';');
return $res->fetchArray(SQLITE3_ASSOC)['name']; return $res->fetchArray(SQLITE3_ASSOC)['name'];
} }
public function getNtpList() { // 获取所有NTP服务器地址 public function getList() { // 获取所有NTP服务器地址
$db = new SqliteDB($this->ntpDB); $db = new SqliteDB($this->ntpDB);
$res = $db->query('SELECT * FROM `ntp_host`;'); $res = $db->query('SELECT * FROM `ntp_host`;');
while ($row = $res->fetchArray(SQLITE3_ASSOC)) { while ($row = $res->fetchArray(SQLITE3_ASSOC)) {
$index = $row['list_id']; $index = $row['list_id'];
unset($row['list_id']); unset($row['list_id']);
$data[$this->getListName($index)][] = $row; $list[$this->getListName($index)][] = $row;
} }
return $data; return $list;
} }
} }
class ntpCheck { class ntpCheck { // NTP服务器检查
private function formatOffset($str) { // 格式化Offset private function formatOffset($str) { // 格式化偏移时间
$num = number_format($str, 6) * 1000; // s -> ms $num = number_format($str, 6) * 1000; // s -> ms
$str = sprintf("%1\$.3f", $num); // 补零到小数点后3位 $str = sprintf("%1\$.3f", $num); // 补零到小数点后3位
if ($num > 0) { if ($num > 0) {
@ -31,23 +31,61 @@ class ntpCheck {
return $str . 'ms'; return $str . 'ms';
} }
private function curlPost($url, $data) { // curl模拟post操作 40s超时 private function sortByIp($servers) { // 排序算法
$curl = curl_init(); $temp = array();
curl_setopt($curl, CURLOPT_URL, $url); foreach ($servers as $val){
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1); $temp[] = $val['Server'];
curl_setopt($curl, CURLOPT_CONNECTTIMEOUT, 40); }
curl_setopt($curl, CURLOPT_POST, 1); sort($temp);
curl_setopt($curl, CURLOPT_POSTFIELDS, $data); $temp = array_flip($temp);
$content = curl_exec($curl); $sort = array();
curl_close($curl); foreach ($servers as $val) {
return $content; $temp_1 = $val['Server'];
$temp_2 = $temp[$temp_1];
$sort[$temp_2] = $val;
}
asort($sort);
return $sort;
}
private function sortServer($servers) { // 按顺序排列服务器
foreach ($servers as $server) {
if(filter_var($server['Server'], FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) {
$ipv4[] = $server; // 提取IPv4服务器
}
if(filter_var($server['Server'], FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) {
$ipv6[] = $server; // 提取IPv6服务器
}
}
if (isset($ipv4)) { // 存在IPv4服务器
foreach ($ipv4 as $index => $ip) {
$ipv4[$index]['Server'] = ip2long($ip['Server']); // IPv4预处理
}
$ipv4 = $this->sortByIp($ipv4); // 排序IPv4服务器
foreach ($ipv4 as $index => $ip) {
$ip['Server'] = long2ip($ip['Server']); // IPv4恢复
$result[] = $ip;
}
}
if (isset($ipv6)) { // 存在IPv6服务器
foreach ($ipv6 as $index => $ip) {
$ipv6[$index]['Server'] = (new DNS)->ip2long6($ip['Server']); // IPv6预处理
}
$ipv6 = $this->sortByIp($ipv6); // 排序IPv6服务器
foreach ($ipv6 as $index => $ip) {
$ip['Server'] = (new DNS)->long2ip6($ip['Server']); // IPv6恢复
$result[] = $ip;
}
}
return (!isset($result)) ? array() : $result; // 无结果 返回空数组
} }
private function getNtpStatus($host) { // 获取NTP服务器状态 private function getNtpStatus($host) { // 获取NTP服务器状态
$html = $this->curlPost('https://servertest.online/ntp', array( $html = (new Curl)->post('https://servertest.online/ntp', array(
'a' => $host, 'a' => $host,
'c' => 'Query+both' 'c' => 'Query+both'
)); ));
if ($html == '') { return null; } // 服务错误
preg_match('/<\/form>[\s\S]+<footer>/', $html, $match); // 切取数据部分 preg_match('/<\/form>[\s\S]+<footer>/', $html, $match); // 切取数据部分
preg_match('/<label>[\s\S]+<footer>/', $match[0], $match); // 去除前部干扰 preg_match('/<label>[\s\S]+<footer>/', $match[0], $match); // 去除前部干扰
$match = substr($match[0], 0, strlen($match[0]) - 8); // 去除后部<footer> $match = substr($match[0], 0, strlen($match[0]) - 8); // 去除后部<footer>
@ -83,15 +121,23 @@ class ntpCheck {
$group['Offset'] = $this->formatOffset($group['Offset']); // 转换为ms $group['Offset'] = $this->formatOffset($group['Offset']); // 转换为ms
$data[] = $group; $data[] = $group;
} }
return ($data === null) ? array() : $data; $data = (!isset($data)) ? array() : $data; // 无结果时为空数组
return $this->sortServer($data); // 排序后返回
}
public function isCache($host) { // 检查NTP服务器是否已缓存
$redis = new RedisCache('ntp');
$servers = $redis->getData($host);
return (!$servers) ? false : true;
} }
public function ntpStatus($host) { // 检测NTP服务器状态 带缓存 public function ntpStatus($host) { // 检测NTP服务器状态
$redis = new redisCache('ntp'); $redis = new RedisCache('ntp');
$servers = $redis->getData($host); // 查询缓存数据 $servers = $redis->getData($host); // 查询缓存数据
if (!$servers) { // 缓存未命中 if (!$servers) { // 缓存未命中
$servers = $this->getNtpStatus($host); // 发起测试 $servers = $this->getNtpStatus($host); // 发起测试
$redis->setData($host, json_encode($servers), 300); // 缓存5min if ($servers === null) { return null; } // 服务接口错误
$redis->setData($host, json_encode($servers), 900); // 缓存15min
} else { // 缓存命中 } else { // 缓存命中
$servers = json_decode($servers, true); // 使用缓存数据 $servers = json_decode($servers, true); // 使用缓存数据
} }
@ -99,24 +145,13 @@ class ntpCheck {
} }
} }
class ntpCheckEntry { class ntpCheckEntry { // NTP功能入口
private function isHost($host) { // 判断host是否合法
preg_match('/^(?=^.{3,255}$)[a-zA-Z0-9][-a-zA-Z0-9]{0,62}(\.[a-zA-Z0-9][-a-zA-Z0-9]{0,62})+$/', $host, $match);
if (count($match) !== 0) { // 域名
if (!is_numeric(substr($host, -1))) { return true; } // 域名最后一位不为数字
}
if (filter_var($host, FILTER_VALIDATE_IP)) { // IP地址
return true;
}
return false;
}
private function hashGroupName($str) { // 计算组名的哈希值 取前12位 private function hashGroupName($str) { // 计算组名的哈希值 取前12位
return substr(md5($str), 0, 12); return substr(md5($str), 0, 12);
} }
private function showList() { // 列出所有NTP服务器组 private function showNtpList() { // 列出所有NTP服务器组
$ntpList = (new ntpList)->getNtpList(); $ntpList = (new ntpServer)->getList();
foreach ($ntpList as $index => $ntpHosts) { foreach ($ntpList as $index => $ntpHosts) {
$buttons[] = array([ // 生成按钮列表 $buttons[] = array([ // 生成按钮列表
'text' => $index, 'text' => $index,
@ -125,29 +160,29 @@ class ntpCheckEntry {
} }
return array( return array(
'text' => 'Which one did you like?', 'text' => 'Which one did you like?',
'reply_markup' => json_encode(array( // 列表按钮 'reply_markup' => json_encode(array( // 按钮列表
'inline_keyboard' => $buttons 'inline_keyboard' => $buttons
)) ))
); );
} }
private function showNtpServer($targetGroup) { // 列出指定组的NTP服务器 private function showNtpServer($targetGroup) { // 列出指定组的NTP服务器
$ntpList = (new ntpList)->getNtpList(); $ntpList = (new ntpServer)->getList();
foreach ($ntpList as $index => $ntpHosts) { foreach ($ntpList as $index => $ntpHosts) { // 搜索目标组
if ($this->hashGroupName($index) === $targetGroup) { break; } if ($this->hashGroupName($index) === $targetGroup) { break; }
} }
$msg = '*' . $index . '*' . PHP_EOL; $msg = '*' . $index . '*' . PHP_EOL; // 显示组名
foreach ($ntpHosts as $ntpHost) { foreach ($ntpHosts as $ntpHost) {
if ($ntpHost['desc'] !== '') { if ($ntpHost['desc'] !== '') {
$msg .= $ntpHost['desc'] . ':'; $msg .= $ntpHost['desc'] . ':'; // 服务器描述
} }
$msg .= '`' . $ntpHost['host'] . '`' . PHP_EOL; $msg .= '`' . $ntpHost['host'] . '`' . PHP_EOL; // 服务器地址
} }
return array( return array(
'parse_mode' => 'Markdown',
'text' => $msg, 'text' => $msg,
'parse_mode' => 'Markdown',
'reply_markup' => json_encode(array( 'reply_markup' => json_encode(array(
'inline_keyboard' => array([[ 'inline_keyboard' => array([[ // 返回上一级 按钮
'text' => ' << Go back <<', 'text' => ' << Go back <<',
'callback_data' => '/ntp servers' 'callback_data' => '/ntp servers'
]]) ]])
@ -155,65 +190,76 @@ class ntpCheckEntry {
); );
} }
private function checkNtp($host) { // 检查NTP服务器状态 private function genMessage($host, $servers) { // 生成返回信息
$servers = (new ntpCheck)->ntpStatus($host); if (count($servers) === 0) { // NTP服务不可用
if (count($servers) === 0) {
$msg = '`' . $host . '`' . PHP_EOL; $msg = '`' . $host . '`' . PHP_EOL;
$msg .= 'NTP Server *Offline*' . PHP_EOL . PHP_EOL; $msg .= 'NTP Server *Offline*' . PHP_EOL . PHP_EOL;
return array( return $msg;
'parse_mode' => 'Markdown',
'text' => $msg
);
} }
$msg = '`' . $host . '`' . PHP_EOL; $msg = '`' . $host . '`' . PHP_EOL;
$msg .= 'NTP Server *Normally*' . PHP_EOL . PHP_EOL; $msg .= 'NTP Server *Normally*' . PHP_EOL . PHP_EOL; // NTP服务器正常
foreach ($servers as $server) { foreach ($servers as $server) { // 显示所有服务器
$msg .= '`' . $server['Server'] . '`' . PHP_EOL; $msg .= '`' . $server['Server'] . '`' . PHP_EOL;
$msg .= '_Stratum:_ ' . $server['Stratum'] . PHP_EOL; $msg .= '_Stratum:_ ' . $server['Stratum'] . PHP_EOL;
$msg .= '_Offset:_ ' . $server['Offset'] . PHP_EOL; $msg .= '_Offset:_ ' . $server['Offset'] . PHP_EOL;
$msg .= PHP_EOL; $msg .= PHP_EOL;
} }
return array( return $msg;
}
private function sendNtpStatus($host) { // 检查并发送NTP服务器状态
if ((new ntpCheck)->isCache($host)) { // 状态已缓存
$servers = (new ntpCheck)->ntpStatus($host);
tgApi::sendMarkdown($this->genMessage($host, $servers)); // 查询并发送
return;
}
$message = tgApi::sendMarkdown('`' . $host . '`' . PHP_EOL . 'NTP Server Checking...');
$messageId = json_decode($message, true)['result']['message_id']; // 未缓存 发送缓冲消息
$servers = (new ntpCheck)->ntpStatus($host); // 发起查询
if ($servers === null) { // 上游接口错误
$message = 'Server error';
} else {
$message = $this->genMessage($host, $servers); // 生成返回信息
}
tgApi::editMessage(array( // 返回查询结果
'text' => $message,
'parse_mode' => 'Markdown',
'message_id' => $messageId
));
}
private function sendHelp() { // 显示使用说明
$helpMessage = array(
'parse_mode' => 'Markdown', 'parse_mode' => 'Markdown',
'text' => $msg 'text' => '*Usage:* `/ntp IP/Domain`',
'reply_markup' => json_encode(array( // 显示 NTP服务器列表 按钮
'inline_keyboard' => array([[
'text' => 'Get NTP Servers',
'callback_data' => '/ntp servers'
]])
))
); );
tgApi::sendMessage($helpMessage); // 发送使用说明
} }
public function query($rawParam) { // NTP测试查询入口 public function query($rawParam) { // NTP测试查询入口
if ($rawParam == '' || $rawParam === 'help') { // 显示使用说明 if ($rawParam == '' || $rawParam === 'help') {
tgApi::sendMessage(array( $this->sendHelp(); // 显示使用说明
'parse_mode' => 'Markdown', } else if (!(new Domain)->isHost($rawParam)) {
'text' => '*Usage:* `/ntp IP/Domain`',
'reply_markup' => json_encode(array( // 获取NTP服务列表
'inline_keyboard' => array([[
'text' => 'Get NTP Servers',
'callback_data' => '/ntp servers'
]])
))
));
return;
}
if (!$this->isHost($rawParam)) {
tgApi::sendText('Illegal host'); // 输入错误 tgApi::sendText('Illegal host'); // 输入错误
return; } else {
$this->sendNtpStatus($rawParam); // 检查并发送NTP服务器状态
} }
$message = tgApi::sendMarkdown('`' . $rawParam . '`' . PHP_EOL . 'NTP Server Checking...');
$message = json_decode($message, true);
fastcgi_finish_request(); // 断开连接
tgApi::editMessage(array(
'message_id' => $message['result']['message_id'],
) + $this->checkNtp($rawParam)); // 发起查询并返回结果
} }
public function callback($rawParam) { // NTP测试回调入口 public function callback($rawParam) { // NTP测试回调入口
global $tgEnv;
if ($rawParam === 'servers') { if ($rawParam === 'servers') {
$content = $this->showList(); // 显示可选组 $content = $this->showNtpList(); // 显示可选组
} else { } else {
$content = $this->showNtpServer($rawParam); // 显示指定组的服务器列表 $content = $this->showNtpServer($rawParam); // 显示指定组的服务器列表
} }
tgApi::editMessage(array( tgApi::editMessage(array(
'message_id' => $tgEnv['messageId'] 'message_id' => $GLOBALS['tgEnv']['messageId']
) + $content); // 输出结果 ) + $content); // 输出结果
} }
} }

Loading…
Cancel
Save