403Webshell
Server IP : 202.61.199.114  /  Your IP : 216.73.217.139
Web Server : nginx/1.22.1
System : Linux de.arni-solutions.de 6.1.0-49-amd64 #1 SMP PREEMPT_DYNAMIC Debian 6.1.174-1 (2026-05-26) x86_64
User : web20 ( 1018)
PHP Version : 8.4.23
Disable Function : NONE
MySQL : OFF  |  cURL : ON  |  WGET : ON  |  Perl : ON  |  Python : OFF  |  Sudo : ON  |  Pkexec : ON
Directory :  /var/www/clients/client2/web21/web/api/lib/

Upload File :
current_dir [ Writeable ] document_root [ Writeable ]

 

Command :


[ Back ]     

Current File : /var/www/clients/client2/web21/web/api/lib/net.php
<?php
/**
 * Netzwerk-Kernbibliothek: Eingabe-Parsing, IP-Klassifizierung, SSRF-Schutz,
 * sicherer Cache und sichere Outbound-Requests zu bekannten Providern.
 *
 * WICHTIG (SSRF): Jede vom Nutzer stammende Ziel-Auflösung MUSS über
 * smi_resolve_public_ips() laufen. Interne/Private/Reservierte Ziele werden
 * konsequent blockiert.
 *
 * @package SafeMyIdent\Api
 */

declare(strict_types=1);

/**
 * Konfiguration lesen (vom WordPress-Plugin geschrieben) inkl. Defaults.
 *
 * @return array<string,mixed>
 */
function smi_config(): array
{
    static $cfg = null;
    if ($cfg !== null) {
        return $cfg;
    }
    $defaults = [
        'cache_ttl'        => 900,          // Sekunden.
        'rate_limit'       => 40,           // Anfragen …
        'rate_window'      => 60,           // … pro Fenster (s).
        'pdf_enabled'      => true,
        'debug'            => false,
        'modules'          => [             // aktivierbare Checks.
            'geo' => true, 'asn' => true, 'rdap' => true, 'dns' => true,
            'blacklist' => true, 'mail' => true, 'url' => true, 'detect' => true,
        ],
        'dnsbl_custom'     => [],           // optionale zusätzliche Zonen (Plugin).
        'keys'             => [ 'abuseipdb' => '', 'ipinfo' => '' ],
        'geo_provider'     => 'ip-api',     // ip-api | ipinfo
        'ipapi_direct'     => false,        // ip-api.com DIREKT (US) abfragen.
        'ipapi_via_gateway'=> true,         // ip-api über Gateway-Proxy (VPN, DSGVO) – Vorrang.
        'route_http_via_gateway' => true,   // RIPEstat/RDAP/Tor-Liste über den Gateway-VPN leiten.
        'route_dns_via_gateway'  => false,  // DNS (Team Cymru/DNSBL/Records) über Gateway-DoH (Endpoint nötig).
        'intel'            => [             // IP-Intel-Gateway (horse.pacim.de).
            'enabled'  => false,
            'base_url' => 'https://horse.pacim.de',
            'api_key'  => '',
        ],
    ];
    // Bevorzugt PHP-Config (nicht web-abrufbar); Fallback JSON (altes Format, ohne Secrets).
    $php  = __DIR__ . '/../config/settings.php';
    $json = __DIR__ . '/../config/settings.json';
    $cfg  = $defaults;
    if (is_file($php)) {
        $dec = @include $php;
        if (is_array($dec)) {
            $cfg = array_replace_recursive($defaults, $dec);
        }
    } elseif (is_file($json)) {
        $raw = @file_get_contents($json);
        $dec = $raw !== false ? json_decode($raw, true) : null;
        if (is_array($dec)) {
            $cfg = array_replace_recursive($defaults, $dec);
        }
    }
    return $cfg;
}

/**
 * Datei-Cache lesen.
 *
 * @param string $key Eindeutiger Schlüssel.
 * @return mixed|null Wert oder null, wenn nicht/abgelaufen.
 */
