Haraka 3.0.2 → 3.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (267) hide show
  1. package/.eslintrc.yaml +5 -9
  2. package/.prettierrc.yml +1 -0
  3. package/CONTRIBUTORS.md +11 -0
  4. package/Changes.md +1393 -1211
  5. package/Dockerfile +3 -3
  6. package/Plugins.md +119 -106
  7. package/README.md +7 -16
  8. package/TODO +1 -24
  9. package/bin/haraka +197 -298
  10. package/config/auth_flat_file.ini +2 -0
  11. package/config/auth_vpopmaild.ini +4 -2
  12. package/config/dhparams.pem +8 -0
  13. package/config/mail_from.is_resolvable.ini +4 -2
  14. package/config/me +1 -0
  15. package/config/outbound.ini +0 -2
  16. package/config/plugins +36 -35
  17. package/config/rabbitmq_amqplib.ini +8 -1
  18. package/config/smtp.ini +0 -1
  19. package/config/smtp.json +17 -0
  20. package/config/tls_cert.pem +23 -0
  21. package/config/tls_key.pem +28 -0
  22. package/connection.js +46 -73
  23. package/contrib/bsd-rc.d/haraka +3 -1
  24. package/contrib/plugin2npm.sh +6 -36
  25. package/docs/Connection.md +1 -1
  26. package/docs/CoreConfig.md +2 -2
  27. package/docs/Logging.md +7 -21
  28. package/docs/Outbound.md +104 -210
  29. package/docs/Plugins.md +47 -40
  30. package/docs/Transaction.md +59 -82
  31. package/docs/{plugins → deprecated}/connect.rdns_access.md +1 -1
  32. package/docs/{plugins → deprecated}/mail_from.access.md +1 -1
  33. package/docs/{plugins → deprecated}/rcpt_to.access.md +1 -1
  34. package/docs/plugins/auth/auth_vpopmaild.md +15 -19
  35. package/docs/plugins/auth/flat_file.md +23 -30
  36. package/docs/plugins/queue/rabbitmq_amqplib.md +7 -0
  37. package/docs/plugins/queue/smtp_forward.md +1 -1
  38. package/docs/plugins/queue/smtp_proxy.md +5 -10
  39. package/docs/plugins/relay.md +2 -2
  40. package/docs/plugins/tls.md +29 -9
  41. package/endpoint.js +16 -13
  42. package/haraka.js +10 -14
  43. package/host_pool.js +5 -5
  44. package/line_socket.js +3 -4
  45. package/logger.js +44 -28
  46. package/outbound/client_pool.js +27 -23
  47. package/outbound/config.js +4 -6
  48. package/outbound/fsync_writestream.js +1 -1
  49. package/outbound/hmail.js +180 -220
  50. package/outbound/index.js +86 -99
  51. package/outbound/qfile.js +1 -1
  52. package/outbound/queue.js +55 -43
  53. package/outbound/timer_queue.js +3 -2
  54. package/outbound/tls.js +19 -7
  55. package/package.json +66 -55
  56. package/plugins/.eslintrc.yaml +0 -6
  57. package/plugins/auth/auth_base.js +30 -12
  58. package/plugins/auth/auth_proxy.js +14 -12
  59. package/plugins/auth/auth_vpopmaild.js +30 -20
  60. package/plugins/auth/flat_file.js +17 -12
  61. package/plugins/block_me.js +1 -1
  62. package/plugins/data.signatures.js +2 -4
  63. package/plugins/early_talker.js +2 -1
  64. package/plugins/mail_from.is_resolvable.js +65 -135
  65. package/plugins/queue/deliver.js +4 -5
  66. package/plugins/queue/lmtp.js +11 -14
  67. package/plugins/queue/qmail-queue.js +2 -2
  68. package/plugins/queue/quarantine.js +2 -2
  69. package/plugins/queue/rabbitmq.js +16 -17
  70. package/plugins/queue/rabbitmq_amqplib.js +1 -1
  71. package/plugins/queue/smtp_forward.js +6 -6
  72. package/plugins/queue/smtp_proxy.js +10 -1
  73. package/plugins/queue/test.js +2 -2
  74. package/plugins/rcpt_to.host_list_base.js +5 -5
  75. package/plugins/rcpt_to.in_host_list.js +2 -2
  76. package/plugins/relay.js +6 -7
  77. package/plugins/reseed_rng.js +1 -1
  78. package/plugins/status.js +37 -33
  79. package/plugins/tls.js +2 -2
  80. package/plugins/xclient.js +3 -2
  81. package/plugins.js +51 -54
  82. package/run_tests +3 -30
  83. package/server.js +190 -190
  84. package/smtp_client.js +30 -23
  85. package/{tests → test}/config/plugins +0 -2
  86. package/{tests → test}/config/smtp.ini +1 -1
  87. package/test/config/tls/example.com/_.example.com.key +28 -0
  88. package/test/config/tls/example.com/example.com.crt +25 -0
  89. package/test/connection.js +302 -0
  90. package/test/endpoint.js +94 -0
  91. package/{tests → test}/fixtures/line_socket.js +1 -1
  92. package/{tests → test}/fixtures/util_hmailitem.js +19 -25
  93. package/{tests → test}/host_pool.js +42 -57
  94. package/test/logger.js +258 -0
  95. package/test/outbound/hmail.js +141 -0
  96. package/test/outbound/index.js +220 -0
  97. package/test/outbound/qfile.js +126 -0
  98. package/test/outbound_bounce_net_errors.js +142 -0
  99. package/{tests → test}/outbound_bounce_rfc3464.js +110 -122
  100. package/test/plugins/auth/auth_base.js +484 -0
  101. package/test/plugins/auth/auth_vpopmaild.js +83 -0
  102. package/test/plugins/early_talker.js +104 -0
  103. package/test/plugins/mail_from.is_resolvable.js +35 -0
  104. package/test/plugins/queue/smtp_forward.js +206 -0
  105. package/test/plugins/rcpt_to.host_list_base.js +122 -0
  106. package/test/plugins/rcpt_to.in_host_list.js +193 -0
  107. package/test/plugins/relay.js +303 -0
  108. package/test/plugins/status.js +130 -0
  109. package/test/plugins/tls.js +70 -0
  110. package/test/plugins.js +228 -0
  111. package/{tests → test}/queue/multibyte +0 -0
  112. package/{tests → test}/queue/plain +0 -0
  113. package/test/rfc1869.js +73 -0
  114. package/test/server.js +491 -0
  115. package/test/smtp_client.js +299 -0
  116. package/test/tls_socket.js +273 -0
  117. package/test/transaction.js +270 -0
  118. package/tls_socket.js +202 -252
  119. package/transaction.js +9 -24
  120. package/CONTRIBUTING.md +0 -1
  121. package/bin/dkimverify +0 -40
  122. package/config/access.domains +0 -13
  123. package/config/attachment.ctype.regex +0 -2
  124. package/config/attachment.filename.regex +0 -1
  125. package/config/avg.ini +0 -5
  126. package/config/bounce.ini +0 -15
  127. package/config/data.headers.ini +0 -61
  128. package/config/dkim/dkim_key_gen.sh +0 -78
  129. package/config/dkim_sign.ini +0 -4
  130. package/config/dkim_verify.ini +0 -7
  131. package/config/dnsbl.ini +0 -23
  132. package/config/greylist.ini +0 -43
  133. package/config/helo.checks.ini +0 -52
  134. package/config/lookup_rdns.strict.ini +0 -12
  135. package/config/lookup_rdns.strict.timeout +0 -1
  136. package/config/lookup_rdns.strict.whitelist +0 -1
  137. package/config/lookup_rdns.strict.whitelist_regex +0 -5
  138. package/config/messagesniffer.ini +0 -18
  139. package/config/rcpt_to.blocklist +0 -1
  140. package/config/rdns.allow_regexps +0 -0
  141. package/config/rdns.deny_regexps +0 -0
  142. package/config/spamassassin.ini +0 -56
  143. package/config.js +0 -6
  144. package/dkim.js +0 -614
  145. package/docs/plugins/avg.md +0 -35
  146. package/docs/plugins/bounce.md +0 -69
  147. package/docs/plugins/clamd.md +0 -147
  148. package/docs/plugins/esets.md +0 -8
  149. package/docs/plugins/greylist.md +0 -90
  150. package/docs/plugins/helo.checks.md +0 -135
  151. package/docs/plugins/messagesniffer.md +0 -163
  152. package/docs/plugins/relay_acl.md +0 -29
  153. package/docs/plugins/relay_all.md +0 -15
  154. package/docs/plugins/relay_force_routing.md +0 -33
  155. package/docs/plugins/spamassassin.md +0 -180
  156. package/outbound/mx_lookup.js +0 -70
  157. package/plugins/auth/auth_ldap.js +0 -3
  158. package/plugins/avg.js +0 -162
  159. package/plugins/backscatterer.js +0 -25
  160. package/plugins/bounce.js +0 -381
  161. package/plugins/clamd.js +0 -381
  162. package/plugins/data.headers.js +0 -4
  163. package/plugins/data.uribl.js +0 -4
  164. package/plugins/dkim_sign.js +0 -395
  165. package/plugins/dkim_verify.js +0 -62
  166. package/plugins/dns_list_base.js +0 -221
  167. package/plugins/dnsbl.js +0 -146
  168. package/plugins/dnswl.js +0 -58
  169. package/plugins/esets.js +0 -71
  170. package/plugins/graph.js +0 -5
  171. package/plugins/greylist.js +0 -645
  172. package/plugins/helo.checks.js +0 -533
  173. package/plugins/messagesniffer.js +0 -381
  174. package/plugins/rcpt_to.ldap.js +0 -3
  175. package/plugins/rcpt_to.max_count.js +0 -24
  176. package/plugins/relay_all.js +0 -13
  177. package/plugins/spamassassin.js +0 -384
  178. package/tests/config/dkim/example.com/dns +0 -29
  179. package/tests/config/dkim/example.com/private +0 -6
  180. package/tests/config/dkim/example.com/public +0 -4
  181. package/tests/config/dkim/example.com/selector +0 -1
  182. package/tests/config/dkim.private.key +0 -6
  183. package/tests/config/dkim_sign.ini +0 -4
  184. package/tests/config/helo.checks.ini +0 -52
  185. package/tests/connection.js +0 -327
  186. package/tests/endpoint.js +0 -128
  187. package/tests/fixtures/vm_harness.js +0 -59
  188. package/tests/logger.js +0 -327
  189. package/tests/outbound/hmail.js +0 -112
  190. package/tests/outbound/index.js +0 -324
  191. package/tests/outbound/qfile.js +0 -67
  192. package/tests/outbound_bounce_net_errors.js +0 -173
  193. package/tests/plugins/auth/auth_base.js +0 -463
  194. package/tests/plugins/auth/auth_vpopmaild.js +0 -91
  195. package/tests/plugins/bounce.js +0 -307
  196. package/tests/plugins/clamd.js +0 -224
  197. package/tests/plugins/deprecated/relay_acl.js +0 -140
  198. package/tests/plugins/deprecated/relay_all.js +0 -59
  199. package/tests/plugins/dkim_sign.js +0 -315
  200. package/tests/plugins/dkim_signer.js +0 -108
  201. package/tests/plugins/dns_list_base.js +0 -259
  202. package/tests/plugins/dnsbl.js +0 -101
  203. package/tests/plugins/early_talker.js +0 -115
  204. package/tests/plugins/greylist.js +0 -58
  205. package/tests/plugins/helo.checks.js +0 -525
  206. package/tests/plugins/mail_from.is_resolvable.js +0 -116
  207. package/tests/plugins/queue/smtp_forward.js +0 -221
  208. package/tests/plugins/rcpt_to.host_list_base.js +0 -132
  209. package/tests/plugins/rcpt_to.in_host_list.js +0 -218
  210. package/tests/plugins/relay.js +0 -339
  211. package/tests/plugins/spamassassin.js +0 -171
  212. package/tests/plugins/status.js +0 -138
  213. package/tests/plugins/tls.js +0 -84
  214. package/tests/plugins.js +0 -247
  215. package/tests/rfc1869.js +0 -61
  216. package/tests/server.js +0 -510
  217. package/tests/smtp_client/auth.js +0 -105
  218. package/tests/smtp_client/basic.js +0 -101
  219. package/tests/smtp_client.js +0 -80
  220. package/tests/tls_socket.js +0 -333
  221. package/tests/transaction.js +0 -284
  222. /package/docs/{plugins → deprecated}/dkim_sign.md +0 -0
  223. /package/docs/{plugins → deprecated}/dkim_verify.md +0 -0
  224. /package/docs/{plugins → deprecated}/dnsbl.md +0 -0
  225. /package/docs/{plugins → deprecated}/dnswl.md +0 -0
  226. /package/docs/{plugins → deprecated}/rcpt_to.routes.md +0 -0
  227. /package/{tests → test}/.eslintrc.yaml +0 -0
  228. /package/{tests → test}/config/auth_flat_file.ini +0 -0
  229. /package/{tests → test}/config/dhparams.pem +0 -0
  230. /package/{tests → test}/config/host_list +0 -0
  231. /package/{tests → test}/config/outbound_tls_cert.pem +0 -0
  232. /package/{tests → test}/config/outbound_tls_key.pem +0 -0
  233. /package/{tests → test}/config/smtp_forward.ini +0 -0
  234. /package/{tests → test}/config/tls/ec.pem +0 -0
  235. /package/{tests → test}/config/tls/haraka.local.pem +0 -0
  236. /package/{tests → test}/config/tls/mismatched.pem +0 -0
  237. /package/{tests → test}/config/tls.ini +0 -0
  238. /package/{tests → test}/config/tls_cert.pem +0 -0
  239. /package/{tests → test}/config/tls_key.pem +0 -0
  240. /package/{tests → test}/fixtures/todo_qfile.txt +0 -0
  241. /package/{tests → test}/installation/config/test-plugin-flat +0 -0
  242. /package/{tests → test}/installation/config/test-plugin.ini +0 -0
  243. /package/{tests → test}/installation/config/tls.ini +0 -0
  244. /package/{tests → test}/installation/node_modules/load_first/index.js +0 -0
  245. /package/{tests → test}/installation/node_modules/load_first/package.json +0 -0
  246. /package/{tests → test}/installation/node_modules/test-plugin/config/test-plugin-flat +0 -0
  247. /package/{tests → test}/installation/node_modules/test-plugin/config/test-plugin.ini +0 -0
  248. /package/{tests → test}/installation/node_modules/test-plugin/package.json +0 -0
  249. /package/{tests → test}/installation/node_modules/test-plugin/test-plugin.js +0 -0
  250. /package/{tests → test}/installation/plugins/base_plugin.js +0 -0
  251. /package/{tests → test}/installation/plugins/folder_plugin/index.js +0 -0
  252. /package/{tests → test}/installation/plugins/folder_plugin/package.json +0 -0
  253. /package/{tests → test}/installation/plugins/inherits.js +0 -0
  254. /package/{tests → test}/installation/plugins/load_first.js +0 -0
  255. /package/{tests → test}/installation/plugins/plugin.js +0 -0
  256. /package/{tests → test}/installation/plugins/tls.js +0 -0
  257. /package/{tests → test}/loud/config/dhparams.pem +0 -0
  258. /package/{tests → test}/loud/config/tls/goobered.pem +0 -0
  259. /package/{tests → test}/loud/config/tls.ini +0 -0
  260. /package/{tests → test}/mail_specimen/base64-root-part.txt +0 -0
  261. /package/{tests → test}/mail_specimen/varied-fold-lengths-preserve-data.txt +0 -0
  262. /package/{tests → test}/queue/1507509981169_1507509981169_0_61403_e0Y0Ym_1_fixed +0 -0
  263. /package/{tests → test}/queue/1507509981169_1507509981169_0_61403_e0Y0Ym_1_haraka +0 -0
  264. /package/{tests → test}/queue/1508269674999_1508269674999_0_34002_socVUF_1_haraka +0 -0
  265. /package/{tests → test}/queue/1508455115683_1508455115683_0_90253_9Q4o4V_1_haraka +0 -0
  266. /package/{tests → test}/queue/zero-length +0 -0
  267. /package/{tests → test}/test-queue/delete-me +0 -0
