Re: Месть ботам

Volodymyr Kostyrko c.kworr на gmail.com
Пн Май 7 06:32:26 UTC 2012


adept wrote:
> Проще сделать иптаблесом, в нем есть
> замечательная штука, TARPIT

Словно самому сложно написать. За два кило в коде можно ещё с рюшками и 
финтифлюшками.

Аттачу что писал полгода назад во время очередного ддоса. В локальном 
каталоге ищет файл tarpit.html с полным текстом ответа (с заголовками). 
Режет окно, слушает запрос и начинает отдавать содержимое файла по байту 
в секунду. Рекомендую не делать файл совсем большим так как за сутки 
соответственно отдаст только 85k.

Минус - жрёт память, в районе гига на 100k подключений. Сейчас бы писал 
на lua, да лень уже.

-- 
Sphinx of black quartz judge my vow.
----------- следущая часть -----------
#!/usr/bin/env python-shared
# vim:set fileencoding=utf-8

from __future__ import division, print_function, unicode_literals

import gevent.monkey
gevent.monkey.patch_all()

import datetime, gevent.pool, signal, socket, sys

answer = []
pool = gevent.pool.Pool()
port = 8081

pitted = {}
sockets = set()

for line in open('tarpit.html', 'rb'):
	answer += line

def serve_one(conn, addr):
	global answer, sockets
	sockets.add(conn)
	buf = ''
	if addr[0] in pitted:
		pitted[addr[0]] += 1
	else:
		pitted[addr[0]] = 1
	try:
		conn.settimeout(300)
		while True:
			gevent.sleep(1)
			byte = conn.recv(1)
			if byte == '':
				break
			if byte in ('\r', '\n'):
				buf += byte
				if buf == '\r\n\r\n':
					break
			else:
				buf = ''
		x = 0
		while True:
			gevent.sleep(1)
			conn.settimeout(300)
			x += conn.send(answer[x])
			conn.settimeout(0)
			try:
				conn.recv(1)
			except socket.error as err:
				if err[0] == 35:
					pass
				else:
					raise err
			if x >= len(answer):
				break
		conn.shutdown(socket.SHUT_RDWR)
		conn.close()
	except socket.error as err:
		if not type(err) == socket.timeout:
			if not err[0] in [32, 54]:
				print('disconnected:', repr(err), 'at line', sys.exc_info()[2].tb_lineno)
			if not err[0] in [1, 32, 54]:
				raise err
	finally:
		conn.close()
	if pitted[addr[0]] == 1:
		del pitted[addr[0]]
	else:
		pitted[addr[0]] -= 1
	sockets.discard(conn)

def listen(pool):
	s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
	s.setsockopt(socket.SOL_SOCKET, socket.SO_SNDBUF, 1)
	s.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, 1)
	sockets.add(s)
	s.bind(('', port))
	s.listen(256)
	print('Bound to port:', port)

	while True:
		try:
			pool.spawn(serve_one, *s.accept())
		except socket.error as err:
			if err[0] == 53:
				pass
			else:
				raise(err)

def siginfo_handler(signum, frame):
	print('tarpitted(', port, '):', len(pitted), 'addresses,', len(pool) - 1, 'connections')

signal.signal(signal.SIGINFO, siginfo_handler)

try:
	pool.spawn(listen, pool)
	pool.join()
except KeyboardInterrupt:
	pass
finally:
	print('killing sockets')
	for socket in sockets:
		socket.close()
	sys.exit(1)


Подробная информация о списке рассылки nginx-ru