4 # pyftpdlib is released under the MIT license, reproduced below:
5 # ======================================================================
6 # Copyright (C) 2007-2012 Giampaolo Rodola' <g.rodola@gmail.com>
10 # Permission is hereby granted, free of charge, to any person
11 # obtaining a copy of this software and associated documentation
12 # files (the "Software"), to deal in the Software without
13 # restriction, including without limitation the rights to use,
14 # copy, modify, merge, publish, distribute, sublicense, and/or sell
15 # copies of the Software, and to permit persons to whom the
16 # Software is furnished to do so, subject to the following
19 # The above copyright notice and this permission notice shall be
20 # included in all copies or substantial portions of the Software.
22 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
23 # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
24 # OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
25 # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
26 # HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
27 # WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
28 # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
29 # OTHER DEALINGS IN THE SOFTWARE.
31 # ======================================================================
34 A FTP server banning clients in case of commands flood.
36 If client sends more than 300 requests per-second it will be
37 disconnected and won't be able to re-connect for 1 hour.
40 from pyftpdlib.ftpserver import FTPHandler, FTPServer, DummyAuthorizer, CallEvery
43 class AntiFloodHandler(FTPHandler):
45 cmds_per_second = 300 # max number of cmds per second
46 ban_for = 60 * 60 # 1 hour
49 def __init__(self, *args, **kwargs):
50 super(AntiFloodHandler, self).__init__(*args, **kwargs)
51 self.processed_cmds = 0
52 self.pcmds_callback = CallEvery(1, self.check_processed_cmds)
55 # called when client connects.
56 if self.remote_ip in self.banned_ips:
57 self.respond('550 you are banned')
60 super(AntiFloodHandler, self).handle()
62 def check_processed_cmds(self):
63 # called every second; checks for the number of commands
64 # sent in the last second.
65 if self.processed_cmds > self.cmds_per_second:
66 self.ban(self.remote_ip)
68 self.processed_cmds = 0
70 def process_command(self, *args, **kwargs):
71 # increase counter for every received command
72 self.processed_cmds += 1
73 super(AntiFloodHandler, self).process_command(*args, **kwargs)
76 # ban ip and schedule next un-ban
77 if ip not in self.banned_ips:
78 self.log('banned IP %s for command flooding' % ip)
79 self.respond('550 you are banned for %s seconds' % self.ban_for)
81 self.banned_ips.append(ip)
86 self.banned_ips.remove(ip)
90 self.log('unbanning IP %s' % ip)
93 super(AntiFloodHandler, self).close()
94 if not self.pcmds_callback.cancelled:
95 self.pcmds_callback.cancel()
99 authorizer = DummyAuthorizer()
100 authorizer.add_user('user', '12345', '.', perm='elradfmw')
101 authorizer.add_anonymous('.')
102 handler = AntiFloodHandler
103 handler.authorizer = authorizer
104 ftpd = FTPServer(('', 21), handler)
105 ftpd.serve_forever(timeout=1)
107 if __name__ == '__main__':