#!/bin/sh
# ╔══════════════════════════════════════════════════════════════════════════════════╗
# ║  IMMORTAL TUNNEL CLIENT v8.1 - TRUE REVERSE PROXY                                ║
# ║                                                                                  ║
# ║  АРХИТЕКТУРА:                                                                    ║
# ║    СОФТ → СЕРВЕР:10001 ──[туннель]──► ЭТОТ КЛИЕНТ ──► Target ──► Internet       ║
# ║                                                                                  ║
# ║  МЕТОДЫ (fallback):                                                              ║
# ║    C (gcc) → Python3 → Python2 → Perl                                           ║
# ║    Каждый метод пробуется 2 раза перед переходом к следующему                   ║
# ║                                                                                  ║
# ║  НАДЁЖНОСТЬ:                                                                     ║
# ║    ✓ 999999 перезапусков             ✓ Exponential backoff                      ║
# ║    ✓ Auto fallback между методами    ✓ Health monitoring                        ║
# ╚══════════════════════════════════════════════════════════════════════════════════╝

VERSION="8.1.0"
MAX_RESTART=999999
TRIES_PER_METHOD=2

# Logging
log_info()  { printf "\033[92m[%s][INFO]\033[0m %s\n" "$(date +%H:%M:%S)" "$1" >&2; }
log_warn()  { printf "\033[93m[%s][WARN]\033[0m %s\n" "$(date +%H:%M:%S)" "$1" >&2; }
log_error() { printf "\033[91m[%s][ERROR]\033[0m %s\n" "$(date +%H:%M:%S)" "$1" >&2; }

# ═══════════════════════════════════════════════════════════════════════════════════
# METHOD 1: C CLIENT
# ═══════════════════════════════════════════════════════════════════════════════════

