Haraka 3.0.3 → 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 (238) hide show
  1. package/.eslintrc.yaml +5 -9
  2. package/.prettierrc.yml +1 -0
  3. package/CONTRIBUTORS.md +11 -0
  4. package/Changes.md +1365 -1214
  5. package/Plugins.md +117 -105
  6. package/README.md +4 -13
  7. package/bin/haraka +197 -298
  8. package/config/auth_flat_file.ini +1 -0
  9. package/config/dhparams.pem +8 -0
  10. package/config/mail_from.is_resolvable.ini +4 -2
  11. package/config/me +1 -0
  12. package/config/outbound.ini +0 -2
  13. package/config/plugins +36 -35
  14. package/config/smtp.ini +0 -1
  15. package/config/smtp.json +17 -0
  16. package/config/tls_cert.pem +23 -0
  17. package/config/tls_key.pem +28 -0
  18. package/connection.js +46 -73
  19. package/contrib/bsd-rc.d/haraka +3 -1
  20. package/contrib/plugin2npm.sh +6 -36
  21. package/docs/CoreConfig.md +2 -2
  22. package/docs/Logging.md +7 -21
  23. package/docs/Outbound.md +104 -201
  24. package/docs/Plugins.md +2 -2
  25. package/docs/Transaction.md +59 -82
  26. package/docs/plugins/queue/smtp_proxy.md +5 -10
  27. package/docs/plugins/tls.md +29 -9
  28. package/endpoint.js +16 -13
  29. package/haraka.js +10 -14
  30. package/host_pool.js +5 -5
  31. package/line_socket.js +3 -4
  32. package/logger.js +44 -28
  33. package/outbound/client_pool.js +27 -23
  34. package/outbound/config.js +4 -6
  35. package/outbound/fsync_writestream.js +1 -1
  36. package/outbound/hmail.js +178 -218
  37. package/outbound/index.js +86 -99
  38. package/outbound/qfile.js +1 -1
  39. package/outbound/queue.js +51 -44
  40. package/outbound/timer_queue.js +3 -2
  41. package/outbound/tls.js +19 -7
  42. package/package.json +59 -48
  43. package/plugins/.eslintrc.yaml +0 -6
  44. package/plugins/auth/auth_base.js +4 -2
  45. package/plugins/auth/auth_proxy.js +14 -12
  46. package/plugins/auth/auth_vpopmaild.js +1 -1
  47. package/plugins/block_me.js +1 -1
  48. package/plugins/data.signatures.js +2 -4
  49. package/plugins/early_talker.js +2 -1
  50. package/plugins/mail_from.is_resolvable.js +65 -135
  51. package/plugins/queue/deliver.js +4 -5
  52. package/plugins/queue/lmtp.js +11 -14
  53. package/plugins/queue/qmail-queue.js +2 -2
  54. package/plugins/queue/quarantine.js +2 -2
  55. package/plugins/queue/rabbitmq.js +16 -17
  56. package/plugins/queue/smtp_forward.js +3 -3
  57. package/plugins/queue/smtp_proxy.js +10 -1
  58. package/plugins/queue/test.js +2 -2
  59. package/plugins/rcpt_to.host_list_base.js +5 -5
  60. package/plugins/rcpt_to.in_host_list.js +2 -2
  61. package/plugins/relay.js +6 -7
  62. package/plugins/reseed_rng.js +1 -1
  63. package/plugins/status.js +37 -33
  64. package/plugins/tls.js +2 -2
  65. package/plugins/xclient.js +3 -2
  66. package/plugins.js +50 -54
  67. package/run_tests +3 -30
  68. package/server.js +190 -190
  69. package/smtp_client.js +30 -23
  70. package/{tests → test}/config/plugins +0 -2
  71. package/{tests → test}/config/smtp.ini +1 -1
  72. package/test/config/tls/example.com/_.example.com.key +28 -0
  73. package/test/config/tls/example.com/example.com.crt +25 -0
  74. package/test/connection.js +302 -0
  75. package/test/endpoint.js +94 -0
  76. package/{tests → test}/fixtures/line_socket.js +1 -1
  77. package/{tests → test}/fixtures/util_hmailitem.js +19 -25
  78. package/{tests → test}/host_pool.js +42 -57
  79. package/test/logger.js +258 -0
  80. package/test/outbound/hmail.js +141 -0
  81. package/test/outbound/index.js +220 -0
  82. package/test/outbound/qfile.js +126 -0
  83. package/test/outbound_bounce_net_errors.js +142 -0
  84. package/{tests → test}/outbound_bounce_rfc3464.js +110 -122
  85. package/test/plugins/auth/auth_base.js +484 -0
  86. package/test/plugins/auth/auth_vpopmaild.js +83 -0
  87. package/test/plugins/early_talker.js +104 -0
  88. package/test/plugins/mail_from.is_resolvable.js +35 -0
  89. package/test/plugins/queue/smtp_forward.js +206 -0
  90. package/test/plugins/rcpt_to.host_list_base.js +122 -0
  91. package/test/plugins/rcpt_to.in_host_list.js +193 -0
  92. package/test/plugins/relay.js +303 -0
  93. package/test/plugins/status.js +130 -0
  94. package/test/plugins/tls.js +70 -0
  95. package/test/plugins.js +228 -0
  96. package/test/rfc1869.js +73 -0
  97. package/test/server.js +491 -0
  98. package/test/smtp_client.js +299 -0
  99. package/test/tls_socket.js +273 -0
  100. package/test/transaction.js +270 -0
  101. package/tls_socket.js +202 -252
  102. package/transaction.js +8 -23
  103. package/CONTRIBUTING.md +0 -1
  104. package/bin/dkimverify +0 -40
  105. package/config/access.domains +0 -13
  106. package/config/attachment.ctype.regex +0 -2
  107. package/config/attachment.filename.regex +0 -1
  108. package/config/avg.ini +0 -5
  109. package/config/bounce.ini +0 -15
  110. package/config/data.headers.ini +0 -61
  111. package/config/dkim/dkim_key_gen.sh +0 -78
  112. package/config/dkim_sign.ini +0 -4
  113. package/config/dkim_verify.ini +0 -7
  114. package/config/dnsbl.ini +0 -23
  115. package/config/greylist.ini +0 -43
  116. package/config/helo.checks.ini +0 -52
  117. package/config/messagesniffer.ini +0 -18
  118. package/config/spamassassin.ini +0 -56
  119. package/dkim.js +0 -614
  120. package/docs/plugins/avg.md +0 -35
  121. package/docs/plugins/bounce.md +0 -69
  122. package/docs/plugins/clamd.md +0 -147
  123. package/docs/plugins/esets.md +0 -8
  124. package/docs/plugins/greylist.md +0 -90
  125. package/docs/plugins/helo.checks.md +0 -135
  126. package/docs/plugins/messagesniffer.md +0 -163
  127. package/docs/plugins/spamassassin.md +0 -180
  128. package/outbound/mx_lookup.js +0 -70
  129. package/plugins/auth/auth_ldap.js +0 -3
  130. package/plugins/avg.js +0 -162
  131. package/plugins/backscatterer.js +0 -25
  132. package/plugins/bounce.js +0 -381
  133. package/plugins/clamd.js +0 -382
  134. package/plugins/data.uribl.js +0 -4
  135. package/plugins/dkim_sign.js +0 -395
  136. package/plugins/dkim_verify.js +0 -62
  137. package/plugins/dns_list_base.js +0 -221
  138. package/plugins/dnsbl.js +0 -146
  139. package/plugins/dnswl.js +0 -58
  140. package/plugins/esets.js +0 -71
  141. package/plugins/graph.js +0 -5
  142. package/plugins/greylist.js +0 -645
  143. package/plugins/helo.checks.js +0 -533
  144. package/plugins/messagesniffer.js +0 -381
  145. package/plugins/rcpt_to.ldap.js +0 -3
  146. package/plugins/rcpt_to.max_count.js +0 -24
  147. package/plugins/spamassassin.js +0 -384
  148. package/tests/config/dkim/example.com/dns +0 -29
  149. package/tests/config/dkim/example.com/private +0 -6
  150. package/tests/config/dkim/example.com/public +0 -4
  151. package/tests/config/dkim/example.com/selector +0 -1
  152. package/tests/config/dkim.private.key +0 -6
  153. package/tests/config/dkim_sign.ini +0 -4
  154. package/tests/config/helo.checks.ini +0 -52
  155. package/tests/connection.js +0 -327
  156. package/tests/endpoint.js +0 -128
  157. package/tests/fixtures/vm_harness.js +0 -59
  158. package/tests/logger.js +0 -327
  159. package/tests/outbound/hmail.js +0 -112
  160. package/tests/outbound/index.js +0 -324
  161. package/tests/outbound/qfile.js +0 -67
  162. package/tests/outbound_bounce_net_errors.js +0 -173
  163. package/tests/plugins/auth/auth_base.js +0 -463
  164. package/tests/plugins/auth/auth_vpopmaild.js +0 -91
  165. package/tests/plugins/bounce.js +0 -307
  166. package/tests/plugins/clamd.js +0 -224
  167. package/tests/plugins/deprecated/relay_acl.js +0 -140
  168. package/tests/plugins/deprecated/relay_all.js +0 -59
  169. package/tests/plugins/dkim_sign.js +0 -315
  170. package/tests/plugins/dkim_signer.js +0 -108
  171. package/tests/plugins/dns_list_base.js +0 -259
  172. package/tests/plugins/dnsbl.js +0 -101
  173. package/tests/plugins/early_talker.js +0 -115
  174. package/tests/plugins/greylist.js +0 -58
  175. package/tests/plugins/helo.checks.js +0 -525
  176. package/tests/plugins/mail_from.is_resolvable.js +0 -116
  177. package/tests/plugins/queue/smtp_forward.js +0 -221
  178. package/tests/plugins/rcpt_to.host_list_base.js +0 -132
  179. package/tests/plugins/rcpt_to.in_host_list.js +0 -218
  180. package/tests/plugins/relay.js +0 -339
  181. package/tests/plugins/spamassassin.js +0 -171
  182. package/tests/plugins/status.js +0 -138
  183. package/tests/plugins/tls.js +0 -84
  184. package/tests/plugins.js +0 -247
  185. package/tests/rfc1869.js +0 -61
  186. package/tests/server.js +0 -510
  187. package/tests/smtp_client/auth.js +0 -105
  188. package/tests/smtp_client/basic.js +0 -101
  189. package/tests/smtp_client.js +0 -80
  190. package/tests/tls_socket.js +0 -333
  191. package/tests/transaction.js +0 -284
  192. /package/docs/{plugins → deprecated}/dkim_sign.md +0 -0
  193. /package/docs/{plugins → deprecated}/dkim_verify.md +0 -0
  194. /package/docs/{plugins → deprecated}/dnsbl.md +0 -0
  195. /package/docs/{plugins → deprecated}/dnswl.md +0 -0
  196. /package/{tests → test}/.eslintrc.yaml +0 -0
  197. /package/{tests → test}/config/auth_flat_file.ini +0 -0
  198. /package/{tests → test}/config/dhparams.pem +0 -0
  199. /package/{tests → test}/config/host_list +0 -0
  200. /package/{tests → test}/config/outbound_tls_cert.pem +0 -0
  201. /package/{tests → test}/config/outbound_tls_key.pem +0 -0
  202. /package/{tests → test}/config/smtp_forward.ini +0 -0
  203. /package/{tests → test}/config/tls/ec.pem +0 -0
  204. /package/{tests → test}/config/tls/haraka.local.pem +0 -0
  205. /package/{tests → test}/config/tls/mismatched.pem +0 -0
  206. /package/{tests → test}/config/tls.ini +0 -0
  207. /package/{tests → test}/config/tls_cert.pem +0 -0
  208. /package/{tests → test}/config/tls_key.pem +0 -0
  209. /package/{tests → test}/fixtures/todo_qfile.txt +0 -0
  210. /package/{tests → test}/installation/config/test-plugin-flat +0 -0
  211. /package/{tests → test}/installation/config/test-plugin.ini +0 -0
  212. /package/{tests → test}/installation/config/tls.ini +0 -0
  213. /package/{tests → test}/installation/node_modules/load_first/index.js +0 -0
  214. /package/{tests → test}/installation/node_modules/load_first/package.json +0 -0
  215. /package/{tests → test}/installation/node_modules/test-plugin/config/test-plugin-flat +0 -0
  216. /package/{tests → test}/installation/node_modules/test-plugin/config/test-plugin.ini +0 -0
  217. /package/{tests → test}/installation/node_modules/test-plugin/package.json +0 -0
  218. /package/{tests → test}/installation/node_modules/test-plugin/test-plugin.js +0 -0
  219. /package/{tests → test}/installation/plugins/base_plugin.js +0 -0
  220. /package/{tests → test}/installation/plugins/folder_plugin/index.js +0 -0
  221. /package/{tests → test}/installation/plugins/folder_plugin/package.json +0 -0
  222. /package/{tests → test}/installation/plugins/inherits.js +0 -0
  223. /package/{tests → test}/installation/plugins/load_first.js +0 -0
  224. /package/{tests → test}/installation/plugins/plugin.js +0 -0
  225. /package/{tests → test}/installation/plugins/tls.js +0 -0
  226. /package/{tests → test}/loud/config/dhparams.pem +0 -0
  227. /package/{tests → test}/loud/config/tls/goobered.pem +0 -0
  228. /package/{tests → test}/loud/config/tls.ini +0 -0
  229. /package/{tests → test}/mail_specimen/base64-root-part.txt +0 -0
  230. /package/{tests → test}/mail_specimen/varied-fold-lengths-preserve-data.txt +0 -0
  231. /package/{tests → test}/queue/1507509981169_1507509981169_0_61403_e0Y0Ym_1_fixed +0 -0
  232. /package/{tests → test}/queue/1507509981169_1507509981169_0_61403_e0Y0Ym_1_haraka +0 -0
  233. /package/{tests → test}/queue/1508269674999_1508269674999_0_34002_socVUF_1_haraka +0 -0
  234. /package/{tests → test}/queue/1508455115683_1508455115683_0_90253_9Q4o4V_1_haraka +0 -0
  235. /package/{tests → test}/queue/multibyte +0 -0
  236. /package/{tests → test}/queue/plain +0 -0
  237. /package/{tests → test}/queue/zero-length +0 -0
  238. /package/{tests → test}/test-queue/delete-me +0 -0
