Forbid empty DNS names
[regano.git] / db_antiskid.sql
blob532f23930e43131cfe2e58c7c347ad116d53c627
1 -- Regano database DoS protection module
2 --
3 -- Uses PostgreSQL extensions.
4 --
5 --  Regano is a domain registration system for OpenNIC TLDs written in
6 --  Perl.  This file is part of Regano.
7 --
8 --  Regano may be distributed under the same terms as Perl itself.  Of
9 --  particular importance, note that while regano is distributed in the
10 --  hope that it will be useful, there is NO WARRANTY OF ANY KIND
11 --  WHATSOEVER WHETHER EXPLICIT OR IMPLIED.
13 -- The type definitions in db_types.sql must already be installed.
14 -- The function definitions in db_functions.sql must already be installed.
15 -- The main API definitions in db_api.sql must already be installed.
16 -- The configuration in db_config.sql must be loaded for these to actually work.
18 -- Currently blocked clients
19 CREATE TABLE IF NOT EXISTS regano.antiskid_block (
20         client          inet PRIMARY KEY,
21         blocked         timestamp with time zone
22                                 NOT NULL DEFAULT CURRENT_TIMESTAMP
23 ) WITH (fillfactor = 90);
24 CREATE INDEX ON regano.antiskid_block (blocked);
26 -- Tally of expensive operations
27 CREATE TABLE IF NOT EXISTS regano.antiskid_tally (
28         client          inet NOT NULL,
29         points          integer NOT NULL
30                                 CHECK(points > 0),
31         tallied         timestamp with time zone
32                                 NOT NULL DEFAULT CURRENT_TIMESTAMP,
33         PRIMARY KEY(client, tallied, points)
35 CREATE INDEX ON regano.antiskid_tally (tallied);
37 ALTER TABLE regano.antiskid_block OWNER TO regano;
38 ALTER TABLE regano.antiskid_tally OWNER TO regano;
40 -- Check if a client may perform an expensive operation.
41 CREATE OR REPLACE FUNCTION regano_api.antiskid_check
42         (op text, addr inet)
43         RETURNS boolean AS $$
44 DECLARE
45     block_expire        CONSTANT interval NOT NULL
46                             := (regano.config_get('antiskid/block')).interval;
47     points_expire       CONSTANT interval NOT NULL
48                             := (regano.config_get('antiskid/tally')).interval;
49     points_threshold    CONSTANT integer NOT NULL
50                             := (regano.config_get('antiskid/tally')).number;
52     add_points          integer NOT NULL
53                             := (regano.config_get('antiskid/weight/'||op)).number;
54     current_points      integer;
56     block               regano.antiskid_block%ROWTYPE;
57 BEGIN
58     -- clean up blocked client list and check if client is blocked
59     DELETE
60         FROM regano.antiskid_block
61         WHERE blocked < (now() - block_expire);
62     SELECT * INTO block
63         FROM regano.antiskid_block
64         WHERE client = addr;
65     IF FOUND THEN
66         IF block.blocked < (now() - (block_expire / 2)) THEN
67             UPDATE regano.antiskid_block
68                 SET blocked = now()
69                 WHERE client = addr;
70         END IF;
71         RETURN FALSE;
72     END IF;
74     -- tally the new points and check if client should be blocked
75     INSERT INTO regano.antiskid_tally (client, points)
76         VALUES (addr, add_points);
77     SELECT SUM(points) INTO STRICT current_points
78         FROM regano.antiskid_tally
79         WHERE client = addr AND tallied >= (now() - points_expire);
80     IF current_points > points_threshold THEN
81         INSERT INTO regano.antiskid_block (client) VALUES (addr);
82         RETURN FALSE;
83     END IF;
85     -- clean up point tallies and return success
86     DELETE
87         FROM regano.antiskid_tally
88         WHERE tallied < (now() - points_expire);
89     RETURN TRUE;
90 END;
91 $$ LANGUAGE plpgsql VOLATILE STRICT SECURITY DEFINER;
92 ALTER FUNCTION regano_api.antiskid_check (text, inet)
93         OWNER TO regano;
95 -- Check for valid session from non-blocked client
96 CREATE OR REPLACE FUNCTION regano_api.antiskid_session_check
97         (id uuid, addr inet)
98         RETURNS text AS $$
99 DECLARE
100     block_expire        CONSTANT interval NOT NULL
101                             := (regano.config_get('antiskid/block')).interval;
102 BEGIN
103     PERFORM *
104         FROM regano.antiskid_block
105         WHERE client = addr AND blocked > (now() - block_expire);
106     IF FOUND THEN
107         RETURN NULL;
108     ELSE
109         RETURN regano_api.session_check(id);
110     END IF;
111 END;
112 $$ LANGUAGE plpgsql VOLATILE STRICT SECURITY DEFINER;
113 ALTER FUNCTION regano_api.antiskid_session_check (uuid, inet)
114         OWNER TO regano;