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,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 || 30) * 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
- };