@@ -1,533 +0,0 @@
1
- 'use strict';
2
- // Check various bits of the HELO string
3
-
4
- const tlds = require('haraka-tld');
5
- const dns = require('dns');
6
- const net_utils = require('haraka-net-utils');
7
- const utils = require('haraka-utils');
8
-
9
- const checks = [
10
- 'match_re', // List of regexps
11
- 'bare_ip', // HELO is bare IP (vs required Address Literal)
12
- 'dynamic', // HELO hostname looks dynamic (dsl|dialup|etc...)
13
- 'big_company', // Well known HELOs that must match rdns
14
- 'valid_hostname', // HELO hostname is a legal DNS name
15
- 'rdns_match', // HELO hostname matches rDNS
16
- 'forward_dns', // HELO hostname resolves to the connecting IP
17
- 'host_mismatch', // hostname differs between invocations
18
- ];
19
-
20
- exports.register = function () {
21
- this.load_helo_checks_ini();
22
-
23
- if (this.cfg.check.proto_mismatch) {
24
- // NOTE: these *must* run before init
25
- this.register_hook('helo', 'proto_mismatch_smtp');
26
- this.register_hook('ehlo', 'proto_mismatch_esmtp');
27
- }
28
-
29
- // Always run init
30
- this.register_hook('helo', 'init');
31
- this.register_hook('ehlo', 'init');
32
-
33
- for (const c of checks) {
34
- if (!this.cfg.check[c]) continue; // disabled in config
35
- this.register_hook('helo', c);
36
- this.register_hook('ehlo', c);
37
- }
38
-
39
- // Always emit a log entry
40
- this.register_hook('helo', 'emit_log');
41
- this.register_hook('ehlo', 'emit_log');
42
-
43
- // IP literal that doesn't match remote IP
44
- this.register_hook('helo', 'literal_mismatch');
45
- this.register_hook('ehlo', 'literal_mismatch');
46
-
47
- this.cfg.check.literal_mismatch = this.cfg.check.literal_mismatch ?? 2;
48
- this.cfg.reject.literal_mismatch = this.cfg.reject.literal_mismatch ?? false;
49
-
50
- if (this.cfg.check.match_re) {
51
- const load_re_file = () => {
52
- const regex_list = utils.valid_regexes(this.config.get('helo.checks.regexps', 'list', load_re_file));
53
- // pre-compile the regexes
54
- this.cfg.list_re = new RegExp(`^(${regex_list.join('|')})$`, 'i');
55
- };
56
- load_re_file();
57
- }
58
- }
59
-
60
- exports.load_helo_checks_ini = function () {
61
-
62
- const booleans = [
63
- '+skip.private_ip',
64
- '+skip.whitelist',
65
- '+skip.relaying',
66
-
67
- '+check.proto_mismatch',
68
- '-reject.proto_mismatch',
69
- ];
70
-
71
- checks.forEach(c => {
72
- booleans.push(`+check.${c}`);
73
- booleans.push(`-reject.${c}`);
74
- });
75
-
76
- this.cfg = this.config.get('helo.checks.ini', { booleans },
77
- () => {
78
- this.load_helo_checks_ini();
79
- });
80
-
81
- // backwards compatible with old config file
82
- if (this.cfg.check_no_dot !== undefined) {
83
- this.cfg.check.valid_hostname = !!this.cfg.check_no_dot;
84
- }
85
- if (this.cfg.check_dynamic !== undefined) {
86
- this.cfg.check.dynamic = !!this.cfg.check_dynamic;
87
- }
88
- if (this.cfg.check_raw_ip !== undefined) {
89
- this.cfg.check.bare_ip = !!this.cfg.check_raw_ip;
90
- }
91
-
92
- // non-default setting, so apply their localized setting
93
- if (this.cfg.check.mismatch !== undefined && !this.cfg.check.mismatch) {
94
- this.logerror('deprecated setting mismatch renamed to host_mismatch');
95
- this.cfg.check.host_mismatch = this.cfg.check.mismatch;
96
- }
97
- if (this.cfg.reject.mismatch !== undefined && this.cfg.reject.mismatch) {
98
- this.logerror('deprecated setting mismatch renamed to host_mismatch');
99
- this.cfg.reject.host_mismatch = this.cfg.reject.mismatch;
100
- }
101
- }
102
-
103
- exports.init = function (next, connection, helo) {
104
- if (!connection.results.has('helo.checks', 'helo_host', helo)) {
105
- connection.results.add(this, {helo_host: helo});
106
- }
107
-
108
- next();
109
- }
110
-
111
- exports.should_skip = function (connection, test_name) {
112
- if (connection.results.has('helo.checks', '_skip_hooks', test_name)) {
113
- this.logdebug(connection, `SKIPPING: ${test_name}`);
114
- return true;
115
- }
116
- connection.results.push(this, {_skip_hooks: test_name});
117
-
118
- if (this.cfg.skip.relaying && connection.relaying) {
119
- connection.results.add(this, {skip: `${test_name}(relay)`});
120
- return true;
121
- }
122
-
123
- if (this.cfg.skip.private_ip && connection.remote.is_private) {
124
- connection.results.add(this, {skip: `${test_name}(private)`});
125
- return true;
126
- }
127
-
128
- return false;
129
- }
130
-
131
- exports.host_mismatch = function (next, connection, helo) {
132
- if (this.should_skip(connection, 'host_mismatch')) return next();
133
-
134
- const prev_helo = connection.results.get('helo.checks').helo_host;
135
- if (!prev_helo) {
136
- connection.results.add(this, {skip: 'host_mismatch(1st)'});
137
- connection.notes.prev_helo = helo;
138
- return next();
139
- }
140
-
141
- if (prev_helo === helo) {
142
- connection.results.add(this, {pass: 'host_mismatch'});
143
- return next();
144
- }
145
-
146
- const msg = `host_mismatch(${prev_helo} / ${helo})`;
147
- connection.results.add(this, {fail: msg});
148
- if (!this.cfg.reject.host_mismatch) return next();
149
-
150
- next(DENY, `HELO host ${msg}`);
151
- }
152
-
153
- exports.valid_hostname = function (next, connection, helo) {
154
-
155
- if (this.should_skip(connection, 'valid_hostname')) return next();
156
-
157
- if (net_utils.is_ip_literal(helo)) {
158
- connection.results.add(this, {skip: 'valid_hostname(literal)'});
159
- return next();
160
- }
161
-
162
- if (!/\./.test(helo)) {
163
- connection.results.add(this, {fail: 'valid_hostname(no_dot)'});
164
- if (this.cfg.reject.valid_hostname) {
165
- return next(DENY, 'HELO host must be a FQDN or address literal (RFC 5321 2.3.5)');
166
- }
167
- return next();
168
- }
169
-
170
- // this will fail if TLD is invalid or hostname is a public suffix
171
- if (!tlds.get_organizational_domain(helo)) {
172
- // Check for any excluded TLDs
173
- const excludes = this.config.get('helo.checks.allow', 'list');
174
- const tld = (helo.split(/\./).reverse())[0].toLowerCase();
175
- // Exclude .local, .lan and .corp
176
- if (tld === 'local' || tld === 'lan' || tld === 'corp' || excludes.includes(`.${tld}`)) {
177
- return next();
178
- }
179
- connection.results.add(this, {fail: 'valid_hostname'});
180
- if (this.cfg.reject.valid_hostname) {
181
- return next(DENY, "HELO host name invalid");
182
- }
183
- return next();
184
- }
185
-
186
- connection.results.add(this, {pass: 'valid_hostname'});
187
- next();
188
- }
189
-
190
- exports.match_re = function (next, connection, helo) {
191
-
192
- if (this.should_skip(connection, 'match_re')) return next();
193
-
194
- if (this.cfg.list_re?.test(helo)) {
195
- connection.results.add(this, {fail: 'match_re'});
196
- if (this.cfg.reject.match_re) {
197
- return next(DENY, "That HELO not allowed here");
198
- }
199
- return next();
200
- }
201
- connection.results.add(this, {pass: 'match_re'});
202
- next();
203
- }
204
-
205
- exports.rdns_match = function (next, connection, helo) {
206
-
207
- if (this.should_skip(connection, 'rdns_match')) return next();
208
-
209
- if (!helo) {
210
- connection.results.add(this, {fail: 'rdns_match(empty)'});
211
- return next();
212
- }
213
-
214
- if (net_utils.is_ip_literal(helo)) {
215
- connection.results.add(this, {fail: 'rdns_match(literal)'});
216
- return next();
217
- }
218
-
219
- const r_host = connection.remote.host;
220
- if (r_host && helo === r_host) {
221
- connection.results.add(this, {pass: 'rdns_match'});
222
- return next();
223
- }
224
-
225
- if (tlds.get_organizational_domain(r_host) ===
226
- tlds.get_organizational_domain(helo)) {
227
- connection.results.add(this, {pass: 'rdns_match(org_dom)'});
228
- return next();
229
- }
230
-
231
- connection.results.add(this, {fail: 'rdns_match'});
232
- if (this.cfg.reject.rdns_match) {
233
- return next(DENY, 'HELO host does not match rDNS');
234
- }
235
- next();
236
- }
237
-
238
- exports.bare_ip = function (next, connection, helo) {
239
-
240
- if (this.should_skip(connection, 'bare_ip')) return next();
241
-
242
- // RFC 2821, 4.1.1.1 Address literals must be in brackets
243
- // RAW IPs must be formatted: "[1.2.3.4]" not "1.2.3.4" in HELO
244
- if (net_utils.get_ipany_re('^(?:IPv6:)?','$','').test(helo)) {
245
- connection.results.add(this, {fail: 'bare_ip(invalid literal)'});
246
- if (this.cfg.reject.bare_ip) {
247
- return next(DENY, "Invalid address format in HELO");
248
- }
249
- return next();
250
- }
251
-
252
- connection.results.add(this, {pass: 'bare_ip'});
253
- next();
254
- }
255
-
256
- exports.dynamic = function (next, connection, helo) {
257
-
258
- if (this.should_skip(connection, 'dynamic')) return next();
259
-
260
- // Skip if no dots or an IP literal or address
261
- if (!/\./.test(helo)) {
262
- connection.results.add(this, {skip: 'dynamic(no dots)'});
263
- return next();
264
- }
265
-
266
- if (net_utils.get_ipany_re('^\\[?(?:IPv6:)?','\\]?$','').test(helo)) {
267
- connection.results.add(this, {skip: 'dynamic(literal)'});
268
- return next();
269
- }
270
-
271
- if (net_utils.is_ip_in_str(connection.remote.ip, helo)) {
272
- connection.results.add(this, {fail: 'dynamic'});
273
- if (this.cfg.reject.dynamic) {
274
- return next(DENY, 'HELO is dynamic');
275
- }
276
- return next();
277
- }
278
-
279
- connection.results.add(this, {pass: 'dynamic'});
280
- next();
281
- }
282
-
283
- exports.big_company = function (next, connection, helo) {
284
-
285
- if (this.should_skip(connection, 'big_company')) return next();
286
-
287
- if (net_utils.is_ip_literal(helo)) {
288
- connection.results.add(this, {skip: 'big_co(literal)'});
289
- return next();
290
- }
291
-
292
- if (!this.cfg.bigco) {
293
- connection.results.add(this, {err: 'big_co(config missing)'});
294
- return next();
295
- }
296
-
297
- if (!this.cfg.bigco[helo]) {
298
- connection.results.add(this, {pass: 'big_co(not)'});
299
- return next();
300
- }
301
-
302
- const rdns = connection.remote.host;
303
- if (!rdns || rdns === 'Unknown' || rdns === 'DNSERROR') {
304
- connection.results.add(this, {fail: 'big_co(rDNS)'});
305
- if (this.cfg.reject.big_company) {
306
- return next(DENY, "Big company w/o rDNS? Unlikely.");
307
- }
308
- return next();
309
- }
310
-
311
- const allowed_rdns = this.cfg.bigco[helo].split(/,/);
312
- for (const allow of allowed_rdns) {
313
- const re = new RegExp(`${allow.replace(/\./g, '\\.')}$`);
314
- if (re.test(rdns)) {
315
- connection.results.add(this, {pass: 'big_co'});
316
- return next();
317
- }
318
- }
319
-
320
- connection.results.add(this, {fail: 'big_co'});
321
- if (this.cfg.reject.big_company) {
322
- return next(DENY, "You are not who you say you are");
323
- }
324
- next();
325
- }
326
-
327
- exports.literal_mismatch = function (next, connection, helo) {
328
-
329
- if (this.should_skip(connection, 'literal_mismatch')) return next();
330
-
331
- const literal = net_utils.get_ipany_re('^\\[(?:IPv6:)?','\\]$','').exec(helo);
332
- if (!literal) {
333
- connection.results.add(this, {pass: 'literal_mismatch'});
334
- return next();
335
- }
336
-
337
- const lmm_mode = parseInt(this.cfg.check.literal_mismatch, 10);
338
- const helo_ip = literal[1];
339
- if (lmm_mode > 2 && net_utils.is_private_ip(helo_ip)) {
340
- connection.results.add(this, {pass: 'literal_mismatch(private)'});
341
- return next();
342
- }
343
-
344
- if (lmm_mode > 1) {
345
- if (net_utils.same_ipv4_network(connection.remote.ip, [helo_ip])) {
346
- connection.results.add(this, {pass: 'literal_mismatch'});
347
- return next();
348
- }
349
-
350
- connection.results.add(this, {fail: 'literal_mismatch'});
351
- if (this.cfg.reject.literal_mismatch) {
352
- return next(DENY, 'HELO IP literal not in the same /24 as your IP address');
353
- }
354
- return next();
355
- }
356
-
357
- if (helo_ip === connection.remote.ip) {
358
- connection.results.add(this, {pass: 'literal_mismatch'});
359
- return next();
360
- }
361
-
362
- connection.results.add(this, {fail: 'literal_mismatch'});
363
- if (this.cfg.reject.literal_mismatch) {
364
- return next(DENY, 'HELO IP literal does not match your IP address');
365
- }
366
- next();
367
- }
368
-
369
- exports.forward_dns = function (next, connection, helo) {
370
-
371
- if (this.should_skip(connection, 'forward_dns')) return next();
372
- if (!this.cfg.check.valid_hostname) {
373
- connection.results.add(this, {err: 'forward_dns(valid_hostname disabled)'});
374
- return next();
375
- }
376
-
377
- if (net_utils.is_ip_literal(helo)) {
378
- connection.results.add(this, {skip: 'forward_dns(literal)'});
379
- return next();
380
- }
381
-
382
- if (!connection.results.has('helo.checks', 'pass', /^valid_hostname/)) {
383
- connection.results.add(this, {fail: 'forward_dns(invalid_hostname)'});
384
- if (this.cfg.reject.forward_dns) {
385
- return next(DENY, "Invalid HELO host cannot achieve forward DNS match");
386
- }
387
- return next();
388
- }
389
-
390
- this.get_a_records(helo)
391
- .then(ips => {
392
-
393
- if (!ips) {
394
- connection.results.add(this, {err: 'forward_dns, no ips!'});
395
- return next();
396
- }
397
- connection.results.add(this, {ips});
398
-
399
- if (ips.includes(connection.remote.ip)) {
400
- connection.results.add(this, {pass: 'forward_dns'});
401
- return next();
402
- }
403
-
404
- // some valid hosts (facebook.com, hotmail.com) use a generic HELO
405
- // hostname that resolves but doesn't contain the IP that is
406
- // connecting. If their rDNS passed, and their HELO hostname is in
407
- // the same domain, consider it close enough.
408
- if (connection.results.has('helo.checks', 'pass', /^rdns_match/)) {
409
- const helo_od = tlds.get_organizational_domain(helo);
410
- const rdns_od = tlds.get_organizational_domain(connection.remote.host);
411
- if (helo_od && helo_od === rdns_od) {
412
- connection.results.add(this, {pass: 'forward_dns(domain)'});
413
- return next();
414
- }
415
- connection.results.add(this, {msg: `od miss: ${helo_od}, ${rdns_od}`});
416
- }
417
-
418
- connection.results.add(this, {fail: 'forward_dns(no IP match)'});
419
- if (this.cfg.reject.forward_dns) {
420
- return next(DENY, "HELO host has no forward DNS match");
421
- }
422
- next();
423
- })
424
- .catch(err => {
425
- if (err.code === dns.NOTFOUND || err.code === dns.NODATA || err.code === dns.SERVFAIL) {
426
- connection.results.add(this, {fail: `forward_dns(${err.code})`});
427
- return next();
428
- }
429
- if (err.code === dns.TIMEOUT && this.cfg.reject.forward_dns) {
430
- connection.results.add(this, {fail: `forward_dns(${err.code})`});
431
- return next(DENYSOFT, "DNS timeout resolving your HELO hostname");
432
- }
433
- connection.results.add(this, {err: `forward_dns(${err})`, emit_log_level: 'warn'});
434
- next();
435
- })
436
- }
437
-
438
- exports.proto_mismatch = function (next, connection, helo, proto) {
439
-
440
- if (this.should_skip(connection, 'proto_mismatch')) return next();
441
-
442
- const r = connection.results.get('helo.checks');
443
- if (!r || (r && !r.helo_host)) return next();
444
-
445
- if ((connection.esmtp && proto === 'smtp') ||
446
- (!connection.esmtp && proto === 'esmtp')) {
447
- connection.results.add(this, {fail: `proto_mismatch(${proto})`});
448
- if (this.cfg.reject.proto_mismatch) {
449
- return next(DENY, `${proto === 'smtp' ? 'HELO' : 'EHLO'} protocol mismatch`);
450
- }
451
- }
452
-
453
- next();
454
- }
455
-
456
- exports.proto_mismatch_smtp = function (next, connection, helo) {
457
- this.proto_mismatch(next, connection, helo, 'smtp');
458
- }
459
-
460
- exports.proto_mismatch_esmtp = function (next, connection, helo) {
461
- this.proto_mismatch(next, connection, helo, 'esmtp');
462
- }
463
-
464
- exports.emit_log = function (next, connection, helo) {
465
- // Spits out an INFO log entry. Default looks like this:
466
- // [helo.checks] helo_host: [182.212.17.35], fail:big_co(rDNS) rdns_match(literal), pass:valid_hostname, match_re, bare_ip, literal_mismatch, mismatch, skip:dynamic(literal), valid_hostname(literal)
467
- //
468
- // Although sometimes useful, that's a bit verbose. I find that I'm rarely
469
- // interested in the passes, the helo_host is already logged elsewhere,
470
- // and so I set this in config/results.ini:
471
- //
472
- // [helo.checks]
473
- // order=fail,pass,msg,err,skip
474
- // hide=helo_host,multi,pass
475
- //
476
- // Thus set, my log entries look like this:
477
- //
478
- // [UUID] [helo.checks] fail:rdns_match
479
- // [UUID] [helo.checks]
480
- // [UUID] [helo.checks] fail:dynamic
481
- connection.loginfo(this, connection.results.collate(this));
482
- next();
483
- }
484
-
485
- exports.get_a_records = async function (host) {
486
-
487
- if (!/\./.test(host)) {
488
- // a single label is not a host name
489
- const e = new Error("invalid hostname");
490
- e.code = dns.NOTFOUND;
491
- throw e;
492
- }
493
-
494
- // Set-up timer
495
- let timed_out = false;
496
- const timer = setTimeout(() => {
497
- timed_out = true;
498
- const err = new Error(`timeout resolving: ${host}`);
499
- err.code = dns.TIMEOUT;
500
- this.logerror(err);
501
- throw err;
502
- }, (this.cfg.main.dns_timeout || 28) * 1000);
503
-
504
- // fully qualify, to ignore any search options in /etc/resolv.conf
505
- if (!/\.$/.test(host)) host = `${host}.`;
506
-
507
- // do the queries
508
- let ips
509
- let err = '';
510
- try {
511
- ips = await net_utils.get_ips_by_host(host)
512
- }
513
- catch (errs) {
514
- for (const error of errs) {
515
- switch (error.code) {
516
- case dns.NODATA:
517
- case dns.NOTFOUND:
518
- case dns.SERVFAIL:
519
- continue;
520
- default:
521
- err = `${err}, ${error.message}`;
522
- }
523
- }
524
- }
525
-
526
- // results is now equals to: {queryA: 1, queryAAAA: 2}
527
- if (timed_out) return;
528
- if (timer) clearTimeout(timer);
529
- if (!ips.length && err) throw err
530
- // this.logdebug(this, host + ' => ' + ips);
531
- // return the DNS results
532
- return ips;
533
- };