Created
June 20, 2025 09:51
-
-
Save odysseus0/32e3af5d44c7335230ba501cf0ccfe53 to your computer and use it in GitHub Desktop.
IPv6 Pattern Test - The script that revealed the hotel WiFi issue
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/env python3 | |
| """ | |
| IPv6 Pattern Test - The script that revealed the hotel WiFi issue | |
| This is the actual script Claude Code wrote that tested multiple services | |
| and revealed the pattern: all real IPv6 addresses failed at TLS, | |
| while IPv6-mapped IPv4 addresses worked fine. | |
| When run at home, everything worked - revealing it was the hotel WiFi | |
| breaking IPv6 in a very specific way. | |
| Usage: python ipv6_pattern_test.py | |
| """ | |
| import asyncio | |
| import socket | |
| import ssl | |
| import time | |
| async def test_service(name, host, port=443): | |
| """Test both IPv4 and IPv6 for a service.""" | |
| print(f"\n{'='*60}") | |
| print(f"Testing: {name} ({host}:{port})") | |
| print('='*60) | |
| results = {} | |
| for ip_version in [6, 4]: | |
| family = socket.AF_INET if ip_version == 4 else socket.AF_INET6 | |
| try: | |
| # Get address | |
| addr_info = socket.getaddrinfo(host, port, family=family, proto=socket.IPPROTO_TCP) | |
| if not addr_info: | |
| results[f'ipv{ip_version}'] = "No address" | |
| print(f"IPv{ip_version}: No address found") | |
| continue | |
| addr = addr_info[0][4] | |
| print(f"\nIPv{ip_version}: {addr[0]}") | |
| # TCP test | |
| sock = socket.socket(family, socket.SOCK_STREAM) | |
| sock.settimeout(5) | |
| try: | |
| sock.connect(addr) | |
| print(f" ✅ TCP: Connected") | |
| # TLS test | |
| context = ssl.create_default_context() | |
| try: | |
| ssock = context.wrap_socket(sock, server_hostname=host) | |
| print(f" ✅ TLS: Handshake successful") | |
| results[f'ipv{ip_version}'] = "Full success" | |
| ssock.close() | |
| except Exception as e: | |
| print(f" ❌ TLS: {type(e).__name__}: {str(e)[:50]}") | |
| results[f'ipv{ip_version}'] = "TCP works, TLS fails" | |
| sock.close() | |
| except Exception as e: | |
| print(f" ❌ TCP: {type(e).__name__}: {str(e)[:50]}") | |
| results[f'ipv{ip_version}'] = "TCP fails" | |
| except Exception as e: | |
| results[f'ipv{ip_version}'] = f"Error: {type(e).__name__}" | |
| print(f"IPv{ip_version}: Error - {type(e).__name__}") | |
| return results | |
| async def main(): | |
| """Test various services to show the pattern.""" | |
| print("Comparing IPv6 behavior across different services") | |
| print("=" * 70) | |
| # Test various services | |
| services = [ | |
| ("TikAPI", "api.tikapi.io"), | |
| ("Google", "www.google.com"), | |
| ("Cloudflare", "cloudflare.com"), | |
| ("GitHub", "api.github.com"), | |
| ("OpenAI", "api.openai.com"), | |
| ] | |
| all_results = {} | |
| for name, host in services: | |
| all_results[name] = await test_service(name, host) | |
| # Summary | |
| print("\n" + "="*70) | |
| print("📋 SUMMARY TABLE") | |
| print("="*70) | |
| print(f"{'Service':<15} {'IPv6 Status':<30} {'IPv4 Status':<25}") | |
| print("-"*70) | |
| ipv6_real_count = 0 | |
| ipv6_mapped_count = 0 | |
| ipv6_broken_count = 0 | |
| for name, results in all_results.items(): | |
| ipv6 = results.get('ipv6', 'Unknown') | |
| ipv4 = results.get('ipv4', 'Unknown') | |
| # Check for IPv6-mapped IPv4 addresses | |
| if 'ipv6' in results: | |
| try: | |
| addr_info = socket.getaddrinfo(services[list(all_results.keys()).index(name)][1], 443, socket.AF_INET6) | |
| if addr_info and addr_info[0][4][0].startswith('::ffff:'): | |
| ipv6 = f"{ipv6} (IPv6-mapped IPv4)" | |
| ipv6_mapped_count += 1 | |
| else: | |
| ipv6_real_count += 1 | |
| if ipv6 == "TCP works, TLS fails": | |
| ipv6_broken_count += 1 | |
| except: | |
| pass | |
| # Highlight the broken pattern | |
| if ipv6 == "TCP works, TLS fails": | |
| ipv6 = f"⚠️ {ipv6}" | |
| print(f"{name:<15} {ipv6:<30} {ipv4:<25}") | |
| # The revealing pattern | |
| print("\n" + "="*70) | |
| print("🔍 THE PATTERN THAT REVEALED THE TRUTH") | |
| print("="*70) | |
| if ipv6_broken_count == ipv6_real_count and ipv6_mapped_count > 0: | |
| print("\n🚨 ALL real IPv6 addresses fail at TLS!") | |
| print("✅ But IPv6-mapped IPv4 addresses work fine") | |
| print("\nThis pattern indicates:") | |
| print("- It's NOT just TikAPI that's broken") | |
| print("- The network/firewall is breaking IPv6 at TLS level") | |
| print("- This defeats Happy Eyeballs because TCP succeeds") | |
| print("\n💡 When this script works at home but fails at hotel/office,") | |
| print(" you've found an enterprise firewall breaking IPv6!") | |
| if __name__ == "__main__": | |
| asyncio.run(main()) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment