Fading Coder

One Final Commit for the Last Sprint

Home > Tech > Content

Use IPv6 as an IPv4 Bridge to Revive Legacy TCP Games

Tech 2

Nginx delivers high performance but demands careful configuration. Many legacy games cannot support online multiplayer without a public IPv4 address, and using forwarding services from cloud providers adds cost and performance overhead due to NAT translation. Direct public IPv4 access is often blocked by carrier-grade NAT, making this approach unfeasible.

To address this, we can build an IPv4-IPv6-IPv4 TCP relay, using IPv6 as a bridge between two IPv4 endpoints. Below are two Python scripts to implement this relay for TCP traffic, which works for retro games such as Minecraft 1.7.2 servers.

Relay Server Script

This script runs on a machine with public IPv6 access, listening for IPv6 connections and forwarding traffic to a local IPv4 game server.

import socket
import select
import threading

def handle_client_session(client_sock):
    print("New client connection established")
    try:
        with socket.create_connection((REMOTE_HOST, REMOTE_PORT)) as remote_sock:
            monitored_socks = [client_sock, remote_sock]
            while monitored_socks:
                ready_socks, _, _ = select.select(monitored_socks, [], [])
                for sock in ready_socks:
                    data = sock.recv(4096)
                    if sock == client_sock:
                        if data:
                            remote_sock.sendall(data)
                        else:
                            monitored_socks.remove(sock)
                            remote_sock.close()
                            break
                    else:
                        if data:
                            client_sock.sendall(data)
                        else:
                            monitored_socks.remove(sock)
                            client_sock.close()
                            break
    finally:
        client_sock.close()

def run_relay_server():
    with socket.socket(socket.AF_INET6, socket.SOCK_STREAM) as server_sock:
        server_sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        server_sock.bind((LISTEN_ADDR, LISTEN_PORT))
        server_sock.listen(5)
        print(f"Relay server listening on [{LISTEN_ADDR}]:{LISTEN_PORT}")
        while True:
            client_conn, client_addr = server_sock.accept()
            print(f"Accepted connection from {client_addr}")
            worker_thread = threading.Thread(target=handle_client_session, args=(client_conn,))
            worker_thread.start()

if __name__ == "__main__":
    # Configure listening address (use :: to bind all IPv6 interfaces)
    LISTEN_ADDR = "::"
    # Port for the relay server to accept client connections
    LISTEN_PORT = 43325
    # Local IPv4 address of the legacy game server
    REMOTE_HOST = "127.0.0.1"
    # Port of the local legacy game server
    REMOTE_PORT = 64871
    run_relay_server()

Relay Client Script

This script runs on the client machine, listening for local game client connections and forwarding traffic to the remote IPv6 relay server.

import socket
import select
import threading

def handle_client_session(client_sock):
    print("New local client connection established")
    try:
        with socket.create_connection((RELAY_SERVER_ADDR, RELAY_SERVER_PORT)) as relay_sock:
            monitored_socks = [client_sock, relay_sock]
            while monitored_socks:
                ready_socks, _, _ = select.select(monitored_socks, [], [])
                for sock in ready_socks:
                    data = sock.recv(4096)
                    if sock == client_sock:
                        if data:
                            relay_sock.sendall(data)
                        else:
                            monitored_socks.remove(sock)
                            relay_sock.close()
                            break
                    else:
                        if data:
                            client_sock.sendall(data)
                        else:
                            monitored_socks.remove(sock)
                            client_sock.close()
                            break
    finally:
        client_sock.close()

def run_local_proxy():
    with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as proxy_sock:
        proxy_sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        proxy_sock.bind((LOCAL_LISTEN_ADDR, LOCAL_LISTEN_PORT))
        proxy_sock.listen(5)
        print(f"Local proxy listening on {LOCAL_LISTEN_ADDR}:{LOCAL_LISTEN_PORT}")
        while True:
            client_conn, client_addr = proxy_sock.accept()
            print(f"Accepted local connection from {client_addr}")
            worker_thread = threading.Thread(target=handle_client_session, args=(client_conn,))
            worker_thread.start()

if __name__ == "__main__":
    # Local listening address for the game client to connect to
    LOCAL_LISTEN_ADDR = "127.0.0.1"
    # Local port for the game client to use when connecting
    LOCAL_LISTEN_PORT = 46666
    # IPv6 address of the remote relay server
    RELAY_SERVER_ADDR = "2001:db8::1234"
    # Port of the remote relay server
    RELAY_SERVER_PORT = 43325
    run_local_proxy()

Configuration Notes

  1. On the relay server: Udpate REMOTE_HOST and REMOTE_PORT to match your local game server's address and port, then adjust LISTEN_PORT if needed.
  2. On the client machine: Replace RELAY_SERVER_ADDR with the public IPv6 address of the relay server, and set LOCAL_LISTEN_PORT to the port your game client will connect to.
  3. Launch the relay server first, then start the local proxy on the client machine, and configure your game client to connect to 127.0.0.1:[LOCAL_LISTEN_PORT].

While the core logic was assisted by an AI, refining the code to handle edge cases and improve readability required additional work. Future updates will focus on simplifying configuration and adding support for additional protocols.

Related Articles

Understanding Strong and Weak References in Java

Strong References Strong reference are the most prevalent type of object referencing in Java. When an object has a strong reference pointing to it, the garbage collector will not reclaim its memory. F...

Comprehensive Guide to SSTI Explained with Payload Bypass Techniques

Introduction Server-Side Template Injection (SSTI) is a vulnerability in web applications where user input is improper handled within the template engine and executed on the server. This exploit can r...

Implement Image Upload Functionality for Django Integrated TinyMCE Editor

Django’s Admin panel is highly user-friendly, and pairing it with TinyMCE, an effective rich text editor, simplifies content management significantly. Combining the two is particular useful for bloggi...

Leave a Comment

Anonymous

◎Feel free to join the discussion and share your thoughts.