1#!/usr/local/bin/python3.8 2 3# Copyright (C) 2007 Giampaolo Rodola' <g.rodola@gmail.com>. 4# Use of this source code is governed by MIT license that can be 5# found in the LICENSE file. 6 7""" 8A FTP server banning clients in case of commands flood. 9 10If client sends more than 300 requests per-second it will be 11disconnected and won't be able to re-connect for 1 hour. 12""" 13 14from pyftpdlib.authorizers import DummyAuthorizer 15from pyftpdlib.handlers import FTPHandler 16from pyftpdlib.servers import FTPServer 17 18 19class AntiFloodHandler(FTPHandler): 20 21 cmds_per_second = 300 # max number of cmds per second 22 ban_for = 60 * 60 # 1 hour 23 banned_ips = [] 24 25 def __init__(self, *args, **kwargs): 26 FTPHandler.__init__(self, *args, **kwargs) 27 self.processed_cmds = 0 28 self.pcmds_callback = \ 29 self.ioloop.call_every(1, self.check_processed_cmds) 30 31 def on_connect(self): 32 # called when client connects. 33 if self.remote_ip in self.banned_ips: 34 self.respond('550 You are banned.') 35 self.close_when_done() 36 37 def check_processed_cmds(self): 38 # called every second; checks for the number of commands 39 # sent in the last second. 40 if self.processed_cmds > self.cmds_per_second: 41 self.ban(self.remote_ip) 42 else: 43 self.processed_cmds = 0 44 45 def process_command(self, *args, **kwargs): 46 # increase counter for every received command 47 self.processed_cmds += 1 48 FTPHandler.process_command(self, *args, **kwargs) 49 50 def ban(self, ip): 51 # ban ip and schedule next un-ban 52 if ip not in self.banned_ips: 53 self.log('banned IP %s for command flooding' % ip) 54 self.respond('550 You are banned for %s seconds.' % self.ban_for) 55 self.close() 56 self.banned_ips.append(ip) 57 58 def unban(self, ip): 59 # unban ip 60 try: 61 self.banned_ips.remove(ip) 62 except ValueError: 63 pass 64 else: 65 self.log('unbanning IP %s' % ip) 66 67 def close(self): 68 FTPHandler.close(self) 69 if not self.pcmds_callback.cancelled: 70 self.pcmds_callback.cancel() 71 72 73def main(): 74 authorizer = DummyAuthorizer() 75 authorizer.add_user('user', '12345', '.', perm='elradfmwMT') 76 authorizer.add_anonymous('.') 77 handler = AntiFloodHandler 78 handler.authorizer = authorizer 79 server = FTPServer(('', 2121), handler) 80 server.serve_forever(timeout=1) 81 82 83if __name__ == '__main__': 84 main() 85