run_c_client() {
    local host="$1" port="$2" name="$3"
    local src="/tmp/ic_$$.c" bin="/tmp/ic_$$"
    
    cat > "$src" << 'CCODE'
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <time.h>
#include <signal.h>
#include <pthread.h>
#include <sys/socket.h>
#include <sys/select.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <netdb.h>

#define XOR_SIZE 50
#define MAX_STREAMS 200
#define BUFSZ 262144

static volatile int g_run = 1;
static unsigned char g_key[XOR_SIZE];
static int g_tun = -1;
static pthread_mutex_t g_lock = PTHREAD_MUTEX_INITIALIZER;
static time_t g_start;
static int g_conns = 0;

typedef struct { int fd, active; } Stream;
static Stream g_streams[MAX_STREAMS];

static void rc4(unsigned char *d, int n, const unsigned char *k) {
    if (n <= 0) return;
    unsigned char s[256]; int i, j;
    for (i = 0; i < 256; i++) s[i] = i;
    for (i = 0; i < n; i++) d[i] ^= k[i % XOR_SIZE];
    for (j = 0, i = 0; i < 256; i++) { j = (j + s[i] + k[i % XOR_SIZE]) & 255; unsigned char t = s[i]; s[i] = s[j]; s[j] = t; }
    for (int ii = 0, jj = 0, x = 0; x < n; x++) { ii = (ii + 1) & 255; jj = (jj + s[ii]) & 255; unsigned char t = s[ii]; s[ii] = s[jj]; s[jj] = t; d[x] ^= s[(s[ii] + s[jj]) & 255]; }
    for (i = 0; i < n; i++) d[i] ^= k[i % XOR_SIZE];
}

static int tsend(const void *d, int n) {
    pthread_mutex_lock(&g_lock);
    int r = send(g_tun, d, n, MSG_NOSIGNAL);
    pthread_mutex_unlock(&g_lock);
    return r;
}

static void *health(void *x) {
    while (g_run) { sleep(30); int s = 0; for (int i = 0; i < MAX_STREAMS; i++) if (g_streams[i].active) s++;
        fprintf(stderr, "\033[95m[%02d:%02d:%02d][HEALTH]\033[0m Up:%lds Streams:%d Conns:%d\n", 
            (int)(time(0)%86400/3600), (int)(time(0)%3600/60), (int)(time(0)%60), time(0)-g_start, s, g_conns); }
    return 0;
}

static void handle_connect(int sid, unsigned char *p, int len) {
    char host[256] = {0}; int port = 0;
    if (p[0] == 1 && len >= 7) { snprintf(host, 256, "%d.%d.%d.%d", p[1], p[2], p[3], p[4]); port = (p[5]<<8)|p[6]; }
    else if (p[0] == 3 && len >= 4) { int hl = p[1]; memcpy(host, p+2, hl); port = (p[2+hl]<<8)|p[3+hl]; }
    else { unsigned char r[4] = {sid, 0, 1, 5}; rc4(r, 3, g_key); rc4(r+3, 1, g_key); tsend(r, 4); return; }
    
    fprintf(stderr, "\033[96m[%02d:%02d:%02d][STREAM]\033[0m [%d] CONNECT %s:%d\n", 
        (int)(time(0)%86400/3600), (int)(time(0)%3600/60), (int)(time(0)%60), sid, host, port);
    
    struct hostent *he = gethostbyname(host);
    if (!he) { unsigned char r[4] = {sid, 0, 1, 5}; rc4(r, 3, g_key); rc4(r+3, 1, g_key); tsend(r, 4); return; }
    
    struct sockaddr_in a = {0}; a.sin_family = AF_INET; a.sin_port = htons(port); memcpy(&a.sin_addr, he->h_addr, 4);
    int fd = socket(AF_INET, SOCK_STREAM, 0);
    struct timeval tv = {30, 0}; setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
    
    if (connect(fd, (struct sockaddr*)&a, sizeof(a)) < 0) {
        close(fd); unsigned char r[4] = {sid, 0, 1, 5}; rc4(r, 3, g_key); rc4(r+3, 1, g_key); tsend(r, 4); return;
    }
    
    fcntl(fd, F_SETFL, O_NONBLOCK);
    g_streams[sid].fd = fd; g_streams[sid].active = 1; g_conns++;
    unsigned char r[4] = {sid, 0, 1, 0}; rc4(r, 3, g_key); rc4(r+3, 1, g_key); tsend(r, 4);
    fprintf(stderr, "\033[96m[%02d:%02d:%02d][STREAM]\033[0m [%d] CONNECTED\n",
        (int)(time(0)%86400/3600), (int)(time(0)%3600/60), (int)(time(0)%60), sid);
}

static void relay(int sid) {
    static unsigned char buf[BUFSZ + 8];
    int n = recv(g_streams[sid].fd, buf + 3, BUFSZ, 0);
    if (n <= 0) {
        unsigned char c[3] = {sid, 0, 0}; rc4(c, 3, g_key); tsend(c, 3);
        close(g_streams[sid].fd); g_streams[sid].active = 0;
        fprintf(stderr, "\033[96m[%02d:%02d:%02d][STREAM]\033[0m [%d] CLOSED\n",
            (int)(time(0)%86400/3600), (int)(time(0)%3600/60), (int)(time(0)%60), sid);
        return;
    }
    buf[0] = sid; buf[1] = (n >> 8) & 255; buf[2] = n & 255;
    rc4(buf, 3, g_key); rc4(buf + 3, n, g_key); tsend(buf, 3 + n);
}

int main(int argc, char **argv) {
    if (argc < 4) return 1;
    signal(SIGPIPE, SIG_IGN); srand(time(0) ^ getpid());
    g_start = time(0); memset(g_streams, 0, sizeof(g_streams));
    
    struct hostent *he = gethostbyname(argv[1]);
    if (!he) { fprintf(stderr, "DNS failed\n"); return 1; }
    struct sockaddr_in a = {0}; a.sin_family = AF_INET; a.sin_port = htons(atoi(argv[2])); memcpy(&a.sin_addr, he->h_addr, 4);
    
    g_tun = socket(AF_INET, SOCK_STREAM, 0);
    int opt = 1; setsockopt(g_tun, IPPROTO_TCP, TCP_NODELAY, &opt, sizeof(opt));
    if (connect(g_tun, (struct sockaddr*)&a, sizeof(a)) < 0) { fprintf(stderr, "Connect failed\n"); return 1; }
    
    for (int i = 0; i < XOR_SIZE; i++) g_key[i] = rand() & 255;
    unsigned char info[50] = {0xFF, 0xFF}; strncpy((char*)info+4, argv[3], 11);
    rc4(info, 50, g_key);
    unsigned char hs[100]; memcpy(hs, g_key, 50); memcpy(hs+50, info, 50);
    send(g_tun, hs, 100, 0);
    
    fcntl(g_tun, F_SETFL, O_NONBLOCK);
    fprintf(stderr, "\033[94m[%02d:%02d:%02d][TUNNEL]\033[0m Connected to %s:%s\n",
        (int)(time(0)%86400/3600), (int)(time(0)%3600/60), (int)(time(0)%60), argv[1], argv[2]);
    
    pthread_t ht; pthread_create(&ht, 0, health, 0);
    
    static unsigned char tbuf[BUFSZ * 2]; int tlen = 0;
    
    while (g_run) {
        fd_set r; FD_ZERO(&r); FD_SET(g_tun, &r); int mx = g_tun;
        for (int i = 0; i < MAX_STREAMS; i++) if (g_streams[i].active) { FD_SET(g_streams[i].fd, &r); if (g_streams[i].fd > mx) mx = g_streams[i].fd; }
        
        struct timeval tv = {1, 0};
        if (select(mx + 1, &r, 0, 0, &tv) < 0) { if (errno == EINTR) continue; break; }
        
        if (FD_ISSET(g_tun, &r)) {
            int n = recv(g_tun, tbuf + tlen, BUFSZ, 0);
            if (n <= 0) break;
            tlen += n;
            
            while (tlen >= 4) {
                unsigned char h[4]; memcpy(h, tbuf, 4); rc4(h, 4, g_key);
                int cmd = h[0], sid = h[1], dlen = (h[2] << 8) | h[3];
                if (tlen < 4 + dlen) break;
                
                unsigned char *p = tbuf + 4;
                if (dlen > 0) rc4(p, dlen, g_key);
                
                if (cmd == 0 && sid > 0 && sid < MAX_STREAMS) handle_connect(sid, p, dlen);
                else if (cmd == 1 && g_streams[sid].active) send(g_streams[sid].fd, p, dlen, MSG_NOSIGNAL);
                
                memmove(tbuf, tbuf + 4 + dlen, tlen - 4 - dlen); tlen -= 4 + dlen;
            }
        }
        
        for (int i = 0; i < MAX_STREAMS; i++)
            if (g_streams[i].active && FD_ISSET(g_streams[i].fd, &r)) relay(i);
    }
    
    close(g_tun);
    return 0;
}
CCODE

    gcc -O3 -o "$bin" "$src" -lpthread 2>/dev/null
    local ret=$?
    rm -f "$src"
    
    if [ $ret -eq 0 ] && [ -x "$bin" ]; then
        log_info "C client compiled"
        "$bin" "$host" "$port" "$name"
        ret=$?
        rm -f "$bin"
        return $ret
    else
        rm -f "$bin"
        return 1
    fi
}

