improve: add 6-method fallback for server IP detection
- Method 1: \['SERVER_ADDR'] from Apache/Nginx (most reliable) - Method 2: /sbin/ip (absolute path, works if ip not in PATH) - Method 3: Parse /proc/net/route for default gateway interface + IP - Method 4: hostname -I (space-separated IPs) - Method 5: ifconfig (fallback for older systems) - Method 6: Socket connection to 8.8.8.8 (pure PHP, no shell commands) Each method validates IP and skips loopback (127.x, ::1, ::ffff:127.x) Fixes 'Unavailable' when shell commands fail or aren't available
This commit is contained in:
parent
1ade027b00
commit
998eb7f2bf
|
|
@ -430,38 +430,125 @@ class SystemOperations
|
|||
|
||||
private function readNetworkStatus(): string
|
||||
{
|
||||
// Primary: get first non-loopback IPv4 from Linux ip command (scope global)
|
||||
$result = $this->safeCommand("ip -4 addr show scope global 2>/dev/null | grep -oP 'inet \K[0-9.]+' | head -1");
|
||||
if ($result !== 'Unavailable' && trim($result) !== '' && filter_var(trim($result), FILTER_VALIDATE_IP)) {
|
||||
return trim($result);
|
||||
// Method 1: $_SERVER['SERVER_ADDR'] — set by Apache/Nginx when available
|
||||
if (!empty($_SERVER['SERVER_ADDR'])) {
|
||||
$ip = $_SERVER['SERVER_ADDR'];
|
||||
if (filter_var($ip, FILTER_VALIDATE_IP) && !$this->isLoopbackIp($ip)) {
|
||||
return $ip;
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback: hostname -I and take first non-loopback IP
|
||||
$result = $this->safeCommand('hostname -I 2>/dev/null');
|
||||
// Method 2: Try /sbin/ip with absolute path (may not be in PATH)
|
||||
$result = $this->safeCommand('/sbin/ip -4 addr show scope global 2>/dev/null | grep -oP "inet \K[0-9.]+"');
|
||||
if ($result !== 'Unavailable' && trim($result) !== '') {
|
||||
$ips = preg_split('/\s+/', trim($result));
|
||||
foreach ($ips as $ip) {
|
||||
$ip = trim($ip);
|
||||
if (filter_var($ip, FILTER_VALIDATE_IP) && !$this->isLoopbackIp($ip)) {
|
||||
return $ip;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Last resort: try gethostbyname but filter out loopback
|
||||
$hostname = gethostname();
|
||||
if ($hostname !== false) {
|
||||
$ip = gethostbyname($hostname);
|
||||
if ($ip !== $hostname && filter_var($ip, FILTER_VALIDATE_IP) && !$this->isLoopbackIp($ip)) {
|
||||
return $ip;
|
||||
// Method 3: Parse /proc/net/route to get gateway interface and its IP
|
||||
if (file_exists('/proc/net/route')) {
|
||||
$defaultIp = $this->getIpFromProcRoute();
|
||||
if ($defaultIp && filter_var($defaultIp, FILTER_VALIDATE_IP) && !$this->isLoopbackIp($defaultIp)) {
|
||||
return $defaultIp;
|
||||
}
|
||||
}
|
||||
|
||||
// Method 4: hostname -I (simple space-separated list)
|
||||
$result = $this->safeCommand('hostname -I');
|
||||
if ($result !== 'Unavailable' && trim($result) !== '') {
|
||||
$ips = preg_split('/\s+/', trim($result));
|
||||
foreach ($ips as $ip) {
|
||||
$ip = trim($ip);
|
||||
if (filter_var($ip, FILTER_VALIDATE_IP) && !$this->isLoopbackIp($ip)) {
|
||||
return $ip;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Method 5: Try ifconfig output (older systems)
|
||||
$result = $this->safeCommand('ifconfig 2>/dev/null | grep -oP "inet \K[0-9.]+" | grep -v "^127\."');
|
||||
if ($result !== 'Unavailable' && trim($result) !== '') {
|
||||
$ips = preg_split('/\s+/', trim($result));
|
||||
foreach ($ips as $ip) {
|
||||
$ip = trim($ip);
|
||||
if (filter_var($ip, FILTER_VALIDATE_IP)) {
|
||||
return $ip;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Method 6: Socket-based approach — connect to external host to get local IP
|
||||
$localIp = $this->getLocalIpViaSocket();
|
||||
if ($localIp && filter_var($localIp, FILTER_VALIDATE_IP) && !$this->isLoopbackIp($localIp)) {
|
||||
return $localIp;
|
||||
}
|
||||
|
||||
return 'Unavailable';
|
||||
}
|
||||
|
||||
private function getIpFromProcRoute(): ?string
|
||||
{
|
||||
$lines = file('/proc/net/route', FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
|
||||
if ($lines === false) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Find line with destination 00000000 (default route)
|
||||
foreach ($lines as $line) {
|
||||
$parts = preg_split('/\s+/', trim($line));
|
||||
if (count($parts) >= 3 && $parts[1] === '00000000') {
|
||||
// parts[0] is interface name
|
||||
$ifname = $parts[0];
|
||||
// Get IP for this interface from /proc/net/arp or /proc/net/dev_snmp6
|
||||
$ip = $this->getIpForInterface($ifname);
|
||||
if ($ip) {
|
||||
return $ip;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private function getIpForInterface(string $ifname): ?string
|
||||
{
|
||||
// Try to get IP by parsing /proc/net/dev output combined with IP resolution
|
||||
$result = $this->safeCommand("ip -4 addr show dev " . escapeshellarg($ifname) . " 2>/dev/null | grep -oP 'inet \K[0-9.]+'");
|
||||
if ($result !== 'Unavailable' && trim($result) !== '') {
|
||||
return trim(preg_split('/\s+/', trim($result))[0] ?? null);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private function getLocalIpViaSocket(): ?string
|
||||
{
|
||||
try {
|
||||
// Create socket and connect to public DNS to determine local IP
|
||||
$sock = @fsockopen('8.8.8.8', 53, $errno, $errstr, 2);
|
||||
if (!$sock) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$localName = @getsockname($sock);
|
||||
@fclose($sock);
|
||||
|
||||
if ($localName) {
|
||||
// getsockname returns array like ['127.0.0.1', 53]
|
||||
return is_array($localName) ? ($localName[0] ?? null) : null;
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
// Socket operation failed, ignore
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private function isLoopbackIp(string $ip): bool
|
||||
{
|
||||
return strpos($ip, '127.') === 0 || $ip === '::1';
|
||||
return strpos($ip, '127.') === 0 || $ip === '::1' || strpos($ip, '::ffff:127.') === 0;
|
||||
}
|
||||
|
||||
private function readInternetStatus(): string
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user