forked from szepeviktor/zdkimfilter
-
Notifications
You must be signed in to change notification settings - Fork 0
/
odbx_example-1.1.sql
281 lines (245 loc) · 8.63 KB
/
odbx_example-1.1.sql
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
# zdkimfilter database example using MySQL
CREATE DATABASE IF NOT EXISTS test_zfilter;
# GRANT SELECT, INSERT, UPDATE, EXECUTE, DELETE ON test_zfilter.* TO 'zfilter'@'localhost'
USE test_zfilter;
# domains that we exchange mail with
#
DROP TABLE IF EXISTS domain;
CREATE TABLE domain (
id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
domain VARCHAR(63) NOT NULL,
recv INT UNSIGNED NOT NULL DEFAULT 0,
sent INT UNSIGNED NOT NULL DEFAULT 0,
whitelisted TINYINT NOT NULL DEFAULT 0,
since TIMESTAMP NOT NULL DEFAULT NOW(),
last TIMESTAMP NOT NULL DEFAULT 0,
INDEX by_dom(domain(16))
)
ENGINE = MyISAM
CHARACTER SET ascii COLLATE ascii_general_ci;
# many-to-many link between domains and received messages
#
DROP TABLE IF EXISTS msg_ref;
CREATE TABLE msg_ref (
id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
message_in INT UNSIGNED NOT NULL COMMENT 'Foreign key to message_in',
domain INT UNSIGNED NOT NULL COMMENT 'Foreign key to domain',
reputation INT NOT NULL,
auth SET ('author', 'spf_helo', 'spf', 'dkim', 'vbr', 'rep', 'rep_s') NOT NULL,
vbr ENUM ('spamhaus', 'who_else') NOT NULL,
INDEX by_dom_msg(domain, message_in)
)
ENGINE = MyISAM
CHARACTER SET ascii COLLATE ascii_general_ci;
# received messages
#
DROP TABLE IF EXISTS message_in;
CREATE TABLE message_in (
id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
ino BIGINT UNSIGNED NOT NULL,
mtime BIGINT UNSIGNED NOT NULL,
pid BIGINT UNSIGNED NOT NULL,
ip BINARY(4) NOT NULL COMMENT 'Ok for IPv4. For IPv6 use VARBINARY(16)',
date VARCHAR(63),
message_id VARCHAR(63),
envelope_sender VARCHAR(63) NOT NULL DEFAULT '',
content_type VARCHAR(63) NOT NULL DEFAULT 'text/plain',
content_encoding VARCHAR(63) NOT NULL DEFAULT '7bit',
received_count SMALLINT UNSIGNED NOT NULL,
signatures_count SMALLINT UNSIGNED NOT NULL,
mailing_list TINYINT NOT NULL DEFAULT 0,
score SMALLINT DEFAULT NULL COMMENT 'NULL if not tested',
UNIQUE KEY (mtime, pid, ino)
)
ENGINE = MyISAM
CHARACTER SET ascii COLLATE ascii_general_ci;
# user table
#
DROP TABLE IF EXISTS user;
CREATE TABLE user (
id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
addr VARCHAR(63) NOT NULL,
INDEX by_addr(addr(16))
)
ENGINE = MyISAM
CHARACTER SET ascii COLLATE ascii_general_ci;
# sent messages
#
DROP TABLE IF EXISTS message_out;
CREATE TABLE message_out (
id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
ino BIGINT UNSIGNED NOT NULL,
mtime BIGINT UNSIGNED NOT NULL,
pid BIGINT UNSIGNED NOT NULL,
user INT UNSIGNED NOT NULL COMMENT 'Foreign key to user',
ip BINARY(4) NOT NULL COMMENT 'Ok for IPv4. For IPv6 use VARBINARY(16)',
rcpt_count INT UNSIGNED NOT NULL DEFAULT 1,
date VARCHAR(63),
message_id VARCHAR(63),
envelope_sender VARCHAR(63) NOT NULL DEFAULT '',
content_type VARCHAR(63) NOT NULL DEFAULT 'text/plain',
content_encoding VARCHAR(63) NOT NULL DEFAULT '7bit',
UNIQUE KEY (mtime, pid, ino)
)
ENGINE = MyISAM
CHARACTER SET ascii COLLATE ascii_general_ci;
# many-to-many link between domains and sent messages
#
DROP TABLE IF EXISTS msg_out_ref;
CREATE TABLE msg_out_ref (
id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
message_out INT UNSIGNED NOT NULL COMMENT 'Foreign key to message_out',
domain INT UNSIGNED NOT NULL COMMENT 'Foreign key to domain',
INDEX by_dom_msg_out(domain, message_out)
)
ENGINE = MyISAM
CHARACTER SET ascii COLLATE ascii_general_ci;
delimiter //
# Called by db_sql_insert_msg_ref:
# Insert/update domain, insert msg_ref
#
DROP PROCEDURE IF EXISTS recv_from_domain//
CREATE PROCEDURE recv_from_domain (
IN m_mi INT UNSIGNED,
IN m_domain VARCHAR(63),
IN m_auth SET ('author', 'spf_helo', 'spf', 'dkim', 'vbr', 'rep', 'rep_s'),
IN m_vbr VARCHAR(63),
IN m_rep INT)
MODIFIES SQL DATA
BEGIN
DECLARE d_id INT UNSIGNED;
DECLARE d_white TINYINT;
DECLARE Empty_set CONDITION FOR 1329;
DECLARE CONTINUE HANDLER FOR Empty_set
BEGIN
INSERT INTO domain SET domain = m_domain;
SELECT LAST_INSERT_ID() INTO d_id;
SET d_white = 0;
END;
SELECT id, whitelisted INTO d_id, d_white
FROM domain WHERE domain = m_domain;
IF d_white < 1 AND FIND_IN_SET('dkim', m_auth) THEN
# whitelisted=1 just affects the order of signature validation attempts
UPDATE domain SET whitelisted = 1,
recv = recv + 1, last = NOW() WHERE id = d_id;
ELSE
UPDATE domain SET recv = recv + 1,
last = NOW() WHERE id = d_id;
END IF;
INSERT INTO msg_ref SET message_in = m_mi,
domain = d_id,
auth = m_auth,
reputation = m_rep,
vbr = IF(STRCMP(m_vbr, 'dwl.spamhaus.org') = 0, '(spamhaus)', '()');
END //
# Called by db_sql_select_user:
# Insert/update user, insert message_out'
#
DROP PROCEDURE IF EXISTS sent_message //
CREATE PROCEDURE sent_message (
IN m_addr VARCHAR(63),
IN m_ino BIGINT UNSIGNED,
IN m_mtime BIGINT UNSIGNED,
IN m_pid BIGINT UNSIGNED,
IN m_ip BINARY(4), # for IPv6 use VARBINARY(16)
IN m_date VARCHAR(63),
IN m_id VARCHAR(63),
IN m_es VARCHAR(63),
IN m_ct VARCHAR(63),
IN m_ce VARCHAR(63),
IN m_rcpt INT UNSIGNED)
MODIFIES SQL DATA
BEGIN
DECLARE user_ref INT UNSIGNED;
DECLARE Empty_set CONDITION FOR 1329;
DECLARE CONTINUE HANDLER FOR Empty_set
BEGIN
INSERT INTO user SET addr = m_addr;
SELECT LAST_INSERT_ID() INTO user_ref;
END;
SELECT id INTO user_ref FROM user WHERE addr = m_addr LIMIT 1;
INSERT INTO message_out SET ino = m_ino,
mtime = m_mtime,
pid = m_pid,
ip = m_ip,
user = user_ref,
date = m_date,
message_id = m_id,
envelope_sender = m_es,
content_type = m_ct,
content_encoding = m_ce,
rcpt_count = m_rcpt;
SELECT user_ref, LAST_INSERT_ID() AS message_ref;
END //
# Called by db_sql_insert_target_ref:
# Insert/update domain, insert msg_out_ref
#
DROP PROCEDURE IF EXISTS sent_to_domain //
CREATE PROCEDURE sent_to_domain (
IN message_ref INT UNSIGNED,
IN m_domain VARCHAR(63))
MODIFIES SQL DATA
BEGIN
DECLARE d_id INT UNSIGNED;
DECLARE d_white TINYINT;
DECLARE Empty_set CONDITION FOR 1329;
DECLARE CONTINUE HANDLER FOR Empty_set
BEGIN
INSERT INTO domain SET domain = m_domain;
SELECT LAST_INSERT_ID() INTO d_id;
SET d_white = 0;
END;
SELECT id, whitelisted INTO d_id, d_white
FROM domain WHERE domain = m_domain;
IF d_white < 2 THEN
# whitelisted=2 prevents ADSP discard; whitelisted=3 is not used yet
UPDATE domain SET whitelisted = 2,
sent = sent + 1,
last = NOW() WHERE id = d_id;
ELSE
UPDATE domain SET sent = sent + 1,
last = NOW() WHERE id = d_id;
END IF;
INSERT INTO msg_out_ref SET message_out = message_ref,
domain = d_id;
END //
delimiter ;
# example query, to see who signed what messages:
SELECT INET_NTOA(CONV(HEX(m.ip),16,10)), m.date, FROM_UNIXTIME(m.mtime), d.domain, r.auth
FROM msg_ref AS r, message_in AS m, domain AS d
WHERE r.domain=d.id AND r.message_in=m.id AND FIND_IN_SET('dkim', r.auth);
# how many new domains have been added today?
SELECT COUNT(*) FROM domain WHERE since > (NOW() - INTERVAL 1 DAY);
# how many messages did each of them send?
SELECT d.id, d.domain, r.auth, COUNT(*) AS cnt
FROM domain AS d, msg_ref AS r, message_in AS m
WHERE d.id = r.domain AND r.message_in = m.id AND (d.since > NOW() - INTERVAL 1 DAY)
GROUP BY d.id, r.auth ORDER BY cnt DESC LIMIT 10;
# how many messages did they send as a whole?
SELECT count(*)
FROM domain AS d, msg_ref AS r, message_in AS m
WHERE d.id = r.domain AND r.message_in = m.id AND (d.since > NOW() - INTERVAL 1 DAY);
# delete incoming messages older than 1 month
DELETE r, m FROM msg_ref AS r, message_in AS m
WHERE r.message_in = m.id AND m.mtime < UNIX_TIMESTAMP(NOW() - INTERVAL 1 MONTH);
# find domains having been orphaned that way
SELECT l.* FROM domain AS l LEFT JOIN msg_ref AS r ON r.domain = l.id
WHERE r.domain IS NULL AND l.recv > 0;
# delete outgoing messages older than 1 month
DELETE r, m FROM msg_out_ref AS r, message_out AS m
WHERE r.message_out = m.id AND m.mtime < UNIX_TIMESTAMP(NOW() - INTERVAL 1 MONTH);
# find domains having been orphaned that way
SELECT l.* FROM domain AS l LEFT JOIN msg_out_ref AS r ON r.domain = l.id
WHERE r.domain IS NULL AND l.sent > 0;
# find the messages sent in the last 24 hours (add AND u.addr = '[email protected]')
SELECT FROM_UNIXTIME(m.mtime) AS time, u.addr FROM message_out AS m, user AS u
WHERE m.user = u.id AND m.mtime > UNIX_TIMESTAMP(NOW() - INTERVAL 1 DAY);
# find which users sent how many messages to a given list of domains
SELECT d.domain, COUNT(*) AS cnt, u.addr
FROM domain AS d, msg_out_ref AS r, message_out AS m, user AS u
WHERE d.id = r.domain AND r.message_out = m.id AND m.user = u.id AND
d.id IN (1,2,3,4,5) GROUP BY u.id;
SELECT d.domain, COUNT(*) AS cnt, u.addr
FROM domain AS d, msg_out_ref AS r, message_out AS m, user AS u
WHERE d.id = r.domain AND r.message_out = m.id AND m.user = u.id
GROUP BY d.id, u.id ORDER BY cnt DESC LIMIT 10;