# ═══════════════════════════════════════════════════════════════════════════════════
# METHOD 2: PYTHON CLIENT
# ═══════════════════════════════════════════════════════════════════════════════════

run_python_client() {
    local host="$1" port="$2" name="$3" py="${4:-python3}"
    
    "$py" -u - "$host" "$port" "$name" << 'PYCODE'
import socket, select, random, threading, time, sys, signal
XOR, MAXS, BUFSZ = 50, 200, 262144

class RC4:
    def __init__(s, k): s.k = k
    def crypt(s, d):
        d, n, k = bytearray(d), len(d), s.k
        if n == 0: return bytes(d)
        rc = list(range(256))
        for i in range(n): d[i] ^= k[i % XOR]
        j = 0
        for i in range(256): j = (j + rc[i] + k[i % XOR]) & 255; rc[i], rc[j] = rc[j], rc[i]
        ii = jj = 0
        for x in range(n): ii = (ii+1)&255; jj = (jj+rc[ii])&255; rc[ii], rc[jj] = rc[jj], rc[ii]; d[x] ^= rc[(rc[ii]+rc[jj])&255]
        for i in range(n): d[i] ^= k[i % XOR]
        return bytes(d)

class Client:
    def __init__(s, h, p, n):
        s.h, s.p, s.n = h, int(p), n[:11]
        s.k = bytes([random.randint(0,255) for _ in range(XOR)])
        s.c = RC4(s.k)
        s.tun = None
        s.st = {}
        s.run = True
        s.lk = threading.Lock()
        s.conns = 0
        s.start = time.time()
    
    def log(s, l, m):
        c = {'I':'92','W':'93','E':'91','T':'94','S':'96','H':'95'}.get(l[0],'0')
        print(f"\033[{c}m[{time.strftime('%H:%M:%S')}][{l}]\033[0m {m}", file=sys.stderr, flush=True)
    
    def connect(s):
        s.tun = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        s.tun.settimeout(30)
        s.tun.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
        s.tun.connect((s.h, s.p))
        info = bytearray(50); info[0], info[1] = 0xFF, 0xFF
        for i, b in enumerate(s.n.encode()[:11]): info[4+i] = b
        s.tun.sendall(s.k + s.c.crypt(bytes(info)))
        s.tun.setblocking(False)
        s.log('TUNNEL', f'Connected to {s.h}:{s.p}')
    
    def send(s, d):
        with s.lk:
            try: s.tun.sendall(d)
            except: pass
    
    def handle(s, sid, p):
        try:
            a = p[0]
            if a == 1: h, pt = f"{p[1]}.{p[2]}.{p[3]}.{p[4]}", (p[5]<<8)|p[6]
            elif a == 3: hl = p[1]; h = p[2:2+hl].decode(); pt = (p[2+hl]<<8)|p[3+hl]
            else: raise Exception("bad")
            s.log('STREAM', f'[{sid}] CONNECT {h}:{pt}')
            t = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            t.settimeout(30); t.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
            t.connect((h, pt)); t.setblocking(False)
            s.st[sid] = t
            r = bytearray([sid, 0, 1, 0]); r[:3] = s.c.crypt(bytes(r[:3])); r[3:] = s.c.crypt(bytes(r[3:]))
            s.send(bytes(r)); s.conns += 1
            s.log('STREAM', f'[{sid}] CONNECTED')
        except Exception as e:
            s.log('ERROR', f'[{sid}] {e}')
            r = bytearray([sid, 0, 1, 5]); r[:3] = s.c.crypt(bytes(r[:3])); r[3:] = s.c.crypt(bytes(r[3:]))
            s.send(bytes(r))
    
    def relay(s, sid):
        if sid not in s.st: return
        try:
            d = s.st[sid].recv(BUFSZ)
            if not d: raise Exception("closed")
            f = bytearray(3 + len(d)); f[0], f[1], f[2] = sid, (len(d)>>8)&255, len(d)&255; f[3:] = d
            f[:3] = s.c.crypt(bytes(f[:3])); f[3:] = s.c.crypt(bytes(f[3:]))
            s.send(bytes(f))
        except:
            cl = bytearray([sid, 0, 0]); s.send(s.c.crypt(bytes(cl)))
            try: s.st[sid].close()
            except: pass
            if sid in s.st: del s.st[sid]
            s.log('STREAM', f'[{sid}] CLOSED')
    
    def health(s):
        while s.run:
            time.sleep(30)
            s.log('HEALTH', f"Up:{int(time.time()-s.start)}s Streams:{len(s.st)} Conns:{s.conns}")
    
    def loop(s):
        threading.Thread(target=s.health, daemon=True).start()
        buf = b''
        while s.run:
            fds = [s.tun] + list(s.st.values())
            try: r, _, _ = select.select(fds, [], [], 1.0)
            except: continue
            for fd in r:
                if fd == s.tun:
                    try:
                        d = s.tun.recv(BUFSZ)
                        if not d: raise Exception("closed")
                        buf += d
                        while len(buf) >= 4:
                            h = s.c.crypt(buf[:4])
                            cmd, sid, dl = h[0], h[1], (h[2]<<8)|h[3]
                            if len(buf) < 4+dl: break
                            p = s.c.crypt(buf[4:4+dl]) if dl else b''
                            buf = buf[4+dl:]
                            if cmd == 0: s.handle(sid, p)
                            elif cmd == 1 and sid in s.st:
                                try: s.st[sid].sendall(p)
                                except: pass
                    except Exception as e:
                        s.log('ERROR', f'Tunnel: {e}'); s.run = False
                else:
                    for sid, sock in list(s.st.items()):
                        if sock == fd: s.relay(sid); break
        s.tun.close()

if __name__ == '__main__':
    signal.signal(signal.SIGPIPE, signal.SIG_IGN)
    c = Client(sys.argv[1], sys.argv[2], sys.argv[3])
    try: c.connect(); c.loop()
    except Exception as e: c.log('ERROR', str(e)); sys.exit(1)
PYCODE
}