function smi_cache_get(string $key)
{
    $file = sys_get_temp_dir() . '/smi-cache-' . hash('sha256', $key);
    if (!is_file($file)) {
        return null;
    }
    $raw = @file_get_contents($file);
    $data = $raw !== false ? json_decode($raw, true) : null;
    if (is_array($data) && ($data['exp'] ?? 0) >= time()) {
        return $data['val'] ?? null;
    }
    @unlink($file);
    return null;
}

/**
 * Datei-Cache schreiben.
 *
 * @param string $key Schlüssel.
 * @param mixed  $val Wert.
 * @param int    $ttl Lebensdauer in Sekunden.
 */
function smi_cache_set(string $key, $val, int $ttl): void
{
    $file = sys_get_temp_dir() . '/smi-cache-' . hash('sha256', $key);
    @file_put_contents($file, json_encode(['exp' => time() + $ttl, 'val' => $val]));
}

/**
 * IP-Version bestimmen.
 *
 * @param string $ip IP.
 * @return int 4, 6 oder 0.
 */
function smi_ip_version(string $ip): int
{
    if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) {
        return 4;
    }
    if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) {
        return 6;
    }
    return 0;
}

/**
 * Ist die IP öffentlich routbar? (SSRF-Gate)
 * Blockiert private, reservierte, Loopback, Link-Local, Multicast,
 * IPv4-mapped/compat IPv6 und 0.0.0.0/::.
 *
 * @param string $ip IP.
 * @return bool True = öffentlich & erlaubt.
 */
function smi_ip_is_public(string $ip): bool
{
    if (smi_ip_version($ip) === 0) {
        return false;
    }
    // Kernprüfung: keine privaten, keine reservierten Bereiche.
    if (!filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE)) {
        return false;
    }
    $bin = @inet_pton($ip);
    if ($bin === false) {
        return false;
    }
    // 0.0.0.0 / ::  und Multicast.
    if ($ip === '0.0.0.0' || $ip === '::') {
        return false;
    }
    // IPv4-Multicast 224.0.0.0/4, Broadcast 255.255.255.255.
    if (strlen($bin) === 4) {
        $first = ord($bin[0]);
        if ($first >= 224) { // 224–239 Multicast, 240–255 reserviert/Broadcast.
            return false;
        }
        // 100.64.0.0/10 CGNAT.
        if ($first === 100 && (ord($bin[1]) & 0xC0) === 0x40) {
            return false;
        }
    }
    // IPv6-Spezialfälle.
    if (strlen($bin) === 16) {
        // ff00::/8 Multicast.
        if (ord($bin[0]) === 0xff) {
            return false;
        }
        // ::ffff:0:0/96 IPv4-mapped → auf eingebettete IPv4 prüfen.
        $mapped = substr($bin, 0, 12);
        if ($mapped === "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff") {
            $v4 = inet_ntop(substr($bin, 12, 4));
            return $v4 !== false && smi_ip_is_public($v4);
        }
        // ::/96 IPv4-compat & ::1 werden bereits von NO_RES_RANGE erfasst.
    }
    return true;
}

/**
 * Nutzer-Eingabe klassifizieren und normalisieren.
 * Erkennt: IP (v4/v6), URL, Domain, Hostname. Extrahiert bei URL die Domain.
 *
 * @param string $raw Roh-Eingabe (bereits längenbegrenzt aufrufen).
 * @return array{type:string,ip:?string,domain:?string,url:?string,host:?string,error:?string}
 */
