A internet é mesmo um lugar maluco. Eu não sou muito de jogar, mas nos últimos meses um amigo me mostrou o urban terror, um jogo no estilo counter strike que é gratuito, baseado na engine do quake3 arena.
Dias depois fui banido de um dos servidores mais populares do negócio, o bashfull, e então desencanei e nunca mais joguei lá. Porém, várias vezes que fui jogar, não havia ninguém nos outros servidores, e lá estava sempre cheio de gente.

Tentei o meio normal da coisa e mandei 2 emails pro admin, nos últimos 20 dias. Como fui ignorado, e meu IP do virtua é fixo, não sobrou outra alternativa a não ser usar um proxy no estilo BNC, tipo os que eu usava a uns 10 anos atrás nas épocas do irc.
Pra terminar, não achei nada que preste pra fazer proxy Nx1 via udp – talvez eu seja burro de mais pra procurar essas bobeiras – e então acabei escrevendo um em python.
Em 20 minutos, fiz tudo e testei em um ubuntu. Ficou bem simples, veja ai:
#!/usr/bin/env python
# coding: utf-8
# 20081225 AF
#
# urban terror asynchronous udp proxy
# requires python 2.5 (linux, bsd, etc)
# configuration
maxclients = 10
proxyport = 27960
ut_server_addr = '189.38.63.196'
ut_server_port = 27962
# don't touch anything below.
import os, sys, time, syslog, socket, select, threading
clients = {}
timeout = 15
pktsize = 4096
infolog = lambda msg: syslog.syslog(syslog.LOG_INFO, msg)
def daemonize():
# double fork, ignore exceptions
pid = os.fork()
if not pid:
os.setsid()
pid = os.fork()
if not pid:
os.chdir('/')
[k.close() for k in sys.stdin, sys.stdout, sys.stderr]
else:
os._exit(0)
else:
os._exit(0)
def cleaner():
# remove inactive slots
while 1:
now = time.time()
for slot in clients.values():
if now - slot.time > timeout:
infolog('client %s disconnected' % str(slot.addr))
del clients[slot.addr]
time.sleep(5.0)
class proxy:
def __init__(self, addr):
self.addr = addr
self.time = time.time()
self.fd = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
self.fd.connect((ut_server_addr, ut_server_port))
def send(self, chunk):
self.time = time.time()
return self.fd.send(chunk)
if __name__ == '__main__':
fd = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
fd.bind(('', proxyport))
daemonize()
syslog.openlog('utproxy', syslog.LOG_PID, syslog.LOG_DAEMON)
threading.Thread(target=cleaner).start()
while 1:
current = [k.fd for k in clients.values()] + [fd]
rr, rw, er = select.select(current, [], [])
if fd in rr:
chunk, addr = fd.recvfrom(pktsize)
if addr in clients.keys(): # from proxy (cli=addr) to server
clients[addr].send(chunk)
else:
if len(clients) < maxclients:
infolog('client %s connected' % str(addr))
clients[addr] = proxy(addr)
clients[addr].send(chunk)
else:
syslog.syslog(syslog.LOG_INFO,
'client %s rejected: proxy is full' % str(addr))
for cli in clients.values():
if cli.fd in rr: # from server to proxy (cli=addr)
chunk = cli.fd.recv(pktsize)
fd.sendto(chunk, cli.addr)
Como o proxy não precisa fazer absolutamente nenhum processamento, todo o repasse de pacotes é feito de modo assíncrono, em um único processo, e uma thread verifica as conexões inativas para removê-las da lista.
Até poderia fazer o proxy de outras maneiras, talvez usando asynchat ou mesmo twisted, mas acabei optando pelo modo tradicional e o resultado é que ele não depende de nenhum módulo externo, apenas coisas básicas do python 2.5.

Agora, quando entrar lá, só vou matar o bashfull. Meu nick é /dev/null.

Comentários