# ═══════════════════════════════════════════════════════════════════════════════════
# METHOD 3: PERL CLIENT
# ═══════════════════════════════════════════════════════════════════════════════════

run_perl_client() {
    local host="$1" port="$2" name="$3"
    
    perl - "$host" "$port" "$name" << 'PERLCODE'
use strict; use warnings; use IO::Socket::INET; use IO::Select;
$| = 1; $SIG{PIPE} = 'IGNORE';

my ($h, $p, $n) = @ARGV;
my @k = map { int(rand(256)) } 1..50;
my %st; my $conns = 0; my $start = time();

sub log_msg { my ($l,$m)=@_; my %c=('I'=>92,'W'=>93,'E'=>91,'T'=>94,'S'=>96,'H'=>95);
    my $t=sprintf("%02d:%02d:%02d",(localtime)[2,1,0]); print STDERR "\033[$c{substr($l,0,1)}m[$t][$l]\033[0m $m\n"; }

sub rc4 { my $d = shift; my @d = unpack('C*', $d); my $n = @d; return '' if !$n;
    my @s = 0..255; my ($j,$ii,$jj) = (0,0,0);
    $d[$_] ^= $k[$_ % 50] for 0..$n-1;
    for (0..255) { $j = ($j + $s[$_] + $k[$_ % 50]) & 255; @s[$_,$j] = @s[$j,$_]; }
    for my $x (0..$n-1) { $ii = ($ii+1)&255; $jj = ($jj+$s[$ii])&255; @s[$ii,$jj] = @s[$jj,$ii]; $d[$x] ^= $s[($s[$ii]+$s[$jj])&255]; }
    $d[$_] ^= $k[$_ % 50] for 0..$n-1;
    return pack('C*', @d); }

sub tsend { syswrite($main::tun, shift); }

log_msg('INFO', "Perl client starting");
$main::tun = IO::Socket::INET->new(PeerAddr=>$h, PeerPort=>$p, Proto=>'tcp', Timeout=>30) or die "Connect: $!";
$main::tun->setsockopt(6, 1, 1);
log_msg('TUNNEL', "Connected to $h:$p");

my @info = (0xFF, 0xFF, (0)x48); my @nb = unpack('C*', substr($n,0,11)); $info[4+$_] = $nb[$_] for 0..$#nb;
syswrite($main::tun, pack('C*', @k) . rc4(pack('C*', @info)));

my $sel = IO::Select->new($main::tun);
my $buf = ''; my $lh = time();

while (1) {
    if (time() - $lh > 30) { log_msg('HEALTH', sprintf("Up:%ds Streams:%d Conns:%d", time()-$start, scalar keys %st, $conns)); $lh = time(); }
    
    my @r = $sel->can_read(1);
    for my $fh (@r) {
        if ($fh == $main::tun) {
            my $n = sysread($main::tun, my $d, 262144); die "Tunnel closed" if !$n;
            $buf .= $d;
            while (length($buf) >= 4) {
                my $hdr = rc4(substr($buf, 0, 4));
                my ($cmd, $sid, $dh, $dl) = unpack('CCCC', $hdr);
                my $dlen = ($dh << 8) | $dl;
                last if length($buf) < 4 + $dlen;
                my $pl = $dlen > 0 ? rc4(substr($buf, 4, $dlen)) : '';
                $buf = substr($buf, 4 + $dlen);
                
                if ($cmd == 0) {
                    my @pl = unpack('C*', $pl); my ($host, $pt);
                    if ($pl[0] == 1) { $host = join('.', @pl[1..4]); $pt = ($pl[5]<<8)|$pl[6]; }
                    elsif ($pl[0] == 3) { my $hl=$pl[1]; $host = pack('C*', @pl[2..1+$hl]); $pt = ($pl[2+$hl]<<8)|$pl[3+$hl]; }
                    else { my $r = pack('C4', $sid, 0, 1, 5); tsend(rc4(substr($r,0,3)).rc4(substr($r,3,1))); next; }
                    log_msg('STREAM', "[$sid] CONNECT $host:$pt");
                    my $t = IO::Socket::INET->new(PeerAddr=>$host, PeerPort=>$pt, Proto=>'tcp', Timeout=>30);
                    if ($t) { $t->setsockopt(6,1,1); $st{$sid} = $t; $sel->add($t);
                        my $r = pack('C4', $sid, 0, 1, 0); tsend(rc4(substr($r,0,3)).rc4(substr($r,3,1))); $conns++;
                        log_msg('STREAM', "[$sid] CONNECTED");
                    } else { my $r = pack('C4', $sid, 0, 1, 5); tsend(rc4(substr($r,0,3)).rc4(substr($r,3,1))); log_msg('ERROR', "[$sid] Connect failed"); }
                } elsif ($cmd == 1 && exists $st{$sid}) { syswrite($st{$sid}, $pl); }
            }
        } else {
            my ($sid) = grep { $st{$_} == $fh } keys %st; next unless defined $sid;
            my $n = sysread($fh, my $d, 262144);
            if ($n) { my $hdr = pack('CCC', $sid, ($n>>8)&255, $n&255); tsend(rc4($hdr) . rc4($d)); }
            else { tsend(rc4(pack('CCC', $sid, 0, 0))); $sel->remove($fh); close($fh); delete $st{$sid}; log_msg('STREAM', "[$sid] CLOSED"); }
        }
    }
}
PERLCODE
}