function smi_classify_input(string $raw): array
{
    $out = ['type' => 'invalid', 'ip' => null, 'domain' => null, 'url' => null, 'host' => null, 'error' => null];
    $raw = trim($raw);
    if ($raw === '' || strlen($raw) > 2048) {
        $out['error'] = 'Leere oder zu lange Eingabe.';
        return $out;
    }

    // Reine IP?
    if (smi_ip_version($raw) > 0) {
        $out['type'] = 'ip';
        $out['ip']   = $raw;
        $out['host'] = $raw;
        return $out;
    }
    // IPv6 in Klammern [..]?
    if (preg_match('/^\[([0-9a-f:]+)\]$/i', $raw, $m) && smi_ip_version($m[1]) === 6) {
        $out['type'] = 'ip';
        $out['ip']   = $m[1];
        $out['host'] = $m[1];
        return $out;
    }

    // URL (mit Schema oder erkennbarem Pfad/Schema)?
    $looksUrl = str_contains($raw, '://') || preg_match('#^[a-z][a-z0-9+.\-]*://#i', $raw);
    if ($looksUrl) {
        $scheme = strtolower((string) parse_url($raw, PHP_URL_SCHEME));
        if (!in_array($scheme, ['http', 'https'], true)) {
            $out['error'] = 'Nur http/https-URLs werden geprüft.';
            return $out;
        }
        $host = (string) parse_url($raw, PHP_URL_HOST);
        $host = trim($host, '[]');
        if ($host === '') {
            $out['error'] = 'URL ohne Host.';
            return $out;
        }
        $out['type'] = 'url';
        $out['url']  = $raw;
        $out['host'] = $host;
        if (smi_ip_version($host) > 0) {
            $out['ip'] = $host;
        } else {
            $out['domain'] = smi_valid_domain($host);
        }
        return $out;
    }

    // Sonst: Domain/Hostname.
    $host = strtolower($raw);
    // evtl. Pfad ohne Schema (example.com/foo) → Host abschneiden.
    if (str_contains($host, '/')) {
        $host = explode('/', $host, 2)[0];
    }
    $host = trim($host, '.');
    $dom  = smi_valid_domain($host);
    if ($dom !== null) {
        $out['type']   = 'domain';
        $out['domain'] = $dom;
        $out['host']   = $dom;
        return $out;
    }
    $out['error'] = 'Eingabe ist keine gültige IP, Domain oder URL.';
    return $out;
}

/**
 * Hostname SSRF-sicher zu ÖFFENTLICHEN IPs auflösen.
 * Gibt nur öffentlich routbare A/AAAA zurück; leer, wenn nur interne Ziele.
 *
 * @param string $host Hostname oder IP.
 * @return array{ipv4:string[],ipv6:string[],all:string[]}
 */
function smi_resolve_public_ips(string $host): array
{
    $res = ['ipv4' => [], 'ipv6' => [], 'all' => []];
    if (smi_ip_version($host) > 0) {
        if (smi_ip_is_public($host)) {
            $key = smi_ip_version($host) === 6 ? 'ipv6' : 'ipv4';
            $res[$key][] = $host;
            $res['all'][] = $host;
        }
        return $res;
    }
    $a    = smi_dns($host, DNS_A);
    $aaaa = smi_dns($host, DNS_AAAA);
    foreach ($a as $r) {
        $ip = $r['ip'] ?? '';
        if ($ip !== '' && smi_ip_is_public($ip) && !in_array($ip, $res['ipv4'], true)) {
            $res['ipv4'][] = $ip;
        }
    }
    foreach ($aaaa as $r) {
        $ip = $r['ipv6'] ?? '';
        if ($ip !== '' && smi_ip_is_public($ip) && !in_array($ip, $res['ipv6'], true)) {
            $res['ipv6'][] = $ip;
        }
    }
    $res['all'] = array_merge($res['ipv4'], $res['ipv6']);
    return $res;
}

/**
 * DNS-Flag → Typ-String.
 *
 * @param int $flag DNS_A etc.
 * @return string|null
 */
function smi_dns_flag_to_type(int $flag): ?string
{
    $map = [DNS_A => 'A', DNS_AAAA => 'AAAA', DNS_MX => 'MX', DNS_TXT => 'TXT', DNS_NS => 'NS', DNS_CNAME => 'CNAME', DNS_SOA => 'SOA', DNS_PTR => 'PTR'];
    return $map[$flag] ?? null;
}

/**
 * Gateway-DNS-Records → dns_get_record()-kompatibles Format mappen.
 *
 * @param array<int,array<string,mixed>> $records Gateway-Records.
 * @param string                         $type    Typ.
 * @return array<int,array<string,mixed>>
 */
