|
@@ -0,0 +1,304 @@
|
|
|
|
|
+"""
|
|
|
|
|
+通过修复 DNS 解析问题来使用 Cloudflare Tunnel
|
|
|
|
|
+"""
|
|
|
|
|
+import subprocess
|
|
|
|
|
+import time
|
|
|
|
|
+import os
|
|
|
|
|
+import signal
|
|
|
|
|
+import sys
|
|
|
|
|
+import re
|
|
|
|
|
+import socket
|
|
|
|
|
+from pathlib import Path
|
|
|
|
|
+
|
|
|
|
|
+def setup_dns_resolution():
|
|
|
|
|
+ """设置 DNS 解析"""
|
|
|
|
|
+ print("🔧 Setting up DNS resolution...")
|
|
|
|
|
+
|
|
|
|
|
+ # 临时 DNS 解析方案
|
|
|
|
|
+ hosts_entries = {
|
|
|
|
|
+ 'api.trycloudflare.com': '104.18.22.223', # Cloudflare IP
|
|
|
|
|
+ 'trycloudflare.com': '104.18.22.223'
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ # 写入临时 hosts 文件
|
|
|
|
|
+ temp_hosts = Path.home() / 'zhch' / 'temp_hosts'
|
|
|
|
|
+ with open(temp_hosts, 'w') as f:
|
|
|
|
|
+ for domain, ip in hosts_entries.items():
|
|
|
|
|
+ f.write(f"{ip} {domain}\n")
|
|
|
|
|
+
|
|
|
|
|
+ print(f"✅ Created temporary hosts file: {temp_hosts}")
|
|
|
|
|
+ return str(temp_hosts)
|
|
|
|
|
+
|
|
|
|
|
+def test_dns_resolution():
|
|
|
|
|
+ """测试 DNS 解析"""
|
|
|
|
|
+ test_domains = [
|
|
|
|
|
+ 'api.trycloudflare.com',
|
|
|
|
|
+ 'google.com',
|
|
|
|
|
+ 'cloudflare.com'
|
|
|
|
|
+ ]
|
|
|
|
|
+
|
|
|
|
|
+ print("🧪 Testing DNS resolution...")
|
|
|
|
|
+ for domain in test_domains:
|
|
|
|
|
+ try:
|
|
|
|
|
+ ip = socket.gethostbyname(domain)
|
|
|
|
|
+ print(f"✅ {domain} -> {ip}")
|
|
|
|
|
+ except socket.gaierror as e:
|
|
|
|
|
+ print(f"❌ {domain} -> Failed: {e}")
|
|
|
|
|
+
|
|
|
|
|
+def setup_environment_with_dns():
|
|
|
|
|
+ """设置包含 DNS 配置的环境变量"""
|
|
|
|
|
+ proxy_url = "http://172.16.40.16:7280"
|
|
|
|
|
+ env = os.environ.copy()
|
|
|
|
|
+
|
|
|
|
|
+ # 基本代理配置
|
|
|
|
|
+ env.update({
|
|
|
|
|
+ 'HTTP_PROXY': proxy_url,
|
|
|
|
|
+ 'HTTPS_PROXY': proxy_url,
|
|
|
|
|
+ 'http_proxy': proxy_url,
|
|
|
|
|
+ 'https_proxy': proxy_url,
|
|
|
|
|
+ })
|
|
|
|
|
+
|
|
|
|
|
+ # DNS 配置
|
|
|
|
|
+ env.update({
|
|
|
|
|
+ 'GODEBUG': 'netdns=go+1',
|
|
|
|
|
+ 'GOPROXY': 'direct',
|
|
|
|
|
+ 'GOSUMDB': 'off',
|
|
|
|
|
+ })
|
|
|
|
|
+
|
|
|
|
|
+ # 使用公共 DNS
|
|
|
|
|
+ env['NAMESERVER'] = '8.8.8.8'
|
|
|
|
|
+
|
|
|
|
|
+ return env
|
|
|
|
|
+
|
|
|
|
|
+def download_cloudflared_direct():
|
|
|
|
|
+ """直接下载 cloudflared(绕过 DNS 问题)"""
|
|
|
|
|
+ print("🔄 Downloading cloudflared directly...")
|
|
|
|
|
+
|
|
|
|
|
+ bin_dir = Path.home() / "zhch" / "bin"
|
|
|
|
|
+ bin_dir.mkdir(exist_ok=True, parents=True)
|
|
|
|
|
+ cloudflared_path = bin_dir / "cloudflared"
|
|
|
|
|
+
|
|
|
|
|
+ if cloudflared_path.exists():
|
|
|
|
|
+ print("✅ Cloudflared already exists")
|
|
|
|
|
+ return str(cloudflared_path)
|
|
|
|
|
+
|
|
|
|
|
+ # 使用直接 IP 地址下载(绕过 DNS)
|
|
|
|
|
+ github_ip = "140.82.113.3" # GitHub IP
|
|
|
|
|
+ download_cmd = [
|
|
|
|
|
+ 'curl', '-L', '-k', # -k 忽略 SSL 证书验证
|
|
|
|
|
+ '-H', f'Host: github.com',
|
|
|
|
|
+ f'http://{github_ip}/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64',
|
|
|
|
|
+ '-o', str(cloudflared_path)
|
|
|
|
|
+ ]
|
|
|
|
|
+
|
|
|
|
|
+ try:
|
|
|
|
|
+ env = setup_environment_with_dns()
|
|
|
|
|
+ result = subprocess.run(
|
|
|
|
|
+ download_cmd,
|
|
|
|
|
+ check=True,
|
|
|
|
|
+ env=env,
|
|
|
|
|
+ capture_output=True,
|
|
|
|
|
+ text=True,
|
|
|
|
|
+ timeout=300
|
|
|
|
|
+ )
|
|
|
|
|
+
|
|
|
|
|
+ subprocess.run(['chmod', '+x', str(cloudflared_path)], check=True)
|
|
|
|
|
+ print("✅ Cloudflared downloaded successfully")
|
|
|
|
|
+ return str(cloudflared_path)
|
|
|
|
|
+
|
|
|
|
|
+ except Exception as e:
|
|
|
|
|
+ print(f"❌ Download failed: {e}")
|
|
|
|
|
+
|
|
|
|
|
+ # 备选方案:使用 wget
|
|
|
|
|
+ try:
|
|
|
|
|
+ wget_cmd = [
|
|
|
|
|
+ 'wget', '--no-check-certificate',
|
|
|
|
|
+ '--header', f'Host: github.com',
|
|
|
|
|
+ f'http://{github_ip}/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64',
|
|
|
|
|
+ '-O', str(cloudflared_path)
|
|
|
|
|
+ ]
|
|
|
|
|
+
|
|
|
|
|
+ subprocess.run(wget_cmd, check=True, env=env, timeout=300)
|
|
|
|
|
+ subprocess.run(['chmod', '+x', str(cloudflared_path)], check=True)
|
|
|
|
|
+ print("✅ Cloudflared downloaded with wget")
|
|
|
|
|
+ return str(cloudflared_path)
|
|
|
|
|
+
|
|
|
|
|
+ except Exception as e2:
|
|
|
|
|
+ print(f"❌ Wget also failed: {e2}")
|
|
|
|
|
+ return None
|
|
|
|
|
+
|
|
|
|
|
+def setup_cloudflare_tunnel_with_dns_fix(port=8101, timeout=120):
|
|
|
|
|
+ """使用 DNS 修复的 Cloudflare Tunnel 设置"""
|
|
|
|
|
+
|
|
|
|
|
+ print(f"🔄 Setting up Cloudflare Tunnel for port {port} with DNS fix...")
|
|
|
|
|
+
|
|
|
|
|
+ # 测试 DNS
|
|
|
|
|
+ test_dns_resolution()
|
|
|
|
|
+
|
|
|
|
|
+ # 下载 cloudflared
|
|
|
|
|
+ cloudflared_path = download_cloudflared_direct()
|
|
|
|
|
+ if not cloudflared_path:
|
|
|
|
|
+ print("❌ Could not download cloudflared")
|
|
|
|
|
+ return None
|
|
|
|
|
+
|
|
|
|
|
+ # 创建日志目录
|
|
|
|
|
+ log_dir = Path.home() / 'zhch' / 'logs'
|
|
|
|
|
+ log_dir.mkdir(exist_ok=True, parents=True)
|
|
|
|
|
+
|
|
|
|
|
+ # 准备命令
|
|
|
|
|
+ tunnel_cmd = [
|
|
|
|
|
+ cloudflared_path, 'tunnel',
|
|
|
|
|
+ '--url', f'http://localhost:{port}',
|
|
|
|
|
+ '--no-autoupdate',
|
|
|
|
|
+ '--logfile', str(log_dir / 'cloudflared.log'),
|
|
|
|
|
+ '--loglevel', 'debug' # 更详细的日志
|
|
|
|
|
+ ]
|
|
|
|
|
+
|
|
|
|
|
+ print(f"🚀 Starting tunnel: {' '.join(tunnel_cmd)}")
|
|
|
|
|
+
|
|
|
|
|
+ try:
|
|
|
|
|
+ env = setup_environment_with_dns()
|
|
|
|
|
+
|
|
|
|
|
+ # 显示环境配置
|
|
|
|
|
+ print("🔧 Environment configuration:")
|
|
|
|
|
+ for key in ['HTTP_PROXY', 'HTTPS_PROXY', 'GODEBUG', 'NAMESERVER']:
|
|
|
|
|
+ print(f" {key}: {env.get(key, 'Not set')}")
|
|
|
|
|
+
|
|
|
|
|
+ process = subprocess.Popen(
|
|
|
|
|
+ tunnel_cmd,
|
|
|
|
|
+ stdout=subprocess.PIPE,
|
|
|
|
|
+ stderr=subprocess.STDOUT,
|
|
|
|
|
+ text=True,
|
|
|
|
|
+ env=env,
|
|
|
|
|
+ bufsize=1,
|
|
|
|
|
+ universal_newlines=True
|
|
|
|
|
+ )
|
|
|
|
|
+
|
|
|
|
|
+ print("⏳ Waiting for tunnel to start...")
|
|
|
|
|
+ start_time = time.time()
|
|
|
|
|
+
|
|
|
|
|
+ while time.time() - start_time < timeout:
|
|
|
|
|
+ if process.poll() is not None:
|
|
|
|
|
+ stdout, _ = process.communicate()
|
|
|
|
|
+ print(f"❌ Process terminated early:")
|
|
|
|
|
+ print(stdout)
|
|
|
|
|
+ return None
|
|
|
|
|
+
|
|
|
|
|
+ try:
|
|
|
|
|
+ line = process.stdout.readline()
|
|
|
|
|
+ if line:
|
|
|
|
|
+ line = line.strip()
|
|
|
|
|
+ print(f"LOG: {line}")
|
|
|
|
|
+
|
|
|
|
|
+ # 查找成功的隧道 URL
|
|
|
|
|
+ if 'trycloudflare.com' in line:
|
|
|
|
|
+ url_match = re.search(r'https://[a-zA-Z0-9-]+\.trycloudflare\.com', line)
|
|
|
|
|
+ if url_match:
|
|
|
|
|
+ url = url_match.group(0)
|
|
|
|
|
+ print(f"✅ Tunnel URL found: {url}")
|
|
|
|
|
+ return url, process
|
|
|
|
|
+
|
|
|
|
|
+ # 检查特定错误
|
|
|
|
|
+ if 'dial tcp: lookup api.trycloudflare.com' in line:
|
|
|
|
|
+ print("❌ DNS lookup failed for api.trycloudflare.com")
|
|
|
|
|
+ print("🔧 Trying DNS workaround...")
|
|
|
|
|
+ break
|
|
|
|
|
+
|
|
|
|
|
+ if 'failed to request quick Tunnel' in line:
|
|
|
|
|
+ print("❌ Failed to create tunnel")
|
|
|
|
|
+ break
|
|
|
|
|
+
|
|
|
|
|
+ except Exception as e:
|
|
|
|
|
+ print(f"Error reading output: {e}")
|
|
|
|
|
+ break
|
|
|
|
|
+
|
|
|
|
|
+ time.sleep(0.5)
|
|
|
|
|
+
|
|
|
|
|
+ print(f"⏰ Timeout after {timeout} seconds")
|
|
|
|
|
+ process.terminate()
|
|
|
|
|
+ return None
|
|
|
|
|
+
|
|
|
|
|
+ except Exception as e:
|
|
|
|
|
+ print(f"❌ Failed to start tunnel: {e}")
|
|
|
|
|
+ return None
|
|
|
|
|
+
|
|
|
|
|
+def main():
|
|
|
|
|
+ port = 8101
|
|
|
|
|
+
|
|
|
|
|
+ print("🌐 Cloudflare Tunnel Setup with DNS Fix")
|
|
|
|
|
+ print("=" * 60)
|
|
|
|
|
+
|
|
|
|
|
+ # 检查网络连接
|
|
|
|
|
+ print("🔍 Network diagnostics:")
|
|
|
|
|
+
|
|
|
|
|
+ # 检查代理连接
|
|
|
|
|
+ try:
|
|
|
|
|
+ result = subprocess.run(['curl', '-s', '--connect-timeout', '5',
|
|
|
|
|
+ 'http://httpbin.org/ip'],
|
|
|
|
|
+ capture_output=True, text=True)
|
|
|
|
|
+ if result.returncode == 0:
|
|
|
|
|
+ print("✅ Proxy connection working")
|
|
|
|
|
+ else:
|
|
|
|
|
+ print("❌ Proxy connection failed")
|
|
|
|
|
+ except:
|
|
|
|
|
+ print("❌ Could not test proxy")
|
|
|
|
|
+
|
|
|
|
|
+ # 检查本地服务
|
|
|
|
|
+ import socket
|
|
|
|
|
+ try:
|
|
|
|
|
+ sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
|
|
|
+ sock.settimeout(2)
|
|
|
|
|
+ result = sock.connect_ex(('localhost', port))
|
|
|
|
|
+ sock.close()
|
|
|
|
|
+
|
|
|
|
|
+ if result == 0:
|
|
|
|
|
+ print(f"✅ Local service running on port {port}")
|
|
|
|
|
+ else:
|
|
|
|
|
+ print(f"⚠️ No service on port {port}")
|
|
|
|
|
+ except Exception as e:
|
|
|
|
|
+ print(f"❌ Error checking local service: {e}")
|
|
|
|
|
+
|
|
|
|
|
+ print()
|
|
|
|
|
+
|
|
|
|
|
+ # 设置隧道
|
|
|
|
|
+ result = setup_cloudflare_tunnel_with_dns_fix(port, timeout=180)
|
|
|
|
|
+
|
|
|
|
|
+ if result:
|
|
|
|
|
+ url, process = result
|
|
|
|
|
+ print(f"\n🎉 Success!")
|
|
|
|
|
+ print(f"🌐 Your service is accessible at: {url}")
|
|
|
|
|
+ print(f"🔗 Test it: {url}/docs")
|
|
|
|
|
+ print("\nPress Ctrl+C to stop")
|
|
|
|
|
+
|
|
|
|
|
+ def signal_handler(sig, frame):
|
|
|
|
|
+ print("\n🛑 Stopping tunnel...")
|
|
|
|
|
+ process.terminate()
|
|
|
|
|
+ try:
|
|
|
|
|
+ process.wait(timeout=5)
|
|
|
|
|
+ except subprocess.TimeoutExpired:
|
|
|
|
|
+ process.kill()
|
|
|
|
|
+ print("✅ Tunnel stopped")
|
|
|
|
|
+ sys.exit(0)
|
|
|
|
|
+
|
|
|
|
|
+ signal.signal(signal.SIGINT, signal_handler)
|
|
|
|
|
+
|
|
|
|
|
+ try:
|
|
|
|
|
+ while process.poll() is None:
|
|
|
|
|
+ try:
|
|
|
|
|
+ line = process.stdout.readline()
|
|
|
|
|
+ if line:
|
|
|
|
|
+ print(f"TUNNEL: {line.strip()}")
|
|
|
|
|
+ except KeyboardInterrupt:
|
|
|
|
|
+ break
|
|
|
|
|
+ finally:
|
|
|
|
|
+ process.terminate()
|
|
|
|
|
+ else:
|
|
|
|
|
+ print("\n❌ All attempts failed")
|
|
|
|
|
+ print("\n🔧 Alternative solutions:")
|
|
|
|
|
+ print("1. Ask network admin to whitelist *.trycloudflare.com")
|
|
|
|
|
+ print("2. Use VS Code built-in port forwarding")
|
|
|
|
|
+ print("3. Set up SSH tunnel to external server")
|
|
|
|
|
+ print("4. Use internal reverse proxy")
|
|
|
|
|
+
|
|
|
|
|
+if __name__ == "__main__":
|
|
|
|
|
+ main()
|