diff --git a/cyberpanel-mods/rdns/INDEX.md b/cyberpanel-mods/rdns/INDEX.md new file mode 100644 index 000000000..e498bd2cd --- /dev/null +++ b/cyberpanel-mods/rdns/INDEX.md @@ -0,0 +1,60 @@ +# CyberPanel rDNS Fix Mod for v2.4.4 + +## Overview + +This mod fixes critical bugs and issues in the rDNS (reverse DNS) validation system in CyberPanel 2.4.4. + +## Files in This Mod + +### Documentation +- **`rDNS-Fix-README.md`** - Comprehensive documentation of all fixes and issues +- **`INSTALL.md`** - Step-by-step installation guide +- **`INDEX.md`** - This file, mod overview and quick reference + +### Installation Scripts +- **`apply-rdns-fix.sh`** - Automatic installation script (recommended) + +### Fixed Code Files +- **`mailUtilities_fixed.py`** - Complete fixed `reverse_dns_lookup()` function +- **`virtualHostUtilities_fixed.py`** - Fixed code sections for `OnBoardingHostName()` function + +## Quick Start + +### One-Liner (Recommended) + +```bash +sh <(curl https://raw.githubusercontent.com/master3395/cyberpanel-mods/main/rdns/rdns-fix.sh || wget -O - https://raw.githubusercontent.com/master3395/cyberpanel-mods/main/rdns/rdns-fix.sh) +``` + +### Local Installation + +```bash +cd cyberpanel-mods/rdns +chmod +x rdns-fix.sh +sudo ./rdns-fix.sh +``` + +## Issues Fixed + +1. ✅ **NameError Bug** - Fixed undefined variable `msg` → `e` +2. ✅ **Empty List Handling** - Proper validation of empty rDNS results +3. ✅ **Silent Exception Swallowing** - Replaced with specific error handling +4. ✅ **API Failure Handling** - Comprehensive error handling for DNS server failures +5. ✅ **Logic Flow Issues** - Fixed validation flow when rDNS lookup fails +6. ✅ **Error Messages** - Enhanced with debugging information + +## Compatibility + +- **CyberPanel Version**: 2.4.4 +- **Python**: 3.6+ (compatible with CyberPanel 2.4.4) +- **OS**: All supported by CyberPanel 2.4.4 + +## Related Links + +- [CyberPanel v2.4.4 Repository](https://github.com/usmannasir/cyberpanel/tree/v2.4.4) +- [CyberPanel Documentation](https://cyberpanel.net/docs/) + +## Support + +See `rDNS-Fix-README.md` for detailed troubleshooting and support information. + diff --git a/cyberpanel-mods/rdns/INSTALL.md b/cyberpanel-mods/rdns/INSTALL.md new file mode 100644 index 000000000..88b20a0fd --- /dev/null +++ b/cyberpanel-mods/rdns/INSTALL.md @@ -0,0 +1,170 @@ +# Installation Guide - rDNS Fix for CyberPanel 2.4.4 + +## Quick Installation + +### Option 1: One-Liner SSH Command (Easiest - Recommended) + +Run this single command via SSH: + +```bash +sh <(curl https://raw.githubusercontent.com/master3395/cyberpanel-mods/main/rdns/rdns-fix.sh || wget -O - https://raw.githubusercontent.com/master3395/cyberpanel-mods/main/rdns/rdns-fix.sh) +``` + +Or using bash: + +```bash +bash <(curl -s https://raw.githubusercontent.com/master3395/cyberpanel-mods/main/rdns/rdns-fix.sh) || bash <(wget -O - https://raw.githubusercontent.com/master3395/cyberpanel-mods/main/rdns/rdns-fix.sh) +``` + +### Option 2: Local Installation + +```bash +cd /path/to/cyberpanel/cyberpanel-mods/rdns +chmod +x rdns-fix.sh +sudo ./rdns-fix.sh +``` + +### Option 3: Manual Installation + +Follow the step-by-step guide below. + +## Manual Installation Steps + +### Step 1: Backup Original Files + +```bash +# Create backup directory +sudo mkdir -p /usr/local/CyberCP/backup_rdns_fix_$(date +%Y%m%d_%H%M%S) +BACKUP_DIR="/usr/local/CyberCP/backup_rdns_fix_$(date +%Y%m%d_%H%M%S)" + +# Backup files +sudo cp /usr/local/CyberCP/plogical/mailUtilities.py "$BACKUP_DIR/mailUtilities.py" +sudo cp /usr/local/CyberCP/plogical/virtualHostUtilities.py "$BACKUP_DIR/virtualHostUtilities.py" + +echo "Backup created at: $BACKUP_DIR" +``` + +### Step 2: Apply Fix to mailUtilities.py + +#### Method A: Using sed (Quick Fix for NameError only) + +```bash +# Fix the critical NameError bug +sudo sed -i 's/str(msg)/str(e)/g' /usr/local/CyberCP/plogical/mailUtilities.py +``` + +#### Method B: Replace the entire function (Comprehensive Fix) + +1. Locate the `reverse_dns_lookup` function in `/usr/local/CyberCP/plogical/mailUtilities.py` (around line 1637) + +2. Replace the entire function with the content from `mailUtilities_fixed.py` in this mod directory + +3. Or use Python to apply the fix: + +```python +# Run as root +import re + +file_path = '/usr/local/CyberCP/plogical/mailUtilities.py' + +with open(file_path, 'r') as f: + content = f.read() + +# Find and replace the function +# (You'll need to manually copy the fixed function from mailUtilities_fixed.py) + +with open(file_path, 'w') as f: + f.write(content) +``` + +### Step 3: Apply Fix to virtualHostUtilities.py + +1. Open `/usr/local/CyberCP/plogical/virtualHostUtilities.py` + +2. Find the `OnBoardingHostName` function + +3. Replace the rDNS lookup section (around lines 119-137) with Section 1 from `virtualHostUtilities_fixed.py` + +4. Replace the domain validation section (around lines 333-343) with Section 2 from `virtualHostUtilities_fixed.py` + +### Step 4: Verify Changes + +```bash +# Check if NameError fix is applied +grep -n "str(e)" /usr/local/CyberCP/plogical/mailUtilities.py | grep "Error in fetch rDNS" + +# Should show the fixed line, not "str(msg)" +``` + +### Step 5: Restart CyberPanel + +```bash +sudo systemctl restart lscpd +``` + +### Step 6: Test + +1. Access CyberPanel web interface +2. Go to Onboarding page +3. Try configuring a hostname +4. Check that error messages are more descriptive +5. Review logs: `tail -f /home/cyberpanel/cyberpanel.log` + +## Verification Checklist + +- [ ] Backup created successfully +- [ ] NameError bug fixed (no `str(msg)` in mailUtilities.py) +- [ ] Empty rDNS result handling added +- [ ] Error messages include IP and domain information +- [ ] CyberPanel restarted successfully +- [ ] Tested onboarding process +- [ ] Logs show improved error messages + +## Rollback Instructions + +If you need to rollback: + +```bash +# Find your backup directory +ls -la /usr/local/CyberCP/backup_rdns_fix_* + +# Restore files (replace YYYYMMDD_HHMMSS with your backup timestamp) +sudo cp /usr/local/CyberCP/backup_rdns_fix_YYYYMMDD_HHMMSS/mailUtilities.py /usr/local/CyberCP/plogical/mailUtilities.py +sudo cp /usr/local/CyberCP/backup_rdns_fix_YYYYMMDD_HHMMSS/virtualHostUtilities.py /usr/local/CyberCP/plogical/virtualHostUtilities.py + +# Restart CyberPanel +sudo systemctl restart lscpd +``` + +## Troubleshooting + +### Issue: Permission Denied + +```bash +# Ensure you're running as root +sudo su - + +# Check file ownership +ls -la /usr/local/CyberCP/plogical/mailUtilities.py +``` + +### Issue: Changes Not Applied + +1. Verify file paths are correct +2. Check if files were modified: `stat /usr/local/CyberCP/plogical/mailUtilities.py` +3. Ensure CyberPanel was restarted +4. Check for syntax errors: `python3 -m py_compile /usr/local/CyberCP/plogical/mailUtilities.py` + +### Issue: Still Getting Errors + +1. Check logs: `tail -100 /home/cyberpanel/cyberpanel.log` +2. Verify DNS server availability: `curl https://cyberpanel.net/dnsServers.txt` +3. Test rDNS manually: `dig -x YOUR_IP_ADDRESS` + +## Support + +For issues or questions: +1. Check the README.md for detailed information +2. Review log files for specific error messages +3. Verify all steps were completed correctly + diff --git a/cyberpanel-mods/rdns/QUICK-INSTALL.md b/cyberpanel-mods/rdns/QUICK-INSTALL.md new file mode 100644 index 000000000..efb34d474 --- /dev/null +++ b/cyberpanel-mods/rdns/QUICK-INSTALL.md @@ -0,0 +1,51 @@ +# Quick Install - rDNS Fix for CyberPanel 2.4.4 + +## One-Liner SSH Command + +Copy and paste this command into your SSH terminal: + +```bash +sh <(curl https://raw.githubusercontent.com/master3395/cyberpanel-mods/main/rdns/rdns-fix.sh || wget -O - https://raw.githubusercontent.com/master3395/cyberpanel-mods/main/rdns/rdns-fix.sh) +``` + +## Alternative: Using bash + +```bash +bash <(curl -s https://raw.githubusercontent.com/master3395/cyberpanel-mods/main/rdns/rdns-fix.sh) || bash <(wget -O - https://raw.githubusercontent.com/master3395/cyberpanel-mods/main/rdns/rdns-fix.sh) +``` + +## What It Does + +1. ✅ Creates automatic backups +2. ✅ Fixes NameError bug (`str(msg)` → `str(e)`) +3. ✅ Replaces `reverse_dns_lookup()` with improved version +4. ✅ Adds empty rDNS result validation +5. ✅ Improves error messages with debugging info +6. ✅ Restarts CyberPanel automatically + +## Requirements + +- Root/sudo access +- CyberPanel 2.4.4 installed +- Internet connection (to download script) + +## After Installation + +1. Test the onboarding process in CyberPanel +2. Check that error messages are more descriptive +3. Review logs if needed: `tail -f /home/cyberpanel/cyberpanel.log` + +## Rollback + +If needed, restore from backup: + +```bash +# Find backup directory +ls -la /usr/local/CyberCP/backup_rdns_fix_* + +# Restore (replace TIMESTAMP with actual backup timestamp) +sudo cp /usr/local/CyberCP/backup_rdns_fix_TIMESTAMP/mailUtilities.py /usr/local/CyberCP/plogical/mailUtilities.py +sudo cp /usr/local/CyberCP/backup_rdns_fix_TIMESTAMP/virtualHostUtilities.py /usr/local/CyberCP/plogical/virtualHostUtilities.py +sudo systemctl restart lscpd +``` + diff --git a/cyberpanel-mods/rdns/apply-rdns-fix.sh b/cyberpanel-mods/rdns/apply-rdns-fix.sh new file mode 100644 index 000000000..73e017b75 --- /dev/null +++ b/cyberpanel-mods/rdns/apply-rdns-fix.sh @@ -0,0 +1,180 @@ +#!/bin/bash + +############################################################################### +# CyberPanel rDNS System Fix for v2.4.4 +# This script applies fixes to the rDNS validation system +############################################################################### + +set -e # Exit on error + +# Colors for output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +NC='\033[0m' # No Color + +# CyberPanel paths +CYBERCP_PATH="/usr/local/CyberCP" +MAIL_UTILITIES="${CYBERCP_PATH}/plogical/mailUtilities.py" +VIRTUAL_HOST_UTILITIES="${CYBERCP_PATH}/plogical/virtualHostUtilities.py" + +# Backup directory +BACKUP_DIR="${CYBERCP_PATH}/backup_rdns_fix_$(date +%Y%m%d_%H%M%S)" + +echo -e "${GREEN}========================================${NC}" +echo -e "${GREEN}CyberPanel rDNS System Fix v1.0${NC}" +echo -e "${GREEN}For CyberPanel 2.4.4${NC}" +echo -e "${GREEN}========================================${NC}" +echo "" + +# Check if running as root +if [ "$EUID" -ne 0 ]; then + echo -e "${RED}Please run as root${NC}" + exit 1 +fi + +# Check if CyberPanel is installed +if [ ! -d "$CYBERCP_PATH" ]; then + echo -e "${RED}CyberPanel not found at ${CYBERCP_PATH}${NC}" + exit 1 +fi + +# Check if files exist +if [ ! -f "$MAIL_UTILITIES" ]; then + echo -e "${RED}File not found: ${MAIL_UTILITIES}${NC}" + exit 1 +fi + +if [ ! -f "$VIRTUAL_HOST_UTILITIES" ]; then + echo -e "${RED}File not found: ${VIRTUAL_HOST_UTILITIES}${NC}" + exit 1 +fi + +# Create backup directory +echo -e "${YELLOW}Creating backup...${NC}" +mkdir -p "$BACKUP_DIR" +cp "$MAIL_UTILITIES" "${BACKUP_DIR}/mailUtilities.py" +cp "$VIRTUAL_HOST_UTILITIES" "${BACKUP_DIR}/virtualHostUtilities.py" +echo -e "${GREEN}Backup created at: ${BACKUP_DIR}${NC}" +echo "" + +# Get script directory +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" + +# Check if patch files exist +PATCH_MAIL="${SCRIPT_DIR}/mailUtilities.py.patch" +PATCH_VHOST="${SCRIPT_DIR}/virtualHostUtilities.py.patch" + +# Function to apply Python code fix +apply_mail_utilities_fix() { + echo -e "${YELLOW}Applying fix to mailUtilities.py...${NC}" + + # Create temporary Python script to apply the fix + python3 << 'PYTHON_SCRIPT' +import re +import sys + +file_path = sys.argv[1] + +try: + with open(file_path, 'r') as f: + content = f.read() + + # Fix 1: Replace str(msg) with str(e) in exception handler + content = re.sub( + r"logging\.CyberCPLogFileWriter\.writeToFile\(f'Error in fetch rDNS \{str\(msg\)\}'\)", + "logging.CyberCPLogFileWriter.writeToFile(f'Error in fetch rDNS {str(e)}')", + content + ) + + # Fix 2: Check if reverse_dns_lookup function needs comprehensive fix + # This is a more complex fix that requires the full function replacement + # We'll use a marker-based approach + + # Check if the old buggy version exists + if "str(msg)" in content and "reverse_dns_lookup" in content: + print("Found buggy version, applying comprehensive fix...") + + # Read the fixed version from a separate file or embed it + # For now, we'll do a targeted fix + pass + + with open(file_path, 'w') as f: + f.write(content) + + print("Fix applied successfully") + +except Exception as e: + print(f"Error applying fix: {e}") + sys.exit(1) +PYTHON_SCRIPT + "$MAIL_UTILITIES" + + if [ $? -eq 0 ]; then + echo -e "${GREEN}mailUtilities.py fix applied${NC}" + else + echo -e "${RED}Failed to apply fix to mailUtilities.py${NC}" + return 1 + fi +} + +# Function to check if fix is already applied +check_if_fixed() { + if grep -q "str(e)" "$MAIL_UTILITIES" && ! grep -q "str(msg)" "$MAIL_UTILITIES"; then + return 0 # Fixed + else + return 1 # Not fixed + fi +} + +# Main installation +echo -e "${YELLOW}Checking current state...${NC}" + +if check_if_fixed; then + echo -e "${GREEN}Fix appears to already be applied${NC}" + read -p "Do you want to reapply? (y/N): " -n 1 -r + echo + if [[ ! $REPLY =~ ^[Yy]$ ]]; then + echo -e "${YELLOW}Aborted${NC}" + exit 0 + fi +fi + +# Apply fixes +echo "" +echo -e "${YELLOW}Applying fixes...${NC}" + +# Fix 1: mailUtilities.py - NameError bug +if grep -q "str(msg)" "$MAIL_UTILITIES"; then + sed -i 's/str(msg)/str(e)/g' "$MAIL_UTILITIES" + echo -e "${GREEN}✓ Fixed NameError bug in mailUtilities.py${NC}" +else + echo -e "${YELLOW}⚠ NameError fix not needed (may already be fixed)${NC}" +fi + +# Note: The comprehensive fixes require full function replacement +# which is better done manually or with a proper patch file +echo "" +echo -e "${YELLOW}Note: This script applies the critical NameError fix.${NC}" +echo -e "${YELLOW}For comprehensive fixes (error handling improvements),${NC}" +echo -e "${YELLOW}please refer to the README.md for manual installation steps.${NC}" +echo "" + +# Restart CyberPanel +echo -e "${YELLOW}Restarting CyberPanel...${NC}" +if systemctl is-active --quiet lscpd; then + systemctl restart lscpd + echo -e "${GREEN}CyberPanel restarted${NC}" +else + echo -e "${YELLOW}CyberPanel service not running, skipping restart${NC}" +fi + +echo "" +echo -e "${GREEN}========================================${NC}" +echo -e "${GREEN}Fix applied successfully!${NC}" +echo -e "${GREEN}========================================${NC}" +echo "" +echo -e "${YELLOW}Backup location: ${BACKUP_DIR}${NC}" +echo -e "${YELLOW}Please test the rDNS functionality and review logs if needed.${NC}" +echo "" + diff --git a/cyberpanel-mods/rdns/mailUtilities_fixed.py b/cyberpanel-mods/rdns/mailUtilities_fixed.py new file mode 100644 index 000000000..c9bc245b3 --- /dev/null +++ b/cyberpanel-mods/rdns/mailUtilities_fixed.py @@ -0,0 +1,134 @@ +# Fixed reverse_dns_lookup function for CyberPanel 2.4.4 +# Replace the reverse_dns_lookup function in /usr/local/CyberCP/plogical/mailUtilities.py +# +# Original function location: around line 1637 +# This is the complete fixed function to replace the original + + @staticmethod + def reverse_dns_lookup(ip_address): + """ + Perform reverse DNS lookup for the given IP address using external DNS servers. + + Args: + ip_address: The IP address to perform reverse DNS lookup on + + Returns: + list: List of rDNS hostnames found, or empty list if lookup fails + """ + try: + import requests + from requests.exceptions import RequestException, Timeout, ConnectionError + + # Fetch DNS server URLs with proper error handling + try: + fetchURLs = requests.get('https://cyberpanel.net/dnsServers.txt', timeout=10) + except (ConnectionError, Timeout) as e: + logging.CyberCPLogFileWriter.writeToFile(f'Failed to fetch DNS server list from cyberpanel.net: {str(e)}') + return [] + except RequestException as e: + logging.CyberCPLogFileWriter.writeToFile(f'Request error while fetching DNS server list: {str(e)}') + return [] + + if fetchURLs.status_code != 200: + logging.CyberCPLogFileWriter.writeToFile(f'Failed to fetch DNS server list: HTTP {fetchURLs.status_code}') + return [] + + try: + urls_data = fetchURLs.json() + if 'urls' not in urls_data: + logging.CyberCPLogFileWriter.writeToFile('DNS server list response missing "urls" key') + return [] + urls = urls_data['urls'] + except (ValueError, KeyError) as e: + logging.CyberCPLogFileWriter.writeToFile(f'Failed to parse DNS server list JSON: {str(e)}') + return [] + + if not isinstance(urls, list) or len(urls) == 0: + logging.CyberCPLogFileWriter.writeToFile('DNS server list is empty or invalid') + return [] + + if os.path.exists(ProcessUtilities.debugPath): + logging.CyberCPLogFileWriter.writeToFile(f'DNS urls {urls}.') + + results = [] + successful_queries = 0 + + # Query each DNS server + for url in urls: + try: + response = requests.get(f'{url}/index.php?ip={ip_address}', timeout=5) + + if os.path.exists(ProcessUtilities.debugPath): + logging.CyberCPLogFileWriter.writeToFile(f'url to call {ip_address} is {url}') + + if response.status_code == 200: + try: + data = response.json() + + if os.path.exists(ProcessUtilities.debugPath): + logging.CyberCPLogFileWriter.writeToFile(f'response from dns system {str(data)}') + + # Validate response structure + if not isinstance(data, dict): + logging.CyberCPLogFileWriter.writeToFile(f'Invalid response format from {url}: not a dictionary') + continue + + if 'status' not in data: + logging.CyberCPLogFileWriter.writeToFile(f'Response from {url} missing "status" key') + continue + + if data['status'] == 1: + # Validate results structure + if 'results' not in data or not isinstance(data['results'], dict): + logging.CyberCPLogFileWriter.writeToFile(f'Response from {url} missing or invalid "results" key') + continue + + results_dict = data['results'] + + # Safely extract results from different DNS servers + dns_servers = ['8.8.8.8', '1.1.1.1', '9.9.9.9'] + for dns_server in dns_servers: + if dns_server in results_dict: + result_value = results_dict[dns_server] + if result_value and result_value not in results: + results.append(result_value) + + successful_queries += 1 + else: + if os.path.exists(ProcessUtilities.debugPath): + logging.CyberCPLogFileWriter.writeToFile(f'DNS server {url} returned status != 1: {data.get("status", "unknown")}') + except ValueError as e: + logging.CyberCPLogFileWriter.writeToFile(f'Failed to parse JSON response from {url}: {str(e)}') + continue + except KeyError as e: + logging.CyberCPLogFileWriter.writeToFile(f'Missing key in response from {url}: {str(e)}') + continue + else: + if os.path.exists(ProcessUtilities.debugPath): + logging.CyberCPLogFileWriter.writeToFile(f'DNS server {url} returned HTTP {response.status_code}') + except Timeout as e: + logging.CyberCPLogFileWriter.writeToFile(f'Timeout while querying DNS server {url}: {str(e)}') + continue + except ConnectionError as e: + logging.CyberCPLogFileWriter.writeToFile(f'Connection error while querying DNS server {url}: {str(e)}') + continue + except RequestException as e: + logging.CyberCPLogFileWriter.writeToFile(f'Request error while querying DNS server {url}: {str(e)}') + continue + except Exception as e: + logging.CyberCPLogFileWriter.writeToFile(f'Unexpected error while querying DNS server {url}: {str(e)}') + continue + + if os.path.exists(ProcessUtilities.debugPath): + logging.CyberCPLogFileWriter.writeToFile(f'rDNS result of {ip_address} is {str(results)} (successful queries: {successful_queries}/{len(urls)})') + + # Return results (empty list if no successful queries) + return results + + except ImportError as e: + logging.CyberCPLogFileWriter.writeToFile(f'Failed to import requests library: {str(e)}') + return [] + except BaseException as e: + logging.CyberCPLogFileWriter.writeToFile(f'Unexpected error in reverse_dns_lookup for IP {ip_address}: {str(e)}') + return [] + diff --git a/cyberpanel-mods/rdns/rDNS-Fix-README.md b/cyberpanel-mods/rdns/rDNS-Fix-README.md new file mode 100644 index 000000000..a9cff040e --- /dev/null +++ b/cyberpanel-mods/rdns/rDNS-Fix-README.md @@ -0,0 +1,223 @@ +# CyberPanel rDNS System Fix for v2.4.4 + +## Overview + +This fix addresses critical bugs and issues in the rDNS (reverse DNS) validation system in CyberPanel 2.4.4. The problems typically occur during the onboarding process when configuring a hostname, causing errors like: + +- `Domain that you have provided is not configured as rDNS for your server IP. [404]` +- `Failed to perform reverse DNS lookup: [404]` +- Silent failures when DNS lookup services are unavailable + +## Issues Fixed + +### 1. Critical NameError Bug (`mailUtilities.reverse_dns_lookup()`) +- **Problem**: Line 1681 used undefined variable `msg` instead of `e` in exception handler, causing NameError +- **Fix**: Changed `str(msg)` to `str(e)` to correctly reference the exception object + +### 2. Empty List Handling +- **Problem**: When `reverse_dns_lookup()` returns empty list (network/API failures), validation always fails even if domain is correctly configured +- **Fix**: Added proper validation to distinguish between "lookup failed" and "domain not found in rDNS" + +### 3. Silent Exception Swallowing +- **Problem**: Broad `except: pass` blocks hide errors and make debugging impossible +- **Fix**: Replaced with specific exception handling (Timeout, ConnectionError, RequestException) with proper logging + +### 4. Missing API Failure Handling +- **Problem**: No proper error handling when `https://cyberpanel.net/dnsServers.txt` is unavailable +- **Fix**: Added comprehensive error handling for network failures, invalid responses, and missing JSON keys + +### 5. Logic Flow Issue +- **Problem**: When rDNS lookup fails but `skipRDNSCheck` is False, code still validates against empty list +- **Fix**: Added validation to check if rDNS results are empty before domain validation + +### 6. Improved Error Messages +- **Problem**: Generic error messages don't provide enough information for debugging +- **Fix**: Enhanced error messages include server IP, domain name, current rDNS records, and actionable guidance + +## Files Modified + +1. **`cyberpanel/plogical/mailUtilities.py`** + - Fixed `reverse_dns_lookup()` function (lines 1637-1683) + - Added comprehensive error handling and logging + - Added API response validation + - Improved timeout and connection error handling + +2. **`cyberpanel/plogical/virtualHostUtilities.py`** + - Enhanced `OnBoardingHostName()` function (lines 119-343) + - Added empty rDNS result validation + - Improved error messages with debugging information + - Better distinction between lookup failures and domain mismatches + +## Installation + +### One-Liner SSH Command (Easiest - Recommended) + +Run this single command via SSH: + +```bash +sh <(curl https://raw.githubusercontent.com/master3395/cyberpanel-mods/main/rdns/rdns-fix.sh || wget -O - https://raw.githubusercontent.com/master3395/cyberpanel-mods/main/rdns/rdns-fix.sh) +``` + +Or using bash: + +```bash +bash <(curl -s https://raw.githubusercontent.com/master3395/cyberpanel-mods/main/rdns/rdns-fix.sh) || bash <(wget -O - https://raw.githubusercontent.com/master3395/cyberpanel-mods/main/rdns/rdns-fix.sh) +``` + +### Local Installation + +```bash +# Make the script executable +chmod +x cyberpanel/cyberpanel-mods/rdns/rdns-fix.sh + +# Run the fix script +sudo ./cyberpanel/cyberpanel-mods/rdns/rdns-fix.sh +``` + +### Manual Installation + +#### Step 1: Backup Original Files + +```bash +# Backup mailUtilities.py +cp /usr/local/CyberCP/plogical/mailUtilities.py /usr/local/CyberCP/plogical/mailUtilities.py.backup.$(date +%Y%m%d_%H%M%S) + +# Backup virtualHostUtilities.py +cp /usr/local/CyberCP/plogical/virtualHostUtilities.py /usr/local/CyberCP/plogical/virtualHostUtilities.py.backup.$(date +%Y%m%d_%H%M%S) +``` + +#### Step 2: Apply Fixes + +Copy the fixed code from this mod to the respective files: + +1. Replace `reverse_dns_lookup()` function in `/usr/local/CyberCP/plogical/mailUtilities.py` +2. Update `OnBoardingHostName()` function in `/usr/local/CyberCP/plogical/virtualHostUtilities.py` + +#### Step 3: Restart CyberPanel + +```bash +systemctl restart lscpd +``` + +## Verification + +After applying the fix: + +1. **Test rDNS Lookup**: Try the onboarding process with a valid domain +2. **Check Error Messages**: Error messages should now be more descriptive +3. **Test Empty Results**: System should properly handle DNS lookup failures +4. **Check Logs**: Review `/home/cyberpanel/cyberpanel.log` for detailed error information + +## Code Changes Summary + +### mailUtilities.py - reverse_dns_lookup() + +**Before:** +```python +except BaseException as e: + logging.CyberCPLogFileWriter.writeToFile(f'Error in fetch rDNS {str(msg)}') # BUG: msg undefined + return [] +``` + +**After:** +```python +except BaseException as e: + logging.CyberCPLogFileWriter.writeToFile(f'Unexpected error in reverse_dns_lookup for IP {ip_address}: {str(e)}') + return [] +``` + +### virtualHostUtilities.py - OnBoardingHostName() + +**Before:** +```python +rDNS = mailUtilities.reverse_dns_lookup(serverIP) +# ... later ... +if Domain not in rDNS: + message = 'Domain that you have provided is not configured as rDNS for your server IP. [404]' +``` + +**After:** +```python +rDNS = mailUtilities.reverse_dns_lookup(serverIP) +if not rDNS or len(rDNS) == 0: + message = f'Failed to perform reverse DNS lookup for server IP {serverIP}...' + return 0 +# ... later ... +if Domain not in rDNS: + rDNS_list_str = ', '.join(rDNS) if rDNS else 'none' + message = f'Domain "{Domain}" that you have provided is not configured as rDNS for your server IP {serverIP}. Current rDNS records: {rDNS_list_str}...' +``` + +## Troubleshooting + +### Common Issues + +1. **Fix not applying** + - Verify file paths: `/usr/local/CyberCP/plogical/` + - Check file permissions: `ls -la /usr/local/CyberCP/plogical/mailUtilities.py` + - Ensure CyberPanel is restarted: `systemctl restart lscpd` + +2. **Still getting errors** + - Check logs: `tail -f /home/cyberpanel/cyberpanel.log` + - Verify DNS server availability: `curl https://cyberpanel.net/dnsServers.txt` + - Test rDNS manually: `dig -x YOUR_IP_ADDRESS` + +3. **Empty rDNS results** + - Verify your IP has rDNS configured: Contact your hosting provider + - Check if DNS servers are accessible: Review network connectivity + - Consider using "Skip rDNS/PTR Check" if email services aren't needed + +### Log Files + +- **CyberPanel Log**: `/home/cyberpanel/cyberpanel.log` +- **Debug Log**: Check if `/home/cyberpanel/debug` exists for detailed logging +- **LiteSpeed Error Log**: `/usr/local/lsws/logs/error.log` + +## Compatibility + +- **CyberPanel Version**: 2.4.4 +- **Python Version**: 3.6+ (compatible with CyberPanel 2.4.4 requirements) +- **Operating Systems**: All supported by CyberPanel 2.4.4 (Ubuntu, AlmaLinux, RockyLinux, RHEL, CloudLinux, CentOS) + +## Rollback + +If you need to rollback the changes: + +```bash +# Restore from backup +cp /usr/local/CyberCP/plogical/mailUtilities.py.backup.* /usr/local/CyberCP/plogical/mailUtilities.py +cp /usr/local/CyberCP/plogical/virtualHostUtilities.py.backup.* /usr/local/CyberCP/plogical/virtualHostUtilities.py + +# Restart CyberPanel +systemctl restart lscpd +``` + +## Security Considerations + +1. **Error Messages**: Enhanced error messages may reveal server IP addresses - this is intentional for debugging but be aware in production +2. **Network Requests**: The fix adds proper timeout handling to prevent hanging requests +3. **Logging**: More detailed logging helps with debugging but may increase log file size + +## Support + +If you encounter issues after applying this fix: + +1. Check the troubleshooting section above +2. Review log files for specific error messages +3. Verify all file permissions and ownership +4. Test with a simple rDNS configuration first + +## Changelog + +- **v1.0**: Initial fix for rDNS system issues in CyberPanel 2.4.4 + - Fixed NameError bug in `reverse_dns_lookup()` + - Added comprehensive error handling + - Improved empty result validation + - Enhanced error messages with debugging information + +## References + +- [CyberPanel GitHub Repository v2.4.4](https://github.com/usmannasir/cyberpanel/tree/v2.4.4) +- [CyberPanel Documentation](https://cyberpanel.net/docs/) +- [CyberPanel Mods Repository](https://github.com/master3395/cyberpanel-mods) + diff --git a/cyberpanel-mods/rdns/rdns-fix.sh b/cyberpanel-mods/rdns/rdns-fix.sh new file mode 100644 index 000000000..cf22b5338 --- /dev/null +++ b/cyberpanel-mods/rdns/rdns-fix.sh @@ -0,0 +1,367 @@ +#!/bin/bash + +############################################################################### +# CyberPanel rDNS System Fix for v2.4.4 +# Standalone script that can be run via: +# sh <(curl https://raw.githubusercontent.com/master3395/cyberpanel-mods/main/rdns/rdns-fix.sh || wget -O - https://raw.githubusercontent.com/master3395/cyberpanel-mods/main/rdns/rdns-fix.sh) +############################################################################### + +set -e # Exit on error + +# Colors for output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +NC='\033[0m' # No Color + +# CyberPanel paths +CYBERCP_PATH="/usr/local/CyberCP" +MAIL_UTILITIES="${CYBERCP_PATH}/plogical/mailUtilities.py" +VIRTUAL_HOST_UTILITIES="${CYBERCP_PATH}/plogical/virtualHostUtilities.py" + +# Backup directory +BACKUP_DIR="${CYBERCP_PATH}/backup_rdns_fix_$(date +%Y%m%d_%H%M%S)" + +echo -e "${GREEN}========================================${NC}" +echo -e "${GREEN}CyberPanel rDNS System Fix v1.0${NC}" +echo -e "${GREEN}For CyberPanel 2.4.4${NC}" +echo -e "${GREEN}========================================${NC}" +echo "" + +# Check if running as root +if [ "$EUID" -ne 0 ]; then + echo -e "${RED}Please run as root (use sudo)${NC}" + exit 1 +fi + +# Check if CyberPanel is installed +if [ ! -d "$CYBERCP_PATH" ]; then + echo -e "${RED}CyberPanel not found at ${CYBERCP_PATH}${NC}" + exit 1 +fi + +# Check if files exist +if [ ! -f "$MAIL_UTILITIES" ]; then + echo -e "${RED}File not found: ${MAIL_UTILITIES}${NC}" + exit 1 +fi + +if [ ! -f "$VIRTUAL_HOST_UTILITIES" ]; then + echo -e "${RED}File not found: ${VIRTUAL_HOST_UTILITIES}${NC}" + exit 1 +fi + +# Create backup directory +echo -e "${YELLOW}Creating backup...${NC}" +mkdir -p "$BACKUP_DIR" +cp "$MAIL_UTILITIES" "${BACKUP_DIR}/mailUtilities.py" +cp "$VIRTUAL_HOST_UTILITIES" "${BACKUP_DIR}/virtualHostUtilities.py" +echo -e "${GREEN}Backup created at: ${BACKUP_DIR}${NC}" +echo "" + +# Apply fixes using Python +echo -e "${YELLOW}Applying comprehensive fixes...${NC}" + +python3 << 'PYTHON_FIX' +import re +import sys +import os + +# File paths +mail_utils = "/usr/local/CyberCP/plogical/mailUtilities.py" +vhost_utils = "/usr/local/CyberCP/plogical/virtualHostUtilities.py" + +def fix_mail_utilities(): + """Fix mailUtilities.py - reverse_dns_lookup function""" + try: + with open(mail_utils, 'r') as f: + content = f.read() + + original_content = content + + # Fix 1: Replace str(msg) with str(e) - Critical NameError bug + content = re.sub( + r"logging\.CyberCPLogFileWriter\.writeToFile\(f'Error in fetch rDNS \{str\(msg\)\}'\)", + "logging.CyberCPLogFileWriter.writeToFile(f'Error in fetch rDNS {str(e)}')", + content + ) + + # Fix 2: Replace the entire reverse_dns_lookup function with improved version + # Find the function start + func_start_pattern = r'(@staticmethod\s+def reverse_dns_lookup\(ip_address\):.*?)(?=\n @staticmethod|\n def |\Z)' + + fixed_function = ''' @staticmethod + def reverse_dns_lookup(ip_address): + """ + Perform reverse DNS lookup for the given IP address using external DNS servers. + + Args: + ip_address: The IP address to perform reverse DNS lookup on + + Returns: + list: List of rDNS hostnames found, or empty list if lookup fails + """ + try: + import requests + from requests.exceptions import RequestException, Timeout, ConnectionError + + # Fetch DNS server URLs with proper error handling + try: + fetchURLs = requests.get('https://cyberpanel.net/dnsServers.txt', timeout=10) + except (ConnectionError, Timeout) as e: + logging.CyberCPLogFileWriter.writeToFile(f'Failed to fetch DNS server list from cyberpanel.net: {str(e)}') + return [] + except RequestException as e: + logging.CyberCPLogFileWriter.writeToFile(f'Request error while fetching DNS server list: {str(e)}') + return [] + + if fetchURLs.status_code != 200: + logging.CyberCPLogFileWriter.writeToFile(f'Failed to fetch DNS server list: HTTP {fetchURLs.status_code}') + return [] + + try: + urls_data = fetchURLs.json() + if 'urls' not in urls_data: + logging.CyberCPLogFileWriter.writeToFile('DNS server list response missing "urls" key') + return [] + urls = urls_data['urls'] + except (ValueError, KeyError) as e: + logging.CyberCPLogFileWriter.writeToFile(f'Failed to parse DNS server list JSON: {str(e)}') + return [] + + if not isinstance(urls, list) or len(urls) == 0: + logging.CyberCPLogFileWriter.writeToFile('DNS server list is empty or invalid') + return [] + + if os.path.exists(ProcessUtilities.debugPath): + logging.CyberCPLogFileWriter.writeToFile(f'DNS urls {urls}.') + + results = [] + successful_queries = 0 + + # Query each DNS server + for url in urls: + try: + response = requests.get(f'{url}/index.php?ip={ip_address}', timeout=5) + + if os.path.exists(ProcessUtilities.debugPath): + logging.CyberCPLogFileWriter.writeToFile(f'url to call {ip_address} is {url}') + + if response.status_code == 200: + try: + data = response.json() + + if os.path.exists(ProcessUtilities.debugPath): + logging.CyberCPLogFileWriter.writeToFile(f'response from dns system {str(data)}') + + # Validate response structure + if not isinstance(data, dict): + logging.CyberCPLogFileWriter.writeToFile(f'Invalid response format from {url}: not a dictionary') + continue + + if 'status' not in data: + logging.CyberCPLogFileWriter.writeToFile(f'Response from {url} missing "status" key') + continue + + if data['status'] == 1: + # Validate results structure + if 'results' not in data or not isinstance(data['results'], dict): + logging.CyberCPLogFileWriter.writeToFile(f'Response from {url} missing or invalid "results" key') + continue + + results_dict = data['results'] + + # Safely extract results from different DNS servers + dns_servers = ['8.8.8.8', '1.1.1.1', '9.9.9.9'] + for dns_server in dns_servers: + if dns_server in results_dict: + result_value = results_dict[dns_server] + if result_value and result_value not in results: + results.append(result_value) + + successful_queries += 1 + else: + if os.path.exists(ProcessUtilities.debugPath): + logging.CyberCPLogFileWriter.writeToFile(f'DNS server {url} returned status != 1: {data.get("status", "unknown")}') + except ValueError as e: + logging.CyberCPLogFileWriter.writeToFile(f'Failed to parse JSON response from {url}: {str(e)}') + continue + except KeyError as e: + logging.CyberCPLogFileWriter.writeToFile(f'Missing key in response from {url}: {str(e)}') + continue + else: + if os.path.exists(ProcessUtilities.debugPath): + logging.CyberCPLogFileWriter.writeToFile(f'DNS server {url} returned HTTP {response.status_code}') + except Timeout as e: + logging.CyberCPLogFileWriter.writeToFile(f'Timeout while querying DNS server {url}: {str(e)}') + continue + except ConnectionError as e: + logging.CyberCPLogFileWriter.writeToFile(f'Connection error while querying DNS server {url}: {str(e)}') + continue + except RequestException as e: + logging.CyberCPLogFileWriter.writeToFile(f'Request error while querying DNS server {url}: {str(e)}') + continue + except Exception as e: + logging.CyberCPLogFileWriter.writeToFile(f'Unexpected error while querying DNS server {url}: {str(e)}') + continue + + if os.path.exists(ProcessUtilities.debugPath): + logging.CyberCPLogFileWriter.writeToFile(f'rDNS result of {ip_address} is {str(results)} (successful queries: {successful_queries}/{len(urls)})') + + # Return results (empty list if no successful queries) + return results + + except ImportError as e: + logging.CyberCPLogFileWriter.writeToFile(f'Failed to import requests library: {str(e)}') + return [] + except BaseException as e: + logging.CyberCPLogFileWriter.writeToFile(f'Unexpected error in reverse_dns_lookup for IP {ip_address}: {str(e)}') + return []''' + + # Try to replace the function + if re.search(func_start_pattern, content, re.DOTALL): + content = re.sub(func_start_pattern, fixed_function + '\n', content, flags=re.DOTALL) + print("✓ Replaced entire reverse_dns_lookup function") + elif "str(msg)" in content: + # Fallback: just fix the NameError if function replacement didn't work + content = content.replace("str(msg)", "str(e)") + print("✓ Fixed NameError bug (str(msg) -> str(e))") + else: + print("⚠ Function may already be fixed or structure differs") + + if content != original_content: + with open(mail_utils, 'w') as f: + f.write(content) + print("✓ mailUtilities.py updated successfully") + return True + else: + print("⚠ No changes needed in mailUtilities.py") + return False + + except Exception as e: + print(f"✗ Error fixing mailUtilities.py: {e}") + return False + +def fix_virtual_host_utilities(): + """Fix virtualHostUtilities.py - OnBoardingHostName function""" + try: + with open(vhost_utils, 'r') as f: + content = f.read() + + original_content = content + changes_made = False + + # Fix 1: Add empty rDNS result check after reverse_dns_lookup call + # Find the pattern: rDNS = mailUtilities.reverse_dns_lookup(serverIP) + pattern1 = r'(rDNS = mailUtilities\.reverse_dns_lookup\(serverIP\))\s*\n(\s*except Exception as e:)' + replacement1 = r'''\1 + # Check if rDNS lookup returned empty results (indicating lookup failure) + if not rDNS or len(rDNS) == 0: + message = f'Failed to perform reverse DNS lookup for server IP {serverIP}. The DNS lookup service may be unavailable or the IP address may not have rDNS configured. Please verify your rDNS settings with your hosting provider or check the "Skip rDNS/PTR Check" option if you do not need email services. [404]' + logging.CyberCPLogFileWriter.statusWriter(tempStatusPath, message) + logging.CyberCPLogFileWriter.writeToFile(message) + return 0 +\2''' + + if re.search(pattern1, content): + content = re.sub(pattern1, replacement1, content) + print("✓ Added empty rDNS result validation") + changes_made = True + + # Fix 2: Improve error message in exception handler + pattern2 = r"message = f'Failed to perform reverse DNS lookup: \{str\(e\)\} \[404\]'" + replacement2 = "message = f'Failed to perform reverse DNS lookup for server IP {serverIP}: {str(e)}. Please verify your rDNS settings with your hosting provider or check the \\\"Skip rDNS/PTR Check\\\" option if you do not need email services. [404]'" + + if re.search(pattern2, content): + content = re.sub(pattern2, replacement2, content) + print("✓ Improved error message in exception handler") + changes_made = True + + # Fix 3: Add validation before domain check and improve error message + pattern3 = r'(#first check if hostname is already configured as rDNS, if not return error\s*\n\s*\n\s*if Domain not in rDNS:)' + replacement3 = r'''#first check if hostname is already configured as rDNS, if not return error + + # Validate that we have rDNS results before checking + if not rDNS or len(rDNS) == 0: + message = f'Reverse DNS lookup failed for server IP {serverIP}. Unable to verify if domain "{Domain}" is configured as rDNS. Please check your rDNS configuration with your hosting provider or select "Skip rDNS/PTR Check" if you do not need email services. [404]' + print(message) + logging.CyberCPLogFileWriter.statusWriter(tempStatusPath, message) + logging.CyberCPLogFileWriter.writeToFile(message) + config['hostname'] = Domain + config['onboarding'] = 3 + config['skipRDNSCheck'] = skipRDNSCheck + admin.config = json.dumps(config) + admin.save() + return 0 + + if Domain not in rDNS:''' + + if re.search(pattern3, content): + content = re.sub(pattern3, replacement3, content) + print("✓ Added rDNS validation before domain check") + changes_made = True + + # Fix 4: Improve domain mismatch error message + pattern4 = r"message = 'Domain that you have provided is not configured as rDNS for your server IP\. \[404\]'" + replacement4 = r'''rDNS_list_str = ', '.join(rDNS) if rDNS else 'none' + message = f'Domain "{Domain}" that you have provided is not configured as rDNS for your server IP {serverIP}. Current rDNS records: {rDNS_list_str}. Please configure rDNS (PTR record) for your IP address to point to "{Domain}" with your hosting provider, or select "Skip rDNS/PTR Check" if you do not need email services. [404]'''' + + if re.search(pattern4, content): + content = re.sub(pattern4, replacement4, content) + print("✓ Improved domain mismatch error message") + changes_made = True + + if changes_made and content != original_content: + with open(vhost_utils, 'w') as f: + f.write(content) + print("✓ virtualHostUtilities.py updated successfully") + return True + else: + print("⚠ No changes needed in virtualHostUtilities.py") + return False + + except Exception as e: + print(f"✗ Error fixing virtualHostUtilities.py: {e}") + return False + +# Apply fixes +print("\nFixing mailUtilities.py...") +fix_mail_utilities() + +print("\nFixing virtualHostUtilities.py...") +fix_virtual_host_utilities() + +print("\n✓ All fixes applied!") +PYTHON_FIX + +if [ $? -eq 0 ]; then + echo -e "${GREEN}✓ All fixes applied successfully${NC}" +else + echo -e "${RED}✗ Some fixes may have failed. Check output above.${NC}" + exit 1 +fi + +# Restart CyberPanel +echo "" +echo -e "${YELLOW}Restarting CyberPanel...${NC}" +if systemctl is-active --quiet lscpd 2>/dev/null || systemctl is-active --quiet cyberpanel 2>/dev/null; then + if systemctl restart lscpd 2>/dev/null; then + echo -e "${GREEN}✓ CyberPanel restarted${NC}" + elif systemctl restart cyberpanel 2>/dev/null; then + echo -e "${GREEN}✓ CyberPanel restarted${NC}" + else + echo -e "${YELLOW}⚠ Could not restart CyberPanel automatically. Please restart manually.${NC}" + fi +else + echo -e "${YELLOW}⚠ CyberPanel service not running, skipping restart${NC}" +fi + +echo "" +echo -e "${GREEN}========================================${NC}" +echo -e "${GREEN}Fix applied successfully!${NC}" +echo -e "${GREEN}========================================${NC}" +echo "" +echo -e "${YELLOW}Backup location: ${BACKUP_DIR}${NC}" +echo -e "${YELLOW}Please test the rDNS functionality in CyberPanel onboarding.${NC}" +echo "" + diff --git a/cyberpanel-mods/rdns/virtualHostUtilities_fixed.py b/cyberpanel-mods/rdns/virtualHostUtilities_fixed.py new file mode 100644 index 000000000..1753a0bc8 --- /dev/null +++ b/cyberpanel-mods/rdns/virtualHostUtilities_fixed.py @@ -0,0 +1,62 @@ +# Fixed OnBoardingHostName function sections for CyberPanel 2.4.4 +# Replace the relevant sections in /usr/local/CyberCP/plogical/virtualHostUtilities.py +# +# Section 1: Replace the rDNS lookup section (around line 119-137) +# Section 2: Replace the domain validation section (around line 333-343) + +# ============================================================================ +# SECTION 1: Replace lines 119-137 in OnBoardingHostName function +# ============================================================================ + + ### if skipRDNSCheck == 1, it means we need to skip checking for rDNS + if skipRDNSCheck: + ### When skipping rDNS check, include both current hostname and the domain being set up + ### This ensures both code paths work correctly + rDNS = [CurrentHostName, Domain] + else: + try: + rDNS = mailUtilities.reverse_dns_lookup(serverIP) + # Check if rDNS lookup returned empty results (indicating lookup failure) + if not rDNS or len(rDNS) == 0: + message = f'Failed to perform reverse DNS lookup for server IP {serverIP}. The DNS lookup service may be unavailable or the IP address may not have rDNS configured. Please verify your rDNS settings with your hosting provider or check the "Skip rDNS/PTR Check" option if you do not need email services. [404]' + logging.CyberCPLogFileWriter.statusWriter(tempStatusPath, message) + logging.CyberCPLogFileWriter.writeToFile(message) + return 0 + except Exception as e: + message = f'Failed to perform reverse DNS lookup for server IP {serverIP}: {str(e)}. Please verify your rDNS settings with your hosting provider or check the "Skip rDNS/PTR Check" option if you do not need email services. [404]' + logging.CyberCPLogFileWriter.statusWriter(tempStatusPath, message) + logging.CyberCPLogFileWriter.writeToFile(message) + return 0 + +# ============================================================================ +# SECTION 2: Replace lines 333-343 in OnBoardingHostName function +# ============================================================================ + + #first check if hostname is already configured as rDNS, if not return error + + # Validate that we have rDNS results before checking + if not rDNS or len(rDNS) == 0: + message = f'Reverse DNS lookup failed for server IP {serverIP}. Unable to verify if domain "{Domain}" is configured as rDNS. Please check your rDNS configuration with your hosting provider or select "Skip rDNS/PTR Check" if you do not need email services. [404]' + print(message) + logging.CyberCPLogFileWriter.statusWriter(tempStatusPath, message) + logging.CyberCPLogFileWriter.writeToFile(message) + config['hostname'] = Domain + config['onboarding'] = 3 + config['skipRDNSCheck'] = skipRDNSCheck + admin.config = json.dumps(config) + admin.save() + return 0 + + if Domain not in rDNS: + rDNS_list_str = ', '.join(rDNS) if rDNS else 'none' + message = f'Domain "{Domain}" that you have provided is not configured as rDNS for your server IP {serverIP}. Current rDNS records: {rDNS_list_str}. Please configure rDNS (PTR record) for your IP address to point to "{Domain}" with your hosting provider, or select "Skip rDNS/PTR Check" if you do not need email services. [404]' + print(message) + logging.CyberCPLogFileWriter.statusWriter(tempStatusPath, message) + logging.CyberCPLogFileWriter.writeToFile(message) + config['hostname'] = Domain + config['onboarding'] = 3 + config['skipRDNSCheck'] = skipRDNSCheck + admin.config = json.dumps(config) + admin.save() + return 0 + diff --git a/plogical/mailUtilities.py b/plogical/mailUtilities.py index 4b727c1c7..00df0c6f3 100644 --- a/plogical/mailUtilities.py +++ b/plogical/mailUtilities.py @@ -1635,51 +1635,130 @@ LogFile /var/log/clamav/clamav.log @staticmethod def reverse_dns_lookup(ip_address): + """ + Perform reverse DNS lookup for the given IP address using external DNS servers. + + Args: + ip_address: The IP address to perform reverse DNS lookup on + + Returns: + list: List of rDNS hostnames found, or empty list if lookup fails + """ try: import requests + from requests.exceptions import RequestException, Timeout, ConnectionError - fetchURLs = requests.get('https://cyberpanel.net/dnsServers.txt') + # Fetch DNS server URLs with proper error handling + try: + fetchURLs = requests.get('https://cyberpanel.net/dnsServers.txt', timeout=10) + except (ConnectionError, Timeout) as e: + logging.CyberCPLogFileWriter.writeToFile(f'Failed to fetch DNS server list from cyberpanel.net: {str(e)}') + return [] + except RequestException as e: + logging.CyberCPLogFileWriter.writeToFile(f'Request error while fetching DNS server list: {str(e)}') + return [] - if fetchURLs.status_code == 200: + if fetchURLs.status_code != 200: + logging.CyberCPLogFileWriter.writeToFile(f'Failed to fetch DNS server list: HTTP {fetchURLs.status_code}') + return [] - urls = fetchURLs.json()['urls'] + try: + urls_data = fetchURLs.json() + if 'urls' not in urls_data: + logging.CyberCPLogFileWriter.writeToFile('DNS server list response missing "urls" key') + return [] + urls = urls_data['urls'] + except (ValueError, KeyError) as e: + logging.CyberCPLogFileWriter.writeToFile(f'Failed to parse DNS server list JSON: {str(e)}') + return [] - if os.path.exists(ProcessUtilities.debugPath): - logging.CyberCPLogFileWriter.writeToFile(f'DNS urls {urls}.') + if not isinstance(urls, list) or len(urls) == 0: + logging.CyberCPLogFileWriter.writeToFile('DNS server list is empty or invalid') + return [] - results = [] + if os.path.exists(ProcessUtilities.debugPath): + logging.CyberCPLogFileWriter.writeToFile(f'DNS urls {urls}.') - ### + results = [] + successful_queries = 0 - for url in urls: - try: - response = requests.get(f'{url}/index.php?ip={ip_address}', timeout=5) + # Query each DNS server + for url in urls: + try: + response = requests.get(f'{url}/index.php?ip={ip_address}', timeout=5) - if os.path.exists(ProcessUtilities.debugPath): - logging.CyberCPLogFileWriter.writeToFile(f'url to call {ip_address} is {url}') + if os.path.exists(ProcessUtilities.debugPath): + logging.CyberCPLogFileWriter.writeToFile(f'url to call {ip_address} is {url}') - if response.status_code == 200: + if response.status_code == 200: + try: data = response.json() if os.path.exists(ProcessUtilities.debugPath): logging.CyberCPLogFileWriter.writeToFile(f'response from dns system {str(data)}') + # Validate response structure + if not isinstance(data, dict): + logging.CyberCPLogFileWriter.writeToFile(f'Invalid response format from {url}: not a dictionary') + continue + + if 'status' not in data: + logging.CyberCPLogFileWriter.writeToFile(f'Response from {url} missing "status" key') + continue + if data['status'] == 1: - results.append(data['results']['8.8.8.8']) - results.append(data['results']['1.1.1.1']) - results.append(data['results']['9.9.9.9']) - except: - pass + # Validate results structure + if 'results' not in data or not isinstance(data['results'], dict): + logging.CyberCPLogFileWriter.writeToFile(f'Response from {url} missing or invalid "results" key') + continue - if os.path.exists(ProcessUtilities.debugPath): - logging.CyberCPLogFileWriter.writeToFile(f'rDNS result of {ip_address} is {str(results)}') + results_dict = data['results'] + + # Safely extract results from different DNS servers + dns_servers = ['8.8.8.8', '1.1.1.1', '9.9.9.9'] + for dns_server in dns_servers: + if dns_server in results_dict: + result_value = results_dict[dns_server] + if result_value and result_value not in results: + results.append(result_value) + + successful_queries += 1 + else: + if os.path.exists(ProcessUtilities.debugPath): + logging.CyberCPLogFileWriter.writeToFile(f'DNS server {url} returned status != 1: {data.get("status", "unknown")}') + except ValueError as e: + logging.CyberCPLogFileWriter.writeToFile(f'Failed to parse JSON response from {url}: {str(e)}') + continue + except KeyError as e: + logging.CyberCPLogFileWriter.writeToFile(f'Missing key in response from {url}: {str(e)}') + continue + else: + if os.path.exists(ProcessUtilities.debugPath): + logging.CyberCPLogFileWriter.writeToFile(f'DNS server {url} returned HTTP {response.status_code}') + except Timeout as e: + logging.CyberCPLogFileWriter.writeToFile(f'Timeout while querying DNS server {url}: {str(e)}') + continue + except ConnectionError as e: + logging.CyberCPLogFileWriter.writeToFile(f'Connection error while querying DNS server {url}: {str(e)}') + continue + except RequestException as e: + logging.CyberCPLogFileWriter.writeToFile(f'Request error while querying DNS server {url}: {str(e)}') + continue + except Exception as e: + logging.CyberCPLogFileWriter.writeToFile(f'Unexpected error while querying DNS server {url}: {str(e)}') + continue - ### + if os.path.exists(ProcessUtilities.debugPath): + logging.CyberCPLogFileWriter.writeToFile(f'rDNS result of {ip_address} is {str(results)} (successful queries: {successful_queries}/{len(urls)})') - return results + # Return results (empty list if no successful queries) + return results + + except ImportError as e: + logging.CyberCPLogFileWriter.writeToFile(f'Failed to import requests library: {str(e)}') + return [] except BaseException as e: - logging.CyberCPLogFileWriter.writeToFile(f'Error in fetch rDNS {str(msg)}') - # Handle errors, e.g., if reverse DNS lookup fails + logging.CyberCPLogFileWriter.writeToFile(f'Unexpected error in reverse_dns_lookup for IP {ip_address}: {str(e)}') return [] @staticmethod diff --git a/plogical/virtualHostUtilities.py b/plogical/virtualHostUtilities.py index 8a78991a8..78a4d2847 100644 --- a/plogical/virtualHostUtilities.py +++ b/plogical/virtualHostUtilities.py @@ -124,8 +124,14 @@ class virtualHostUtilities: else: try: rDNS = mailUtilities.reverse_dns_lookup(serverIP) + # Check if rDNS lookup returned empty results (indicating lookup failure) + if not rDNS or len(rDNS) == 0: + message = f'Failed to perform reverse DNS lookup for server IP {serverIP}. The DNS lookup service may be unavailable or the IP address may not have rDNS configured. Please verify your rDNS settings with your hosting provider or check the "Skip rDNS/PTR Check" option if you do not need email services. [404]' + logging.CyberCPLogFileWriter.statusWriter(tempStatusPath, message) + logging.CyberCPLogFileWriter.writeToFile(message) + return 0 except Exception as e: - message = f'Failed to perform reverse DNS lookup: {str(e)} [404]' + message = f'Failed to perform reverse DNS lookup for server IP {serverIP}: {str(e)}. Please verify your rDNS settings with your hosting provider or check the "Skip rDNS/PTR Check" option if you do not need email services. [404]' logging.CyberCPLogFileWriter.statusWriter(tempStatusPath, message) logging.CyberCPLogFileWriter.writeToFile(message) return 0 @@ -329,9 +335,22 @@ class virtualHostUtilities: #first check if hostname is already configured as rDNS, if not return error + # Validate that we have rDNS results before checking + if not rDNS or len(rDNS) == 0: + message = f'Reverse DNS lookup failed for server IP {serverIP}. Unable to verify if domain "{Domain}" is configured as rDNS. Please check your rDNS configuration with your hosting provider or select "Skip rDNS/PTR Check" if you do not need email services. [404]' + print(message) + logging.CyberCPLogFileWriter.statusWriter(tempStatusPath, message) + logging.CyberCPLogFileWriter.writeToFile(message) + config['hostname'] = Domain + config['onboarding'] = 3 + config['skipRDNSCheck'] = skipRDNSCheck + admin.config = json.dumps(config) + admin.save() + return 0 if Domain not in rDNS: - message = 'Domain that you have provided is not configured as rDNS for your server IP. [404]' + rDNS_list_str = ', '.join(rDNS) if rDNS else 'none' + message = f'Domain "{Domain}" that you have provided is not configured as rDNS for your server IP {serverIP}. Current rDNS records: {rDNS_list_str}. Please configure rDNS (PTR record) for your IP address to point to "{Domain}" with your hosting provider, or select "Skip rDNS/PTR Check" if you do not need email services. [404]' print(message) logging.CyberCPLogFileWriter.statusWriter(tempStatusPath, message) logging.CyberCPLogFileWriter.writeToFile(message)