function smi_dns_map_gateway(array $records, string $type): array
{
    $out = [];
    foreach ($records as $r) {
        $rt  = strtoupper((string) ($r['type'] ?? $type));
        $val = $r['value'] ?? $r['data'] ?? $r['target'] ?? $r['address'] ?? '';
        $ttl = isset($r['ttl']) ? (int) $r['ttl'] : null;
        switch ($rt) {
            case 'A':     $out[] = ['type' => 'A', 'ip' => $val, 'ttl' => $ttl]; break;
            case 'AAAA':  $out[] = ['type' => 'AAAA', 'ipv6' => $val, 'ttl' => $ttl]; break;
            case 'MX':    $out[] = ['type' => 'MX', 'pri' => (int) ($r['priority'] ?? $r['pri'] ?? 0), 'target' => rtrim((string) $val, '.'), 'ttl' => $ttl]; break;
            case 'NS':    $out[] = ['type' => 'NS', 'target' => rtrim((string) $val, '.'), 'ttl' => $ttl]; break;
            case 'CNAME': $out[] = ['type' => 'CNAME', 'target' => rtrim((string) $val, '.'), 'ttl' => $ttl]; break;
            case 'PTR':   $out[] = ['type' => 'PTR', 'target' => rtrim((string) $val, '.'), 'ttl' => $ttl]; break;
            case 'TXT':   $out[] = ['type' => 'TXT', 'txt' => (string) $val, 'entries' => [(string) $val], 'ttl' => $ttl]; break;
            case 'SOA':   $out[] = ['type' => 'SOA', 'mname' => (string) ($r['mname'] ?? ''), 'rname' => (string) ($r['rname'] ?? ''), 'serial' => (int) ($r['serial'] ?? 0), 'refresh' => (int) ($r['refresh'] ?? 0), 'retry' => (int) ($r['retry'] ?? 0), 'expiry' => (int) ($r['expire'] ?? $r['expiry'] ?? 0), 'minimum-ttl' => (int) ($r['minimum'] ?? 0), 'ttl' => $ttl]; break;
            default: break;
        }
    }
    return $out;
}

/**
 * Typ-String → DNS-Flag.
 *
 * @param string $type Typ.
 * @return int
 */
function smi_dns_type_to_flag(string $type): int
{
    $map = ['A' => DNS_A, 'AAAA' => DNS_AAAA, 'MX' => DNS_MX, 'TXT' => DNS_TXT, 'NS' => DNS_NS, 'CNAME' => DNS_CNAME, 'SOA' => DNS_SOA, 'PTR' => DNS_PTR];
    return $map[strtoupper($type)] ?? DNS_A;
}

/**
 * Mehrere DNS-Abfragen auf einmal – über den Gateway-Batch (1 Round-Trip) oder direkt.
 * Gibt ein Array in gleicher Reihenfolge zurück (je Query die Records im
 * dns_get_record-Format).
 *
 * @param array<int,array{name:string,type:string|int}> $queries Abfragen.
 * @return array<int,array<int,array<string,mixed>>>
 */
function smi_dns_many(array $queries): array
{
    if (!$queries) {
        return [];
    }
    $cfg = smi_config();
    if (!empty($cfg['route_dns_via_gateway'])
        && function_exists('smi_gateway_dns_batch') && function_exists('smi_intel_enabled') && smi_intel_enabled()) {
        $gwq = [];
        foreach ($queries as $q) {
            $type  = is_int($q['type']) ? smi_dns_flag_to_type($q['type']) : strtoupper((string) $q['type']);
            $gwq[] = ['name' => $q['name'], 'type' => $type ?: 'A'];
        }
        $batch = smi_gateway_dns_batch($gwq);
        if (is_array($batch)) {
            $out = [];
            foreach ($queries as $i => $q) {
                $type  = is_int($q['type']) ? smi_dns_flag_to_type($q['type']) : strtoupper((string) $q['type']);
                $out[] = smi_dns_map_gateway(is_array($batch[$i] ?? null) ? $batch[$i] : [], $type ?: 'A');
            }
            return $out;
        }
    }
    // Fallback: einzeln direkt.
    $out = [];
    foreach ($queries as $q) {
        $flag  = is_int($q['type']) ? $q['type'] : smi_dns_type_to_flag((string) $q['type']);
        $out[] = @dns_get_record($q['name'], $flag) ?: [];
    }
    return $out;
}

