Skip to content

Instantly share code, notes, and snippets.

@odysseus0
Created June 20, 2025 09:51
Show Gist options
  • Save odysseus0/32e3af5d44c7335230ba501cf0ccfe53 to your computer and use it in GitHub Desktop.
Save odysseus0/32e3af5d44c7335230ba501cf0ccfe53 to your computer and use it in GitHub Desktop.
IPv6 Pattern Test - The script that revealed the hotel WiFi issue
#!/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