# ═══════════════════════════════════════════════════════════════════════════════════
# DETECT AVAILABLE METHODS
# ═══════════════════════════════════════════════════════════════════════════════════

get_methods() {
    local methods=""
    
    # C (если есть gcc)
    command -v gcc >/dev/null 2>&1 && methods="c $methods"
    
    # Python3
    command -v python3 >/dev/null 2>&1 && methods="$methods python3"
    
    # Python2
    command -v python >/dev/null 2>&1 && methods="$methods python"
    
    # Perl (если есть IO::Socket)
    if command -v perl >/dev/null 2>&1; then
        perl -e 'use IO::Socket::INET; use IO::Select;' 2>/dev/null && methods="$methods perl"
    fi
    
    echo "$methods"
}

# ═══════════════════════════════════════════════════════════════════════════════════
# RUN METHOD WITH FALLBACK
# ═══════════════════════════════════════════════════════════════════════════════════

run_with_fallback() {
    local host="$1" port="$2" name="$3"
    local methods=$(get_methods)
    
    [ -z "$methods" ] && { log_error "No methods available!"; return 1; }
    
    log_info "Available methods: $methods"
    
    for method in $methods; do
        local try=1
        while [ $try -le $TRIES_PER_METHOD ]; do
            log_info "Trying $method (attempt $try/$TRIES_PER_METHOD)"
            
            local start_time=$(date +%s)
            
            case "$method" in
                c) run_c_client "$host" "$port" "$name" ;;
                python3) run_python_client "$host" "$port" "$name" "python3" ;;
                python) run_python_client "$host" "$port" "$name" "python" ;;
                perl) run_perl_client "$host" "$port" "$name" ;;
            esac
            
            local ret=$?
            local runtime=$(($(date +%s) - start_time))
            
            # Если работал больше 30 секунд - это успешный запуск, вернуть код
            if [ $runtime -ge 30 ]; then
                return $ret
            fi
            
            # Если работал меньше 5 секунд - метод не работает
            if [ $runtime -lt 5 ]; then
                log_warn "$method failed quickly (${runtime}s), trying next..."
                break
            fi
            
            # Между 5 и 30 секунд - возможно временная ошибка, повторить
            try=$((try + 1))
            [ $try -le $TRIES_PER_METHOD ] && sleep 2
        done
    done
    
    log_error "All methods failed!"
    return 1
}