/**
 * DNS-Abfrage – optional über den Gateway-VPN (DoH), sonst direkt.
 * Drop-in-Ersatz für dns_get_record() mit EINEM Typ-Flag.
 *
 * @param string $name Name.
 * @param int    $flag DNS_A etc.
 * @return array<int,array<string,mixed>>
 */
function smi_dns(string $name, int $flag): array
{
    $cfg = smi_config();
    if (!empty($cfg['route_dns_via_gateway'])
        && function_exists('smi_gateway_dns') && function_exists('smi_intel_enabled') && smi_intel_enabled()) {
        $type = smi_dns_flag_to_type($flag);
        if ($type !== null) {
            $gw = smi_gateway_dns($name, $type);
            if (is_array($gw)) {              // Erfolg (auch leer = NXDOMAIN).
                return smi_dns_map_gateway($gw, $type);
            }
            // null = Transportfehler → direkter Fallback.
        }
    }
    return @dns_get_record($name, $flag) ?: [];
}

/**
 * Reverse-DNS (PTR) einer IP.
 *
 * @param string $ip IP.
 * @return string Hostname oder ''.
 */
function smi_reverse_dns(string $ip): string
{
    if (smi_ip_version($ip) === 0) {
        return '';
    }
    $host = @gethostbyaddr($ip);
    if ($host === false || $host === $ip) {
        return '';
    }
    return rtrim(strtolower($host), '.');
}

/**
 * Sicherer Outbound-GET zu einem BEKANNTEN Provider-Host (kein Nutzer-Ziel).
 * Mit UA, Timeout, Größenlimit. Gibt Body oder '' zurück.
 *
 * @param string $url    Vollständige URL (Provider).
 * @param int    $timeout Sekunden.
 * @param array<string,string> $headers Zusätzliche Header.
 * @return string
 */
function smi_provider_get(string $url, int $timeout = 6, array $headers = []): string
{
    // Optional über den Gateway-VPN leiten (RIPEstat/RDAP/Tor). Bei Fehler → direkter Fallback.
    $cfg = smi_config();
    if (!empty($cfg['route_http_via_gateway'])
        && function_exists('smi_gateway_fetch') && function_exists('smi_intel_enabled') && smi_intel_enabled()) {
        $body = smi_gateway_fetch($url);
        if ($body !== '') {
            return substr($body, 0, 1_000_000);
        }
        // sonst weiter mit direktem Abruf.
    }
    $hdr = ['User-Agent: SafeMyIdent-IPCheck/1.0 (+https://www.safemyident.de/tools/ip-check/)', 'Accept: application/json'];
    foreach ($headers as $k => $v) {
        $hdr[] = $k . ': ' . $v;
    }
    if (function_exists('curl_init')) {
        $ch = curl_init($url);
        curl_setopt_array($ch, [
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_CONNECTTIMEOUT => $timeout,
            CURLOPT_TIMEOUT        => $timeout,
            CURLOPT_FOLLOWLOCATION => true,
            CURLOPT_MAXREDIRS      => 3,
            CURLOPT_HTTPHEADER     => $hdr,
            CURLOPT_SSL_VERIFYPEER => true,
            CURLOPT_SSL_VERIFYHOST => 2,
            CURLOPT_ENCODING       => '',
        ]);
        $body = curl_exec($ch);
        curl_close($ch);
        return is_string($body) ? substr($body, 0, 1_000_000) : '';
    }
    $ctx = stream_context_create(['http' => [
        'method' => 'GET', 'timeout' => $timeout, 'header' => implode("\r\n", $hdr), 'follow_location' => 1, 'max_redirects' => 3,
    ]]);
    $body = @file_get_contents($url, false, $ctx);
    return is_string($body) ? substr($body, 0, 1_000_000) : '';
}

Youez - 2016 - github.com/yon3zu
LinuXploit