Fixed too-many-open-files ddos

This commit is contained in:
Marcel Hellkamp 2014-04-20 02:01:49 +02:00
parent 5a4cb85bea
commit ab69b8f86f
2 changed files with 71 additions and 40 deletions

View file

@ -2,6 +2,8 @@ import pixelflut
import os
import time
pixelcount = 0
def guess_IP():
import socket
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
@ -17,13 +19,14 @@ port = 1234
text = 'P1XELFLUT! v%s (%d)\n' % (
pixelflut.__version__,
os.stat(__file__).st_mtime)
text += 'Connect to %s:%d\n\n' % (IP, port)
text += '>>> HELP\n'
text += '>>> SIZE\n'
text += '>>> TEXT x y text\n'
text += '>>> PX x y [RRGGBB (hex)]\n'
text += '... and more ...\n\n'
text += 'H A C K O N\n'
text += '$ echo "HELP" | netcat %s %d\n' % (IP, port)
text += 'https://github.com/defnull/pixelflut'
help = 'Commands:'
help += '>>> HELP\n'
help += '>>> SIZE\n'
help += '>>> TEXT x y text\n'
help += '>>> PX x y [RRGGBB (hex)]\n'
@on('LOAD')
def callback(c):
@ -41,7 +44,7 @@ def on_resize(c):
@on('CONNECT')
def on_connect(c, client):
pass
#print c.clients.keys()
#print client
@on('KEYDOWN-c')
def on_key_c(c):
@ -60,12 +63,13 @@ def on_key_s(c):
@on('COMMAND-HELP')
def on_help(canvas, client):
client.send(text)
client.send(help)
@on('COMMAND-TEXT')
def on_text(canvas, client, x, y, *words):
x, y = int(x), int(y)
canvas.text(x, y, ' '.join(words), delay=0.1)
text = ' '.join(words)[:200]
canvas.text(x, y, text, delay=0.5)
@on('COMMAND-SIZE')
def on_size(canvas, client):
@ -77,20 +81,24 @@ def on_quit(canvas, client):
@on('COMMAND-PX')
def on_px(canvas, client, x, y, color=None):
global pixelcount
pixelcount += 1
client.last_pixel = time.time()
x, y = int(x), int(y)
if color:
c = int(color, 16)
if c <= 16777215:
if len(color) == 6:
r = (c & 0xff0000) >> 16
g = (c & 0x00ff00) >> 8
b = c & 0x0000ff
a = 0xff
else:
elif len(color) == 8:
r = (c & 0xff000000) >> 24
g = (c & 0x00ff0000) >> 16
b = (c & 0x0000ff00) >> 8
a = c & 0x000000ff
else:
return
canvas.set_pixel(x, y, r, g, b, a)
else:
r,g,b,a = canvas.get_pixel(x,y)
@ -99,10 +107,16 @@ def on_px(canvas, client, x, y, color=None):
last_save = 0
@on('TICK')
def on_tick(canvas, dt):
global last_save
global last_save, pixelcount
canvas.text(5, 5, text, delay=0)
if time.time() > last_save:
last_save = time.time() + 5
canvas.save_as('save/mov_%d.png' % last_save)
canvas.text(5, 5, text, delay=0)
print len(canvas.clients)
canvas.text(5, 200, 'px/s %d' % (pixelcount/5), delay=0)
canvas.text(5, 208, 'Connections %d' % len([c for c in canvas.clients.values() if c.socket]), delay=0)
pixelcount = 0

View file

@ -1,11 +1,11 @@
#coding: utf8
__version__ = '0.5'
__version__ = '0.6'
import time
from gevent import spawn, sleep as gsleep
from gevent.server import StreamServer
from gevent.coros import Semaphore
from gevent.socket import socket
from gevent.coros import Semaphore, RLock
from gevent.queue import Queue
from collections import deque
import pygame
@ -33,6 +33,7 @@ class Client(object):
# And this is used to limit clients to X messages per tick
# We start at 0 (instead of x) to add a reconnect-penalty.
self.limit = Semaphore(0)
self.lock = RLock()
def send(self, line):
self.sendbuffer.append(line.strip() + '\n')
@ -42,16 +43,21 @@ class Client(object):
self.sendbuffer.append(line.strip() + '\n')
def disconnect(self):
if self.socket:
self.socket.close()
self.socket = None
with self.lock:
if self.socket:
socket = self.socket
self.socket = None
socket.close()
log.info('Disconnect')
def serve(self, socket):
self.socket = socket
sendall = self.socket.sendall
readline = self.socket.makefile().readline
with self.lock:
self.socket = socket
sendall = self.socket.sendall
readline = self.socket.makefile().readline
try:
while True:
while self.socket:
self.limit.acquire()
# Idea: Send first, receive later. If the client is to
# slow to get the send-buffer empty, he cannot send.
@ -64,8 +70,9 @@ class Client(object):
command = arguments.pop(0)
try:
self.canvas.fire('COMMAND-%s' % command.upper(), self, *arguments)
except TypeError, e:
self.nospam('ERROR %r :(' % e)
except Exception, e:
socket.send('ERROR %r :(' % e)
break
finally:
self.disconnect()
@ -79,7 +86,7 @@ class Client(object):
class Canvas(object):
size = 640,480
size = 640, 480
flags = pygame.RESIZABLE#|pygame.FULLSCREEN
def __init__(self):
@ -95,24 +102,34 @@ class Canvas(object):
self.font = pygame.font.Font(None, 17)
def serve(self, host, port):
self.server = StreamServer((host, port), self.make_client)
self.server.start()
return spawn(self._loop)
self.host = host
self.port = port
spawn(self._loop)
self.socket = socket()
self.socket.bind((host, port))
self.socket.listen(500)
while True:
sock, addr = self.socket.accept()
ip, port = addr
log.info('Connect %s:%d', ip, port)
client = self.clients.get(ip)
if not client:
client = self.clients[ip] = Client(self)
def make_client(self, socket, address):
ip = address[0]
client = self.clients.get(ip)
if client:
client.disconnect()
else:
self.clients[ip] = client = Client(self)
spawn(self.handle_client, client, sock)
def handle_client(self, client, sock):
try:
self.fire('CONNECT', client)
client.serve(socket) # This blocks until ready
client.serve(sock) # This blocks until ready
self.fire('DISCONNECT', client)
finally:
client.disconnect()
client.disconnect()
def _loop(self):
while True:
@ -214,7 +231,7 @@ class Canvas(object):
if __name__ == '__main__':
logging.basicConfig()
logging.basicConfig(level=logging.DEBUG)
import optparse
parser = optparse.OptionParser("usage: %prog [options] brain_script")
@ -229,7 +246,7 @@ if __name__ == '__main__':
parser.error("incorrect number of arguments")
canvas = Canvas()
task = canvas.serve(options.hostname, options.portnum)
task = spawn(canvas.serve, options.hostname, options.portnum)
brainfile = args[0]
mtime = 0