ip resolve local over whois
This commit is contained in:
@@ -1045,6 +1045,10 @@ _ip_info_cache: Dict[str, Dict[str, Any]] = {}
|
||||
_ip_api_last_request = 0.0
|
||||
_ip_api_request_count = 0
|
||||
|
||||
# WHOIS Cache (1 Stunde TTL)
|
||||
WHOIS_CACHE_TTL = 3600 # 1 Stunde
|
||||
_whois_cache: Dict[str, Dict[str, Any]] = {}
|
||||
|
||||
# Global Country Ranges Cache (loaded once from ipdeny.com files)
|
||||
_country_ranges_cache: Dict[str, List[tuple]] = {} # country -> [(network_int, mask_int), ...]
|
||||
_country_cache_loaded = False
|
||||
@@ -1202,6 +1206,112 @@ def get_cached_ip_info(ip: str) -> Optional[Dict[str, Any]]:
|
||||
return _ip_info_cache.get(ip)
|
||||
|
||||
|
||||
def whois_lookup(ip: str) -> Dict[str, Any]:
|
||||
"""
|
||||
Führt WHOIS-Lookup für eine IP durch (lokales whois-Tool).
|
||||
Cached Ergebnisse für 1 Stunde.
|
||||
|
||||
Returns:
|
||||
Dict mit: netname, org, asn, country, abuse, range, raw
|
||||
"""
|
||||
global _whois_cache
|
||||
|
||||
# Cache prüfen
|
||||
if ip in _whois_cache:
|
||||
cached = _whois_cache[ip]
|
||||
if time.time() - cached.get('_cached_at', 0) < WHOIS_CACHE_TTL:
|
||||
return cached
|
||||
|
||||
result = {
|
||||
'ip': ip,
|
||||
'netname': '',
|
||||
'org': '',
|
||||
'asn': '',
|
||||
'country': '',
|
||||
'abuse': '',
|
||||
'range': '',
|
||||
'descr': '',
|
||||
'raw': '',
|
||||
'_cached_at': time.time()
|
||||
}
|
||||
|
||||
try:
|
||||
# WHOIS-Kommando ausführen
|
||||
proc = subprocess.run(
|
||||
['whois', ip],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
timeout=10
|
||||
)
|
||||
|
||||
if proc.returncode != 0:
|
||||
logger.debug(f"WHOIS fehlgeschlagen für {ip}: {proc.stderr}")
|
||||
_whois_cache[ip] = result
|
||||
return result
|
||||
|
||||
raw_output = proc.stdout
|
||||
result['raw'] = raw_output
|
||||
|
||||
# Parsing - verschiedene WHOIS-Formate unterstützen (RIPE, ARIN, APNIC, etc.)
|
||||
lines = raw_output.split('\n')
|
||||
|
||||
for line in lines:
|
||||
line_lower = line.lower().strip()
|
||||
|
||||
# Netname
|
||||
if line_lower.startswith('netname:'):
|
||||
result['netname'] = line.split(':', 1)[1].strip()
|
||||
|
||||
# Organisation
|
||||
elif line_lower.startswith('org-name:') or line_lower.startswith('orgname:'):
|
||||
result['org'] = line.split(':', 1)[1].strip()
|
||||
elif line_lower.startswith('organization:') and not result['org']:
|
||||
result['org'] = line.split(':', 1)[1].strip()
|
||||
elif line_lower.startswith('descr:') and not result['org']:
|
||||
# Fallback zu descr wenn keine org gefunden
|
||||
val = line.split(':', 1)[1].strip()
|
||||
if val and not result['descr']:
|
||||
result['descr'] = val
|
||||
|
||||
# ASN
|
||||
elif line_lower.startswith('origin:') or line_lower.startswith('originas:'):
|
||||
result['asn'] = line.split(':', 1)[1].strip()
|
||||
|
||||
# Country
|
||||
elif line_lower.startswith('country:') and not result['country']:
|
||||
result['country'] = line.split(':', 1)[1].strip().upper()
|
||||
|
||||
# Abuse Contact
|
||||
elif 'abuse' in line_lower and '@' in line:
|
||||
# E-Mail aus der Zeile extrahieren
|
||||
import re
|
||||
email_match = re.search(r'[\w\.-]+@[\w\.-]+\.\w+', line)
|
||||
if email_match and not result['abuse']:
|
||||
result['abuse'] = email_match.group(0)
|
||||
|
||||
# IP Range (inetnum/NetRange)
|
||||
elif line_lower.startswith('inetnum:') or line_lower.startswith('netrange:'):
|
||||
result['range'] = line.split(':', 1)[1].strip()
|
||||
elif line_lower.startswith('cidr:') and not result['range']:
|
||||
result['range'] = line.split(':', 1)[1].strip()
|
||||
|
||||
# Fallback: descr als org wenn org leer
|
||||
if not result['org'] and result['descr']:
|
||||
result['org'] = result['descr']
|
||||
|
||||
logger.debug(f"WHOIS für {ip}: netname={result['netname']}, org={result['org']}, asn={result['asn']}")
|
||||
|
||||
except subprocess.TimeoutExpired:
|
||||
logger.warning(f"WHOIS Timeout für {ip}")
|
||||
except FileNotFoundError:
|
||||
logger.error("WHOIS-Tool nicht installiert! Bitte 'whois' installieren.")
|
||||
except Exception as e:
|
||||
logger.error(f"WHOIS Fehler für {ip}: {e}")
|
||||
|
||||
_whois_cache[ip] = result
|
||||
return result
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# LIVE STATISTICS TRACKER
|
||||
# =============================================================================
|
||||
@@ -3889,6 +3999,9 @@ class JTLWAFiAgent:
|
||||
elif event_type == 'command.autoban_config':
|
||||
await self._handle_autoban_config_command(event_data)
|
||||
|
||||
elif event_type == 'command.whois':
|
||||
await self._handle_whois_command(event_data)
|
||||
|
||||
elif event_type == 'log.subscribe':
|
||||
shop = event_data.get('shop')
|
||||
if shop:
|
||||
@@ -4376,6 +4489,47 @@ class JTLWAFiAgent:
|
||||
'message': str(e)
|
||||
})
|
||||
|
||||
async def _handle_whois_command(self, data: Dict[str, Any]):
|
||||
"""Führt WHOIS-Lookup für eine IP durch und sendet Ergebnis."""
|
||||
command_id = data.get('command_id', 'unknown')
|
||||
ip = data.get('ip')
|
||||
|
||||
if not ip:
|
||||
await self._send_event('whois_result', {
|
||||
'command_id': command_id,
|
||||
'success': False,
|
||||
'error': 'Keine IP angegeben'
|
||||
})
|
||||
return
|
||||
|
||||
try:
|
||||
# WHOIS-Lookup durchführen (wird gecacht)
|
||||
result = whois_lookup(ip)
|
||||
|
||||
# Ergebnis senden (ohne raw für kleinere Payload)
|
||||
await self._send_event('whois_result', {
|
||||
'command_id': command_id,
|
||||
'success': True,
|
||||
'ip': ip,
|
||||
'netname': result.get('netname', ''),
|
||||
'org': result.get('org', ''),
|
||||
'asn': result.get('asn', ''),
|
||||
'country': result.get('country', ''),
|
||||
'abuse': result.get('abuse', ''),
|
||||
'range': result.get('range', '')
|
||||
})
|
||||
|
||||
logger.info(f"WHOIS für {ip}: {result.get('org', 'Unknown')} ({result.get('asn', 'N/A')})")
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"WHOIS Fehler für {ip}: {e}")
|
||||
await self._send_event('whois_result', {
|
||||
'command_id': command_id,
|
||||
'success': False,
|
||||
'ip': ip,
|
||||
'error': str(e)
|
||||
})
|
||||
|
||||
async def _periodic_tasks(self):
|
||||
"""Führt periodische Tasks aus."""
|
||||
while self.running:
|
||||
|
||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user