# ═══════════════════════════════════════════════════════════════════════════════════
# MAIN
# ═══════════════════════════════════════════════════════════════════════════════════

print_banner() {
    printf "\033[1;36m"
    cat << 'BANNER'
╔══════════════════════════════════════════════════════════════════════════════════╗
║  IMMORTAL TUNNEL CLIENT v8.1 - TRUE REVERSE PROXY                                ║
║                                                                                  ║
║  СОФТ → СЕРВЕР:port ──[туннель]──► ЭТОТ КЛИЕНТ ──► Internet                     ║
║                                                                                  ║
║  МЕТОДЫ: C → Python3 → Python → Perl (auto-fallback)                            ║
╚══════════════════════════════════════════════════════════════════════════════════╝
BANNER
    printf "\033[0m\n"
}

show_help() {
    echo "Usage: $0 SERVER:PORT [-n NAME]"
    echo ""
    echo "Options:"
    echo "  -n NAME    Client name (default: hostname)"
    echo ""
    echo "Example:"
    echo "  $0 1.2.3.4:8080 -n proxy1"
}

main() {
    local server="" name="${HOSTNAME:-$(hostname 2>/dev/null || echo 'client')}"
    
    while [ $# -gt 0 ]; do
        case "$1" in
            -n) shift; name="$1" ;;
            -h|--help) show_help; exit 0 ;;
            *) [ -z "$server" ] && server="$1" ;;
        esac
        shift
    done
    
    [ -z "$server" ] && { show_help; exit 1; }
    
    local host=$(echo "$server" | cut -d: -f1)
    local port=$(echo "$server" | cut -d: -f2)
    
    [ -z "$host" ] || [ -z "$port" ] && { log_error "Invalid format. Use HOST:PORT"; exit 1; }
    
    print_banner
    
    log_info "Version: $VERSION"
    log_info "Server: $host:$port"
    log_info "Name: $name"
    echo ""
    
    # Supervisor loop с fallback
    local restart=0 delay=1
    
    while [ $restart -lt $MAX_RESTART ]; do
        local start_time=$(date +%s)
        
        run_with_fallback "$host" "$port" "$name"
        
        local runtime=$(($(date +%s) - start_time))
        restart=$((restart + 1))
        
        # Reset delay if ran long enough
        [ $runtime -gt 60 ] && delay=1
        
        log_warn "Exited after ${runtime}s - restarting in ${delay}s (attempt $restart/$MAX_RESTART)"
        sleep $delay
        
        # Exponential backoff
        delay=$((delay * 2))
        [ $delay -gt 60 ] && delay=60
    done
    
    log_error "Max restarts reached!"
}

main "$@"