@@ -1,645 +0,0 @@
1
- // Greylisting plugin for Haraka
2
-
3
- // version 0.1.4
4
-
5
- // node builtins
6
- const net = require('net');
7
- const util = require('util');
8
-
9
- // Haraka modules
10
- const DSN = require('haraka-dsn');
11
- const tlds = require('haraka-tld');
12
- const net_utils = require('haraka-net-utils');
13
- const { Address } = require('address-rfc2821');
14
-
15
- // External NPM modules
16
- const ipaddr = require('ipaddr.js');
17
-
18
- // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
19
- exports.register = function (next) {
20
- this.inherits('haraka-plugin-redis');
21
-
22
- this.load_config();
23
-
24
- this.register_hook('init_master', 'init_redis_plugin');
25
- this.register_hook('init_child', 'init_redis_plugin');
26
-
27
- // redundant - using the special hook_ nomenclature
28
- // this.register_hook('rcpt_ok', 'hook_rcpt_ok');
29
- }
30
-
31
- // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
32
- exports.load_config = function () {
33
-
34
- this.cfg = this.config.get('greylist.ini', {
35
- booleans : [
36
- '+skip.dnswlorg',
37
- '-skip.mailspikewl'
38
- ]
39
- }, () => {
40
- this.load_config();
41
- });
42
-
43
- this.merge_redis_ini();
44
- this.load_config_lists();
45
- }
46
-
47
- // Load various configuration lists
48
- exports.load_config_lists = function () {
49
- const plugin = this;
50
-
51
- plugin.whitelist = {};
52
- plugin.list = {};
53
-
54
- function load_list (type, file_name) {
55
- plugin.whitelist[type] = {};
56
-
57
- const list = Object.keys(plugin.cfg[file_name]);
58
-
59
- // toLower when loading spends a fraction of a second at load time
60
- // to save millions of seconds during run time.
61
- for (const element of list) {
62
- plugin.whitelist[type][element.toLowerCase()] = true;
63
- }
64
- plugin.logdebug(`whitelist {${type}} loaded from ${file_name} with ${list.length} entries`);
65
- }
66
-
67
- function load_ip_list (type, file_name) {
68
- plugin.whitelist[type] = [];
69
-
70
- const list = Object.keys(plugin.cfg[file_name]);
71
-
72
- for (const element of list) {
73
- try {
74
- let addr = element;
75
- if (addr.match(/\/\d+$/)) {
76
- addr = ipaddr.parseCIDR(addr);
77
- }
78
- else {
79
- addr = ipaddr.parseCIDR(`${addr}${((net.isIPv6(addr)) ? '/128' : '/32')}`);
80
- }
81
-
82
- plugin.whitelist[type].push(addr);
83
- }
84
- catch (e) {}
85
- }
86
-
87
- plugin.logdebug(`whitelist {${type}} loaded from ${file_name} with ${plugin.whitelist[type].length} entries`);
88
- }
89
-
90
- function load_config_list (type, file_name) {
91
- plugin.list[type] = Object.keys(plugin.cfg[file_name]);
92
-
93
- plugin.logdebug(`list {${type}} loaded from ${file_name} with ${plugin.list[type].length} entries`);
94
- }
95
-
96
- load_list('mail', 'envelope_whitelist');
97
- load_list('rcpt', 'recipient_whitelist');
98
- load_ip_list('ip', 'ip_whitelist');
99
-
100
- load_config_list('dyndom', 'special_dynamic_domains');
101
- }
102
-
103
- // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
104
- exports.shutdown = function () {
105
- if (this.db) this.db.quit();
106
- }
107
-
108
- // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
109
-
110
- // We check for IP and envelope whitelist
111
- exports.hook_mail = function (next, connection, params) {
112
- if (!connection.transaction) return next();
113
-
114
- const mail_from = params[0];
115
-
116
- // whitelist checks
117
- if (this.ip_in_list(connection.remote.ip)) { // check connecting IP
118
-
119
- this.loginfo(connection, 'Connecting IP was whitelisted via config');
120
- connection.transaction.results.add(this, { skip : 'config-whitelist(ip)' })
121
-
122
- }
123
- else if (this.addr_in_list('mail', mail_from.address().toLowerCase())) { // check envelope (email & domain)
124
-
125
- this.loginfo(connection, 'Envelope was whitelisted via config');
126
- connection.transaction.results.add(this, { skip : 'config-whitelist(envelope)' });
127
-
128
- }
129
- else {
130
- const why_skip = this.process_skip_rules(connection);
131
-
132
- if (why_skip) {
133
- this.loginfo(connection, `Requested to skip the GL because skip rule matched: ${why_skip}`);
134
- connection.transaction.results.add(this, { skip : `requested(${why_skip})` });
135
- }
136
- }
137
-
138
- next();
139
- }
140
-
141
- //
142
- exports.hook_rcpt_ok = function (next, connection, rcpt) {
143
-
144
- if (this.should_skip_check(connection)) return next();
145
- if (this.was_whitelisted_in_session(connection)) {
146
- this.logdebug(connection, 'host already whitelisted in this session');
147
- return next();
148
- }
149
-
150
- const { transaction } = connection
151
-
152
- const ctr = transaction.results;
153
- const { mail_from } = transaction;
154
-
155
- // check rcpt in whitelist (email & domain)
156
- if (this.addr_in_list('rcpt', rcpt.address().toLowerCase())) {
157
- this.loginfo(connection, 'RCPT was whitelisted via config');
158
- ctr.add(this, { skip : 'config-whitelist(recipient)' });
159
- return next();
160
- }
161
-
162
- this.check_and_update_white(connection, (err, white_rec) => {
163
- if (err) {
164
- this.logerror(connection, `Got error: ${util.inspect(err)}`);
165
- return next(DENYSOFT, DSN.sec_unspecified('Backend failure. Please, retry later or contact our support.'));
166
- }
167
- if (white_rec) {
168
- this.logdebug(connection, 'host in WHITE zone');
169
- ctr.add(this, { pass : 'whitelisted' });
170
- ctr.push(this, { stats : { rcpt : white_rec }, stage : 'rcpt' });
171
-
172
- return next();
173
- }
174
-
175
- return this.process_tuple(connection, mail_from.address(), rcpt.address(), (err2, white_promo_rec) => {
176
- if (err2) {
177
- if (err2 instanceof Error && err2.notanerror) {
178
- this.logdebug(connection, 'host in GREY zone');
179
-
180
- ctr.add(this, {
181
- fail : 'greylisted'
182
- });
183
- ctr.push(this, {
184
- stats : {
185
- rcpt : err2.record
186
- },
187
- stage : 'rcpt'
188
- });
189
-
190
- return this.invoke_outcome_cb(next, false);
191
- }
192
-
193
- throw err2;
194
- }
195
-
196
- if (!white_promo_rec) {
197
- ctr.add(this, {
198
- fail : 'greylisted',
199
- stage : 'rcpt'
200
- });
201
- return this.invoke_outcome_cb(next, false);
202
- }
203
- else {
204
- this.loginfo(connection, 'host has been promoted to WHITE zone');
205
- ctr.add(this, {
206
- pass : 'whitelisted',
207
- stats : white_promo_rec,
208
- stage : 'rcpt'
209
- });
210
- ctr.add(this, {
211
- pass : 'whitelisted'
212
- });
213
- return this.invoke_outcome_cb(next, true);
214
- }
215
- })
216
- })
217
- }
218
-
219
- // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
220
-
221
- // Main GL engine that accepts tuple and returns matched record or a rejection.
222
- exports.process_tuple = function (connection, sender, rcpt, cb) {
223
-
224
- const key = this.craft_grey_key(connection, sender, rcpt);
225
- if (!key) return;
226
-
227
- return this.db_lookup(key, (err, record) => {
228
- if (err) {
229
- if (err instanceof Error && err.what == 'db_error')
230
- this.logwarn(connection, `got err from DB: ${util.inspect(err)}`);
231
- throw err;
232
- }
233
- this.logdebug(connection, `got record: ${util.inspect(record)}`);
234
-
235
- // { created: TS, updated: TS, lifetime: TTL, tried: Integer }
236
- const now = Date.now() / 1000;
237
-
238
- if (record &&
239
- (record.created + this.cfg.period.black < now) &&
240
- (record.created + record.lifetime >= now)) {
241
- // Host passed greylisting
242
- return this.promote_to_white(connection, record, cb);
243
- }
244
-
245
- return this.update_grey(key, !record, (err2, created_record) => {
246
- const err3 = new Error('in black zone');
247
- err3.record = created_record || record;
248
- err3.notanerror = true;
249
- return cb(err3, null);
250
- });
251
- });
252
- }
253
-
254
- // Checks if host is _white_. Updates stats if so.
255
- exports.check_and_update_white = function (connection, cb) {
256
-
257
- const key = this.craft_white_key(connection);
258
-
259
- return this.db_lookup(key, (err, record) => {
260
- if (err) {
261
- this.logwarn(connection, `got err from DB: ${util.inspect(err)}`);
262
- throw err;
263
- }
264
- if (record) {
265
- if (record.updated + record.lifetime - 2 < Date.now() / 1000) { // race "prevention".
266
- this.logerror(connection, "Mischief! Race condition triggered.");
267
- return cb(new Error('drunkard'));
268
- }
269
-
270
- return this.update_white_record(key, record, cb);
271
- }
272
-
273
- return cb(null, false);
274
- });
275
- }
276
-
277
- // invokes next() depending on outcome param
278
- exports.invoke_outcome_cb = function (next, is_whitelisted) {
279
-
280
- if (is_whitelisted) return next();
281
-
282
- const text = this.cfg.main.text || '';
283
-
284
- return next(DENYSOFT, DSN.sec_unauthorized(text, '451'));
285
- }
286
-
287
- // Should we skip greylisting invokation altogether?
288
- exports.should_skip_check = function (connection) {
289
- const { transaction, relaying, remote } = connection ?? {}
290
-
291
- if (!transaction) return true;
292
-
293
- const ctr = transaction.results;
294
-
295
- if (relaying) {
296
- this.logdebug(connection, 'skipping GL for relaying host');
297
- ctr.add(this, {
298
- skip : 'relaying'
299
- });
300
- return true;
301
- }
302
-
303
- if (remote?.is_private) {
304
- connection.logdebug(this, `skipping private IP: ${connection.remote.ip}`);
305
- ctr.add(this, {
306
- skip : 'private-ip'
307
- });
308
- return true;
309
- }
310
-
311
- if (ctr) {
312
- if (ctr.has(this, 'skip', /^config-whitelist/)) {
313
- this.loginfo(connection, 'skipping GL for host whitelisted in config');
314
- return true;
315
- }
316
- if (ctr.has(this, 'skip', /^requested/)) {
317
- this.loginfo(connection, 'skipping GL because was asked to previously');
318
- return true;
319
- }
320
- }
321
-
322
- return false;
323
- }
324
-
325
- // Was whitelisted previously in this session
326
- exports.was_whitelisted_in_session = function (connection) {
327
- if (!connection?.transaction?.results) return false;
328
- return connection.transaction.results.has(this, 'pass', 'whitelisted');
329
- }
330
-
331
- exports.process_skip_rules = function (connection) {
332
- const cr = connection.results;
333
-
334
- const skip_cfg = this.cfg.skip;
335
- if (skip_cfg) {
336
- if (skip_cfg.dnswlorg && cr.has('dnswl.org', 'pass', /^list\.dnswl\.org\([123]\)$/)) {
337
- return 'dnswl.org(MED)'
338
- }
339
-
340
- if (skip_cfg.mailspikewl && cr.has('dnswl.org', 'pass', /^wl\.mailspike\.net\((1[7-9]|20)\)$/)) {
341
- return 'mailspike(H2)'
342
- }
343
- }
344
-
345
- return '';
346
- }
347
-
348
- // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
349
-
350
- // Build greylist DB key (originally, a "tuple") off supplied params.
351
- // When _to_ is false, we craft +sender+ key
352
- // When _to_ is String, we craft +rcpt+ key
353
- exports.craft_grey_key = function (connection, from, to) {
354
-
355
- const crafted_host_id = this.craft_hostid(connection)
356
- if (!crafted_host_id) return null;
357
-
358
- let key = `grey:${crafted_host_id}:${(from || '<>')}`;
359
- if (to != undefined) {
360
- key += `:${(to || '<>')}`;
361
- }
362
- return key;
363
- }
364
-
365
- // Build white DB key off supplied params.
366
- exports.craft_white_key = function (connection) {
367
- return `white:${this.craft_hostid(connection)}`;
368
- }
369
-
370
- // Return so-called +hostid+.
371
- exports.craft_hostid = function (connection) {
372
- const plugin = this;
373
- const { transaction, remote } = connection ?? {};
374
- if (!transaction || !remote) return null;
375
-
376
- if (transaction.notes?.greylist?.hostid) {
377
- return transaction.notes.greylist.hostid; // "caching"
378
- }
379
-
380
- function chsit (value, reason) { // cache the return value
381
- if (!value)
382
- plugin.logdebug(connection, `hostid set to IP: ${reason}`);
383
-
384
- transaction.results.add(plugin, {
385
- hostid_type : value ? 'domain' : 'ip',
386
- rdns : (value || remote.ip),
387
- msg : reason
388
- }); // !don't move me.
389
-
390
- value = value || remote.ip;
391
-
392
- return ((transaction.notes.greylist = transaction.notes.greylist || {}).hostid = value);
393
- }
394
-
395
- // no rDNS . FIXME: use fcrdns results
396
- if (!remote.host || [ 'Unknown' | 'DNSERROR' ].includes(remote.host))
397
- return chsit(null, 'no rDNS info for this host');
398
-
399
- remote.host = remote.host.replace(/\.$/, ''); // strip ending dot, just in case
400
-
401
- const fcrdns = connection.results.get('fcrdns');
402
- if (!fcrdns) {
403
- plugin.logwarn(connection, 'No FcrDNS plugin results, fix this.');
404
- return chsit(null, 'no FcrDNS plugin results');
405
- }
406
-
407
- if (!connection.results.has('fcrdns', 'pass', 'fcrdns')) // FcrDNS failed
408
- return chsit(null, 'FcrDNS failed');
409
-
410
- if (connection.results.get('fcrdns').ptr_names.length > 1) // multiple PTR returned
411
- return chsit(null, 'multiple PTR returned');
412
-
413
- if (connection.results.has('fcrdns', 'fail', /^is_generic/)) // generic/dynamic rDNS record
414
- return chsit(null, 'rDNS is a generic record');
415
-
416
- if (connection.results.has('fcrdns', 'fail', /^valid_tld/)) // invalid org domain in rDNS
417
- return chsit(null, 'invalid org domain in rDNS');
418
-
419
- // strip first label up until the tld boundary.
420
- const decoupled = tlds.split_hostname(!remote.host, 3);
421
- const vardom = decoupled[0]; // "variable" portion of domain
422
- const dom = decoupled[1]; // "static" portion of domain
423
-
424
- // we check for special cases where rdns looks custom/static, but really is dynamic
425
- const special_case_info = plugin.check_rdns_for_special_cases(!remote.host, vardom);
426
- if (special_case_info) return chsit(null, special_case_info.why);
427
-
428
- let stripped_dom = dom;
429
-
430
- if (vardom) {
431
-
432
- // check for decimal IP in rDNS
433
- if (vardom.match(String(net_utils.ip_to_long(remote.ip))))
434
- return chsit(null, 'decimal IP');
435
-
436
- // craft the +hostid+
437
- const label = vardom.split('.').slice(1).join('.');
438
- if (label)
439
- stripped_dom = `${label}.${stripped_dom}`;
440
- }
441
-
442
- return chsit(stripped_dom);
443
- }
444
-
445
- // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
446
-
447
- // Retrieve _grey_ record
448
- // not implemented
449
- exports.retrieve_grey = function (rcpt_key, sender_key, cb) {
450
- const multi = this.db.multi();
451
-
452
- multi.hgetall(rcpt_key);
453
- multi.hgetall(sender_key);
454
-
455
- multi.exec((err, result) => {
456
- if (err) {
457
- this.lognotice(`DB error: ${util.inspect(err)}`);
458
- err.what = 'db_error';
459
- throw err;
460
- }
461
- return cb(err, result);
462
- });
463
- }
464
-
465
- // Update or create _grey_ record
466
- exports.update_grey = function (key, create, cb) {
467
- const multi = this.db.multi();
468
-
469
- const ts_now = Math.round(Date.now() / 1000);
470
- let new_record;
471
-
472
- if (create) {
473
- const lifetime = this.cfg.period.grey;
474
- new_record = {
475
- created : ts_now,
476
- updated : ts_now,
477
- lifetime,
478
- tried : 1
479
- };
480
-
481
- multi.hmset(key, new_record);
482
- multi.expire(key, lifetime);
483
- }
484
- else {
485
- multi.hincrby(key, 'tried', 1);
486
- multi.hmset(key, {
487
- updated : ts_now
488
- });
489
- }
490
-
491
- multi.exec((err, records) => {
492
- if (err) {
493
- this.lognotice(`DB error: ${util.inspect(err)}`);
494
- err.what = 'db_error';
495
- throw err;
496
- }
497
- return cb(null, ((create) ? new_record : false));
498
- });
499
- }
500
-
501
- // Promote _grey_ record to _white_.
502
- exports.promote_to_white = function (connection, grey_rec, cb) {
503
-
504
- const ts_now = Math.round(Date.now() / 1000);
505
- const white_ttl = this.cfg.period.white;
506
-
507
- // { first_connect: TS, whitelisted: TS, updated: TS, lifetime: TTL, tried: Integer, tried_when_greylisted: Integer }
508
- const white_rec = {
509
- first_connect : grey_rec.created,
510
- whitelisted : ts_now,
511
- updated : ts_now,
512
- lifetime : white_ttl,
513
- tried_when_greylisted : grey_rec.tried,
514
- tried : 1
515
- };
516
-
517
- const white_key = this.craft_white_key(connection);
518
- if (!white_key) return;
519
-
520
- return this.db.hmset(white_key, white_rec, (err, result) => {
521
- if (err) {
522
- this.lognotice(`DB error: ${util.inspect(err)}`);
523
- err.what = 'db_error';
524
- throw err;
525
- }
526
- this.db.expire(white_key, white_ttl, (err2, result2) => {
527
- if (err2) {
528
- this.lognotice(`DB error: ${util.inspect(err2)}`);
529
- }
530
- return cb(err2, result2);
531
- });
532
- });
533
- }
534
-
535
- // Update _white_ record
536
- exports.update_white_record = function (key, record, cb) {
537
-
538
- const multi = this.db.multi();
539
- const ts_now = Math.round(Date.now() / 1000);
540
-
541
- // { first_connect: TS, whitelisted: TS, updated: TS, lifetime: TTL, tried: Integer, tried_when_greylisted: Integer }
542
- multi.hincrby(key, 'tried', 1);
543
- multi.hmset(key, {
544
- updated : ts_now
545
- });
546
- multi.expire(key, record.lifetime);
547
-
548
- return multi.exec((err2, record2) => {
549
- if (err2) {
550
- this.lognotice(`DB error: ${util.inspect(err2)}`);
551
- err2.what = 'db_error';
552
- throw err2;
553
- }
554
- return cb(null, record2);
555
- });
556
- }
557
-
558
- // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
559
-
560
- exports.db_lookup = function (key, cb) {
561
-
562
- this.db.hgetall(key, (err, result) => {
563
- if (err) {
564
- this.lognotice(`DB error: ${util.inspect(err)}`, key);
565
- }
566
- if (result && typeof result === 'object') { // groom known-to-be numeric values
567
- ['created', 'updated', 'lifetime', 'tried', 'first_connect', 'whitelisted', 'tried_when_greylisted'].forEach(kk => {
568
- const val = result[kk];
569
- if (val !== undefined) {
570
- result[kk] = Number(val);
571
- }
572
- });
573
- }
574
- return cb(null, result);
575
- });
576
- }
577
-
578
- // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
579
- exports.addr_in_list = function (type, address) {
580
-
581
- if (!this.whitelist[type]) {
582
- this.logwarn(`List not defined: ${type}`);
583
- return false;
584
- }
585
-
586
- if (this.whitelist[type][address]) {
587
- return true;
588
- }
589
-
590
- try {
591
- const addr = new Address(address);
592
- return !!this.whitelist[type][addr.host];
593
- }
594
- catch (err) {
595
- return false;
596
- }
597
- }
598
-
599
- exports.ip_in_list = function (ip) {
600
- const ipobj = ipaddr.parse(ip);
601
-
602
- const list = this.whitelist.ip;
603
-
604
- for (const element of list) {
605
- try {
606
- if (ipobj.match(element)) {
607
- return true;
608
- }
609
- }
610
- catch (e) {}
611
- }
612
-
613
- return false;
614
- }
615
-
616
- // Match patterns in the list against (end of) domain
617
- exports.domain_in_list = function (list_name, domain) {
618
- const list = this.list[list_name];
619
-
620
- if (!list) {
621
- this.logwarn(`List not defined: ${list_name}`);
622
- return false;
623
- }
624
-
625
- for (const element of list) {
626
- if (domain.length - domain.lastIndexOf(element) == element.length)
627
- return true;
628
- }
629
-
630
- return false;
631
- }
632
-
633
- // Check for special rDNS cases
634
- // @return {type: 'dynamic'} if rnds is dynamic (hostid should be IP)
635
- exports.check_rdns_for_special_cases = function (domain, label) {
636
-
637
- // ptr for these is in fact dynamic
638
- if (this.domain_in_list('dyndom', domain))
639
- return {
640
- type : 'dynamic',
641
- why : 'rDNS considered dynamic: listed in dynamic.domains config list'
642
- };
643
-
644
- return false;
645
- }