diff --git a/contrib/sendmail/CACerts b/contrib/sendmail/CACerts index 630707555657..5e3b5ee084c7 100644 --- a/contrib/sendmail/CACerts +++ b/contrib/sendmail/CACerts @@ -1,7 +1,7 @@ # This file contains some CA certificates that are used to sign the # certificates of mail servers of members of the sendmail consortium -# who may reply to questions etc sent to sendmail.org. -# It is useful to allow connections from those MTAs that can present +# who may reply to questions etc sent to support.sendmail.org. +# It is useful to allow connections from those MTAs which can present # a certificate signed by one of these CA certificates. # @@ -9,92 +9,92 @@ Certificate: Data: Version: 3 (0x2) Serial Number: - 81:9d:41:0f:40:55:ac:4a - Signature Algorithm: sha1WithRSAEncryption - Issuer: C=US, ST=California, L=Berkeley, O=Endmail Org, OU=MTA, CN=CA/emailAddress=ca+ca-rsa2018@esmtp.org + 92:a1:3b:d3:90:0b:ea:a7 + Signature Algorithm: sha256WithRSAEncryption + Issuer: C=US, ST=California, L=Berkeley, O=Endmail Org, OU=MTA, CN=CA/emailAddress=ca+ca-rsa2021@esmtp.org Validity - Not Before: Feb 27 02:30:55 2018 GMT - Not After : Feb 26 02:30:55 2021 GMT - Subject: C=US, ST=California, L=Berkeley, O=Endmail Org, OU=MTA, CN=CA/emailAddress=ca+ca-rsa2018@esmtp.org + Not Before: Feb 25 17:44:02 2021 GMT + Not After : Feb 25 17:44:02 2024 GMT + Subject: C=US, ST=California, L=Berkeley, O=Endmail Org, OU=MTA, CN=CA/emailAddress=ca+ca-rsa2021@esmtp.org Subject Public Key Info: Public Key Algorithm: rsaEncryption Public-Key: (2048 bit) Modulus: - 00:b8:a3:8d:79:28:c1:1f:9c:11:74:43:26:e1:3b: - cc:14:87:5b:6b:64:4c:ed:79:1b:7f:2a:03:d0:7b: - ef:9e:88:b0:64:36:ee:58:ef:fd:d9:c7:20:b3:71: - e9:6d:1e:a7:bc:c1:7c:3b:fe:2a:e4:16:2f:bc:d6: - 2c:f5:98:f9:c4:21:1c:ca:c3:7e:57:89:c8:a9:2f: - da:6b:9b:52:d6:c9:9d:98:97:6d:08:7c:a6:37:4e: - d4:26:bb:db:73:b0:38:ef:7d:1e:dd:8e:dd:8e:17: - 2f:a0:3d:a9:0e:4d:f0:2b:b8:14:23:33:ad:c8:a0: - e5:9d:0f:27:ad:83:a2:78:90:05:ec:29:06:91:07: - 45:6c:5f:ba:8e:1d:f1:d7:1b:2d:f9:99:ba:2e:27: - e1:03:7d:e9:d2:54:35:cc:39:79:07:83:d8:93:9b: - d6:ef:72:ab:d4:63:8e:6b:f7:00:66:5f:77:e8:b6: - bc:de:5f:8c:d0:ce:1a:c4:db:03:9d:e4:ee:0a:ec: - 77:c5:f2:30:69:7e:70:12:e5:c2:4a:28:3f:e7:19: - eb:af:41:fb:e6:a6:1d:b5:fd:2b:99:03:f5:20:90: - 38:73:bd:43:70:da:cf:1f:34:5d:ab:17:4b:73:cf: - f9:3d:e1:a2:79:14:de:d8:40:85:82:c4:5a:84:82: - 32:f1 + 00:cc:8c:39:bd:cf:55:4f:66:2a:78:c7:65:47:81: + dd:d1:3f:08:12:4b:87:40:48:95:5c:24:52:65:a1: + 82:1c:f4:90:a1:7c:f7:27:8f:02:e5:cb:ac:89:ae: + b8:25:4e:26:da:14:20:07:29:a4:59:03:61:b4:44: + ae:45:55:b4:72:7d:66:9a:88:de:59:bf:6f:31:23: + 06:29:ab:c2:b9:a0:6c:6a:5d:d0:ac:e6:b8:ac:8a: + 6f:5e:bb:f3:19:b5:8d:e1:df:2e:d1:7f:1a:bc:2c: + 13:10:65:46:7f:68:c7:60:49:c6:30:4e:a0:24:ed: + d4:a8:27:cf:b2:d0:c5:7c:96:47:82:b6:f1:17:0a: + 5a:35:82:0b:85:0f:17:71:a9:bd:3a:4c:e6:32:95: + 3e:68:f7:3d:f5:04:33:16:19:1e:4c:0a:04:c3:1f: + 9e:ba:db:e2:0d:29:c8:3f:29:cf:47:cb:11:db:d2: + cd:d0:26:2f:35:eb:7d:a2:60:18:e7:7b:a2:43:15: + 59:d7:ea:9d:38:60:f1:48:df:57:54:df:8a:50:b9: + e3:5c:72:82:51:b7:05:78:c2:14:08:71:71:1c:06: + 44:4a:85:73:08:a8:49:50:b2:d2:fb:da:a2:a5:5a: + 36:49:a8:4b:38:56:f6:67:0f:12:34:39:cc:fb:9c: + bd:d3 Exponent: 65537 (0x10001) X509v3 extensions: - X509v3 Subject Key Identifier: - 42:37:75:E7:8F:12:CF:D9:EB:21:22:7D:8A:E8:49:21:FD:E2:3A:3A - X509v3 Authority Key Identifier: - keyid:42:37:75:E7:8F:12:CF:D9:EB:21:22:7D:8A:E8:49:21:FD:E2:3A:3A - DirName:/C=US/ST=California/L=Berkeley/O=Endmail Org/OU=MTA/CN=CA/emailAddress=ca+ca-rsa2018@esmtp.org - serial:81:9D:41:0F:40:55:AC:4A - X509v3 Basic Constraints: + X509v3 Subject Key Identifier: + 86:F0:F9:7A:CD:66:A9:16:CC:A3:26:08:3D:B3:B2:42:C2:E5:A9:13 + X509v3 Authority Key Identifier: + keyid:86:F0:F9:7A:CD:66:A9:16:CC:A3:26:08:3D:B3:B2:42:C2:E5:A9:13 + DirName:/C=US/ST=California/L=Berkeley/O=Endmail Org/OU=MTA/CN=CA/emailAddress=ca+ca-rsa2021@esmtp.org + serial:92:A1:3B:D3:90:0B:EA:A7 + X509v3 Basic Constraints: CA:TRUE - X509v3 Subject Alternative Name: - email:ca+ca-rsa2018@esmtp.org - X509v3 Issuer Alternative Name: - email:ca+ca-rsa2018@esmtp.org - Signature Algorithm: sha1WithRSAEncryption - 0b:4c:e5:c2:ed:0a:e5:7b:95:29:22:d4:8f:5f:cb:1b:b1:e3: - 4c:fc:90:e7:2e:97:87:87:a2:63:0d:6d:4d:f0:1f:0d:84:11: - dc:df:b7:fa:c3:c6:2e:07:e9:a0:e9:a6:9f:54:17:ad:1a:d0: - 36:be:31:cc:a5:85:a0:45:4a:87:45:80:7e:de:ea:97:68:e0: - 2b:09:5d:9a:31:6f:f5:78:22:c5:66:2a:99:70:9e:6d:c4:ab: - f6:90:01:70:53:07:66:6c:a6:b5:ce:4b:36:05:83:87:0c:a7: - e0:1e:34:d0:5e:76:a4:20:71:cd:9d:c1:ae:82:27:e0:6f:16: - 57:74:e7:63:9f:d0:3d:72:91:6d:97:a4:82:23:84:dd:6e:0d: - da:43:00:a7:ce:2f:f8:79:04:67:6a:e5:b0:ab:30:d8:f1:90: - 10:43:3b:09:77:27:34:a4:d4:c0:25:4e:21:32:a3:ab:60:1c: - 9d:6e:e2:65:39:51:7f:cd:9f:88:3a:7e:f4:38:af:7b:5b:a7: - bb:7b:70:97:21:59:fc:5c:55:a1:db:74:0a:37:1e:33:97:5f: - 70:32:98:b3:d9:99:4e:08:3c:de:01:82:17:9b:49:d7:fa:c9: - 45:8d:93:cc:42:d6:36:f2:39:3a:47:28:3f:6f:6a:e5:23:f3: - 5c:d4:a3:1b + X509v3 Subject Alternative Name: + email:ca+ca-rsa2021@esmtp.org + X509v3 Issuer Alternative Name: + email:ca+ca-rsa2021@esmtp.org + Signature Algorithm: sha256WithRSAEncryption + 41:14:09:49:01:5f:51:ff:20:7b:c2:41:79:9d:11:3c:7c:48: + d6:43:d4:c6:0d:55:e6:76:bb:2c:c7:fb:dd:10:53:79:30:1a: + 35:64:2c:d0:64:b6:5a:fd:a9:d3:e3:09:8c:7d:22:81:b7:71: + a6:7d:bf:80:24:79:24:c1:61:6d:54:ab:14:4b:5a:54:cb:75: + 47:2e:e5:51:6f:cb:91:b6:a7:e8:aa:8d:78:c5:7e:05:56:3b: + 31:02:bd:0c:e4:af:78:27:7d:6d:bf:fd:0f:0d:2a:00:1d:cc: + a2:34:4d:a3:9e:70:45:89:56:2d:d2:35:ee:26:ea:0f:9d:fc: + c0:2c:64:f6:55:af:de:e0:72:64:e2:20:8f:e2:f2:e9:e2:6c: + 3a:0c:45:23:dd:80:57:25:fa:18:bb:25:f8:7e:3c:3b:a7:ef: + 40:f0:ba:6f:ce:b1:eb:f9:14:03:04:34:3d:e0:43:a6:8d:95: + d0:a4:dc:df:e4:79:ce:8d:e2:1e:30:ff:55:0c:e6:9d:e4:1d: + 62:cc:a5:4f:9a:6f:c0:b4:1f:05:7c:a7:c7:b1:72:58:98:ad: + 2f:f9:8a:41:0c:48:d5:78:ad:af:eb:ff:59:0b:4a:99:26:5b: + e8:8c:e3:e5:6b:01:d9:a0:db:a2:1b:d8:8e:f1:82:38:58:ba: + 8c:11:65:36 -----BEGIN CERTIFICATE----- -MIIE4jCCA8qgAwIBAgIJAIGdQQ9AVaxKMA0GCSqGSIb3DQEBBQUAMIGOMQswCQYD +MIIE4jCCA8qgAwIBAgIJAJKhO9OQC+qnMA0GCSqGSIb3DQEBCwUAMIGOMQswCQYD VQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTERMA8GA1UEBwwIQmVya2VsZXkx FDASBgNVBAoMC0VuZG1haWwgT3JnMQwwCgYDVQQLDANNVEExCzAJBgNVBAMMAkNB -MSYwJAYJKoZIhvcNAQkBFhdjYStjYS1yc2EyMDE4QGVzbXRwLm9yZzAeFw0xODAy -MjcwMjMwNTVaFw0yMTAyMjYwMjMwNTVaMIGOMQswCQYDVQQGEwJVUzETMBEGA1UE +MSYwJAYJKoZIhvcNAQkBFhdjYStjYS1yc2EyMDIxQGVzbXRwLm9yZzAeFw0yMTAy +MjUxNzQ0MDJaFw0yNDAyMjUxNzQ0MDJaMIGOMQswCQYDVQQGEwJVUzETMBEGA1UE CAwKQ2FsaWZvcm5pYTERMA8GA1UEBwwIQmVya2VsZXkxFDASBgNVBAoMC0VuZG1h aWwgT3JnMQwwCgYDVQQLDANNVEExCzAJBgNVBAMMAkNBMSYwJAYJKoZIhvcNAQkB -FhdjYStjYS1yc2EyMDE4QGVzbXRwLm9yZzCCASIwDQYJKoZIhvcNAQEBBQADggEP -ADCCAQoCggEBALijjXkowR+cEXRDJuE7zBSHW2tkTO15G38qA9B7756IsGQ27ljv -/dnHILNx6W0ep7zBfDv+KuQWL7zWLPWY+cQhHMrDfleJyKkv2mubUtbJnZiXbQh8 -pjdO1Ca723OwOO99Ht2O3Y4XL6A9qQ5N8Cu4FCMzrcig5Z0PJ62DoniQBewpBpEH -RWxfuo4d8dcbLfmZui4n4QN96dJUNcw5eQeD2JOb1u9yq9Rjjmv3AGZfd+i2vN5f -jNDOGsTbA53k7grsd8XyMGl+cBLlwkooP+cZ669B++amHbX9K5kD9SCQOHO9Q3Da -zx80XasXS3PP+T3honkU3thAhYLEWoSCMvECAwEAAaOCAT8wggE7MB0GA1UdDgQW -BBRCN3XnjxLP2eshIn2K6Ekh/eI6OjCBwwYDVR0jBIG7MIG4gBRCN3XnjxLP2esh -In2K6Ekh/eI6OqGBlKSBkTCBjjELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlm +FhdjYStjYS1yc2EyMDIxQGVzbXRwLm9yZzCCASIwDQYJKoZIhvcNAQEBBQADggEP +ADCCAQoCggEBAMyMOb3PVU9mKnjHZUeB3dE/CBJLh0BIlVwkUmWhghz0kKF89yeP +AuXLrImuuCVOJtoUIAcppFkDYbRErkVVtHJ9ZpqI3lm/bzEjBimrwrmgbGpd0Kzm +uKyKb1678xm1jeHfLtF/GrwsExBlRn9ox2BJxjBOoCTt1Kgnz7LQxXyWR4K28RcK +WjWCC4UPF3GpvTpM5jKVPmj3PfUEMxYZHkwKBMMfnrrb4g0pyD8pz0fLEdvSzdAm +LzXrfaJgGOd7okMVWdfqnThg8UjfV1TfilC541xyglG3BXjCFAhxcRwGREqFcwio +SVCy0vvaoqVaNkmoSzhW9mcPEjQ5zPucvdMCAwEAAaOCAT8wggE7MB0GA1UdDgQW +BBSG8Pl6zWapFsyjJgg9s7JCwuWpEzCBwwYDVR0jBIG7MIG4gBSG8Pl6zWapFsyj +Jgg9s7JCwuWpE6GBlKSBkTCBjjELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlm b3JuaWExETAPBgNVBAcMCEJlcmtlbGV5MRQwEgYDVQQKDAtFbmRtYWlsIE9yZzEM MAoGA1UECwwDTVRBMQswCQYDVQQDDAJDQTEmMCQGCSqGSIb3DQEJARYXY2ErY2Et -cnNhMjAxOEBlc210cC5vcmeCCQCBnUEPQFWsSjAMBgNVHRMEBTADAQH/MCIGA1Ud -EQQbMBmBF2NhK2NhLXJzYTIwMThAZXNtdHAub3JnMCIGA1UdEgQbMBmBF2NhK2Nh -LXJzYTIwMThAZXNtdHAub3JnMA0GCSqGSIb3DQEBBQUAA4IBAQALTOXC7Qrle5Up -ItSPX8sbseNM/JDnLpeHh6JjDW1N8B8NhBHc37f6w8YuB+mg6aafVBetGtA2vjHM -pYWgRUqHRYB+3uqXaOArCV2aMW/1eCLFZiqZcJ5txKv2kAFwUwdmbKa1zks2BYOH -DKfgHjTQXnakIHHNncGugifgbxZXdOdjn9A9cpFtl6SCI4Tdbg3aQwCnzi/4eQRn -auWwqzDY8ZAQQzsJdyc0pNTAJU4hMqOrYBydbuJlOVF/zZ+IOn70OK97W6e7e3CX -IVn8XFWh23QKNx4zl19wMpiz2ZlOCDzeAYIXm0nX+slFjZPMQtY28jk6Ryg/b2rl -I/Nc1KMb +cnNhMjAyMUBlc210cC5vcmeCCQCSoTvTkAvqpzAMBgNVHRMEBTADAQH/MCIGA1Ud +EQQbMBmBF2NhK2NhLXJzYTIwMjFAZXNtdHAub3JnMCIGA1UdEgQbMBmBF2NhK2Nh +LXJzYTIwMjFAZXNtdHAub3JnMA0GCSqGSIb3DQEBCwUAA4IBAQBBFAlJAV9R/yB7 +wkF5nRE8fEjWQ9TGDVXmdrssx/vdEFN5MBo1ZCzQZLZa/anT4wmMfSKBt3Gmfb+A +JHkkwWFtVKsUS1pUy3VHLuVRb8uRtqfoqo14xX4FVjsxAr0M5K94J31tv/0PDSoA +HcyiNE2jnnBFiVYt0jXuJuoPnfzALGT2Va/e4HJk4iCP4vLp4mw6DEUj3YBXJfoY +uyX4fjw7p+9A8LpvzrHr+RQDBDQ94EOmjZXQpNzf5HnOjeIeMP9VDOad5B1izKVP +mm/AtB8FfKfHsXJYmK0v+YpBDEjVeK2v6/9ZC0qZJlvojOPlawHZoNuiG9iO8YI4 +WLqMEWU2 -----END CERTIFICATE----- diff --git a/contrib/sendmail/KNOWNBUGS b/contrib/sendmail/KNOWNBUGS index d3c0a2ff416d..b44f931af585 100644 --- a/contrib/sendmail/KNOWNBUGS +++ b/contrib/sendmail/KNOWNBUGS @@ -20,6 +20,14 @@ This list is not guaranteed to be complete. then it will be truncated which may result in a syntactically invalid address. +* Berkeley DB map locking problem with fcntl(). + + For Linux the default is to use fcntl() for file locking. However, + this does not work with Berkeley DB 5.x and probably later. + Switching to flock(), i.e., compile with -DHASFLOCK fixes this + (however, the have been problems with flock() on some Linux + versions). Alternatively, use CDB or an earlier BDB version. + * Delivery to programs that generate too much output may cause problems If e-mail is delivered to a program which generates too much diff --git a/contrib/sendmail/Makefile b/contrib/sendmail/Makefile index daf20f288f8b..1cb6b56d6a3b 100644 --- a/contrib/sendmail/Makefile +++ b/contrib/sendmail/Makefile @@ -10,8 +10,8 @@ OPTIONS= $(CONFIG) $(FLAGS) all: FRC @for x in $(SUBDIRS); \ do \ - (cd $$x; echo Making $@ in:; pwd; \ - $(SHELL) $(BUILD) $(OPTIONS)); \ + (cd $$x && echo Making $@ in: && pwd && \ + $(SHELL) $(BUILD) $(OPTIONS)) || exit; \ done clean: FRC @@ -24,22 +24,22 @@ clean: FRC install: FRC @for x in $(SUBDIRS); \ do \ - (cd $$x; echo Making $@ in:; pwd; \ - $(SHELL) $(BUILD) $(OPTIONS) $@); \ + (cd $$x && echo Making $@ in: && pwd && \ + $(SHELL) $(BUILD) $(OPTIONS) $@) || exit; \ done install-docs: FRC @for x in $(SUBDIRS); \ do \ - (cd $$x; echo Making $@ in:; pwd; \ - $(SHELL) $(BUILD) $(OPTIONS) $@); \ + (cd $$x && echo Making $@ in: && pwd && \ + $(SHELL) $(BUILD) $(OPTIONS) $@) || exit; \ done fresh: FRC @for x in $(SUBDIRS); \ do \ - (cd $$x; echo Making $@ in:; pwd; \ - $(SHELL) $(BUILD) $(OPTIONS) -c); \ + (cd $$x && echo Making $@ in: && pwd && \ + $(SHELL) $(BUILD) $(OPTIONS) -c) || exit; \ done $(SUBDIRS): FRC diff --git a/contrib/sendmail/PGPKEYS b/contrib/sendmail/PGPKEYS index 03476f63a5ed..0d0b0d5a766c 100644 --- a/contrib/sendmail/PGPKEYS +++ b/contrib/sendmail/PGPKEYS @@ -188,6 +188,182 @@ mk6wxhyuojEHuR7it6IU5BP8vaAGrL1jb1c2EeAe+pdJwpAb1Aq6MU6uWqOGup8t -----END PGP PUBLIC KEY BLOCK----- +pub 4096R/4BEE1BEE 2021-01-24 + Key fingerprint = F4CE 2263 2102 53D6 A9F9 79B0 4C66 EA8D 4BEE 1BEE +uid Sendmail Signing Key/2021 +sub 4096R/A9C0321B 2021-01-24 + +-----BEGIN PGP PUBLIC KEY BLOCK----- + +mQINBGANHTwBEACw6b3NmDyyB6uPll+h+pyOmQrbX+up2S007yTXhj2EnYiriLcL +MdMspVLXl/wtABtfTZ9Lf3v3FuNwHZsVdSZWCFmwlWPptsRrF0VWwYBzxgH6QIUK +Qx9sFAD+KVD/9Cfl7YXeu5lZrNn3D8FoQB480jJJRaxshXcB6y9QCyKHeCZx/3Ct +1TE/tlFOgGoKJzNADOimH5SeEZ2gHhB6WB/yKLQYWS8EAvTlGdgZVo6VY6Ar35cd +3Z9TCQxS8YDsA0p6zENCJ4QgiwolmgZHa4R3/9jObxhVrIpCKCUN+rSdmKDotugP +GPDyZ0rZRAaRlyqt3rYKVAztkLTU6TbDNLmDpw3CQv3Tpbb2TT39ySmruVVJLA1C +DYQrh8f35ic0mDwYxKA5KIPZNj9vcReVrwxPDAV5to4n/ZjNNfnqxRiiq4+IzGZ4 +dTlwh4pECps0WdqphLAoTotFcdvYg8cfHMBULdIGqciAGfu7G0yqvlxt4nRe1k8D +60yAwDtqgO3ThtiTzuYkHZAUmAYOBYPF4e/X/zicWoL+whirV6AELnmv6lft5TaW +UfXbcx0njY/QPa1iy3g8qkQcY8durY9OVYnA5X4von1vMC4naEEf/cFsdDBl+nZG +/XxBHr1QX5/P7egbnnF9qMqry856oPE8bjv1KBqZ52UxmGRl14k8gKcfowARAQAB +tDFTZW5kbWFpbCBTaWduaW5nIEtleS8yMDIxIDxzZW5kbWFpbEBTZW5kbWFpbC5P +Ukc+iQJVBBMBCgA/FiEE9M4iYyECU9ap+XmwTGbqjUvuG+4FAmANHTwCGwMLCwkN +CAoMBwsEAwIGFQoJCAsDBRYCAwEAAh4BAheAAAoJEExm6o1L7hvuZlcP/1ipTzk+ +UT36bM/DbeBHaGhQ4yOSg0iiHzu+bzzP3jZslG7rlLgzowEPk7plDlnVbY6MUeXQ +4hO7keoAn5Cskg8jsrh+kpWYlNTPPKFdjgcuWbUEVAoRIprq1kEVqcG2ai0t1iaH +CmrUwFRqCCEPoHKg1U2GKcKafFaxOZwM27kV6yTLM5sYFVsoSh+bJ7sa9hymdwrf +/d1Rh5E+MHJatn3TXGh06aPkVGozyufTXipsxzd5VaBjLDTYbMFkiOt9MC9Sx2IS +BO5Ws2tOlNslGiAeXQG8EaJB4xrvhOi4i99nBA5TYWVdOAwfJGUZ+X4hItMwlCIg +4TaJcYHQ7GASELGSGA7azd2JeqbEskiCdabWF8aSbUxA68HRCOjAAUk5afxqEc5J +BVfT1QmWAPK5cNQTojbd7msrlGXmcSQyFBUDSzoeQNFhpmDpAXCLnGt0vcwbqTik +Ft+2vJ9nbSczKHkxmN1hudpVdsUNfgGi7p4VzyQq/OzYFVeMXrBBt6aLyATjCoY9 +b7chMDyJBFLEk47U0qQe2VhexI8Fk9Z8wFTPF97gb3rSk5pAfIbCZ9eDcIZuR5eD +yDDd23vxsMJK0haD/nZ6gQNqBeCg+zDE8g4T9zCdOtavLuqwOqPUZDnNdke9cA0m +6GSo2MccibyMdqijETcDOPOC47hrIu68QE54iQEzBBABCgAdFiEEsICXn00EPhnQ +WjacYp747gyLgzMFAmANHmsACgkQYp747gyLgzP1zgf/QJi9+sMvoNVks4+lU4xW +9fy4C7+fAO96dJtSO5jSn+9M/C55UmU0kWz8XEU88XCVyChLmpSb+Y+2sf5XhWEY ++KDLUHgqiT6NItozXKVggNFMsxkzDi7mzdkCIevTlcGbQSxai7hbKwZzGPb/OzUK +pRtLl6hTV5wLlsit41EAwILnRmmn6Iix6SPaCx0YvAIKBiy7CSiJyhPbGEKAeEx9 +OTZ9ce0iQWxaCGNgXv22HPvJ7V3VwmfZBJnHcY0ooxEjz/Ky9kHXc+3yHznlATXs +pzOMH0z+zmHldvIBz0djgVlhn2TRkKSSTaGd4kbLKLmci4Ax/il5noR1hZ82aVhl +TokBMwQQAQoAHRYhBLF1lkRTA13O3XvpGWBN+/KFQQq+BQJgDR6pAAoJEGBN+/KF +QQq+4OAH/RpvOktec2Y0AvzWjHorXWmPLi9xEMIuj2GVVVsg+eXP2CDpYuEOVYLP +8VCWpObXADj+w0DIOMcyqUbMPxkps+CPXjTRc/qED3FLvGNTfNQMe5hDTbbs/tw2 +FtfI9Jzlwsmhcfg5ZxnQKDCPGPQufN9AbQHWc4VIEwhQRc9T/cfBhioWUwrtkgPT +BBTPnJp/nA10Rn+ycURA+BLdlhAFwuTYBH7nWHkDJUGLHFIat2RhHRakZNhcMrhE +cXBrg5ONK9qJYtJXzlHiQhM6NP2RPVvYCzLlqkT13SqvLsPMSncyKlIlUuQZqdcE +QOwGRgp2jkZeRYVBYfFzfIpu5gxVQYOJATMEEAEKAB0WIQRYcmIYqRNADeZgNgE5 +pMd9qXiEsAUCYA0esAAKCRA5pMd9qXiEsAB0CAChpLMwocSQ6hpY7nfVl6wAb3SP +9C2Dwr89YxzqBYS7i3b/0pB4t1c4cg0vC72DeOIUwuAwUOq6NVgPYnh/NpovWouP +HN/3WrI013yGkNZIYz3fb8w2+pk0FFndU121pn3IbVYyMxegyHyN+F5NKZCfsTu+ +imlqje26ecBPuz4wcVqYyl/jnR/MU54uMhQW8q0lxCMS67uta0wd2EaTXNxq17Lp +Z48pNOBiAXxZnXaP996T+7whtLBr9isgeZyeizenjupX69bllRVbwuO8uboTsisC +LlUbOLzdvTjuSrAQAzNaAfVjNsxzEvLcxxKaPKPG1ubrHT50k3zpB/Ixi+oliQEz +BBABCgAdFiEEynqPOaJBn/+wqasnjlrp+87u9DsFAmANHrcACgkQjlrp+87u9DtA +kQf/SLAdxTmR/l95WdeOgvxINcV5ADxCkpO1iJeLp440uddscRrrfHdibEngfAA+ +ARwPv2/jhJgInCOQe+4lmsd+4NtKtanXiRZai1MXCxcF5VLTOMs8Vl7EUMAL5JWG +IlvmT4/H7Zhji64KpDFkwEjsE8SdZ6HJokJMFSq+YYBgvnsu/GDSfDpb/HtdM845 +pjxHJ+r93KPRepncLedgyDsQpzzRIgUJNhuC+UGtRp+3qRf1eWSkO6qbyL8DtFfW +WwX1gG099nr8m9Gj+R8zH6HTnWWuFnUyDTHdTN7/25vZ9eoAgjIx0I3g+O02l42B +G5HeOuLSMdUoqqvOq8313wvWSIkBMwQQAQoAHRYhBLh9RWmG8ZSEB+XMtD1osl1S +B8rTBQJgDR69AAoJED1osl1SB8rThsMH/0JcgLmhr3K4t0cxt94u6UN1pVQZDrgG +uMEDpOxW4nPMwN3SkWMM3K7zw0TiGkksyFifRi7zY1BqRilJOGyLkyB3zCY76hKV +SuLx3U4B6eyrAY8gsPownOdY8FJB3o27uXhPX17qLWOl83/GQMoyRfmmwkBnL4hc +puJcPT4bOt3OhDK7bs1vGabS9L3HuX0lUIcp8VKquJHXgS+xIr/lMBk5Jit1Qx/p +VjqmL2qIxTMubmKxU5RxsCZygdV92kBLzYqZ3JO6LOPCwD1a4fZRlwAW2hpC2gtW +KHK1/QZBSgJGjJUgXGV3fYYR3WH5qmTCAWu2HEloLRSdzdHLldWCsUOJATMEEAEK +AB0WIQRJ9qi+hHM5SVGRbzth3hHs4nY6cwUCYA0ewgAKCRBh3hHs4nY6c4ygB/44 +pigG2UoBQNZq7R9ajbU5nRkl9mVCZ4dEqY6i3QJs5tGew+r774jMouL/sBTXMnvS +zD1XgJevJYnQq5U/08zvYDvqrhm4yTkbgg9UqhD9UI8M/XgN0DtbFT6EU/N92lO4 +2xWBMTyAwjVl9JPPjhMoUsGVScZ8pjplJZlgJNuy8GVu6vUoW8j1Gw0jIPKJ0ufy +20uc4jLuKVmxjj7Z5NsFnWJtiFFq/TknppOQZ8KvZjVzrH6EIOmCJfCnuSATiVsb +YJzMAjshhG+fJsm24loUjmDDAzy4Nwf18IJb+wSe1oFCSAz3euhIAIxBFkihA8wk +G4QmCnvdbPfYy4WIMDNEiQEzBBABCgAdFiEEMLynRwX6QVRVcx17qvW13gW9zFMF +AmANHsgACgkQqvW13gW9zFNuYggAwSZ7y+qCvdvFu6LD4qvk/phRF5VINZIHfl1k +aOVQWA+DZaDM8lRsvn2lxGFksaPzK9ZXd5QnF3QTlOkEsCILE1tmL7Myob27PaGV +4mQXjY9bUXe/Ulj4VbHlWjkt5wpwGj9bRuxnn/RKKRNCpknzqv8VTCMVwUyCF4xE +P0BGFXiyPV+PTNN2GwV5l46zn1FWzTlSgbAxjwQBh43RMuBWG320w+YEysJMs4y9 +k0f3i16hO4G/MiD4WRIaohqjBN6ii/sksYf6mgsZieUlAeQPnovi8pScq6s2cYzJ +krZNxX6PCNQFTLs1GvLh6IQgypN9Lxxu4FW82wKQMS9yIKkIMokBMwQQAQoAHRYh +BA9clq7I5p6cjlQuXG1M0ZQp+wPeBQJgDR7SAAoJEG1M0ZQp+wPeFfcH+wUQdI/R +eMuLByF9cjdC0AfnOXD46azyt7Lgyzdi5OK8xAMmfTGH0iYGGv3pNfcbTxblJ868 +PPjUc2arF6CkLZ5hIQ6dUBmmxG+YOecOZF4jO6Z0WFi1XqxRomhy0m9TNQ931I88 +VRpd0/XepnvJc1lTOiTmxKTFex7mKqzTNBeXlNkVOXpM4aCq4AejEgnEzr5imfyF +P2qyITbyGpWrnTKtg4ASYWVU+JAZ3/eZIl/0pNuD0/C9MGRmS2yGM82KKMYrRV3X +QNAdg6LPi8MicUZWlcVYqR/7jEkJeppUpM46EtEo5YoXQR9UflSdu1xjpBzgU56d +MjXtTE5ROtVDl42JAjMEEAEKAB0WIQQ8ih6Of0TK3hFP7UZLyb2ma/cmrQUCYA0e +2AAKCRBLyb2ma/cmrdDSD/92AidTGYuf+D3SbIOBhQttWp3SvnOj5UuqgXtHrmuq +vbhawUAAby+CL0hMOqYk/Z30N/Sr+OQmNyH+Q1C4nuoq4KOINBuaKpcioQai/Jre +TthuVzeFDk33bQd+IQ4n0WXnVWg0DlpIhDDtZyA2Qqj4nPPsnjuw+Y62VuXFahr1 +ci+8sVns9VZJyVKPzGAKo/4rKjRlAqqVTlh5/RvMJ01TvWwSXSg1+yM08e/zaOCz +tuIfZAjDZNqXKIU+3xlKKvQGnNxUB+Bxn6ZaXW/YCzf/uabYfy2i4GIBhyj3dRSH +zbDSg0b/l0zJDIi6qzTzXZFEQr5AFu3CZeLR8maRU/1olCFR2aE29XoAtEF+SyPh +eI8ZhXqL2ccJqStD37TMsUmemTgBkH1Rig3eelRDeaZ6oh5UjuKcg7IpdmyYdRNE +5KO3afHdhM6C/CXoh689273ddasvdYcGCIYku6AjiNjcr4sNbGdmqDNc/6emHqp4 +WxyKfc5AuqZpmbEVhIYG1PTmldJl78EZBYoLjea6fai+6LH3c75p85lUWbfcpq7s +QczweRPz/X+YMnNpCo8+psngSBIjDiJF7JFrVCFPyH8zFbva/TWCZ3Cf8Z4GLm0d +e1gBJfFeXaQHHL2qaX5FXiYqwL2cjmr09lV3hWmQC9bA4q7Z/q2BEjZtZuPJn/qZ +NIkCMwQQAQoAHRYhBKaHPSSk1tYoSuQqdfBgWf1dx8w/BQJgDR7dAAoJEPBgWf1d +x8w/SdkP/1uvi7L2ZVvq564VXA+5YFNq+BvzMDYkf/8RaAAFFUVbblQQBjlHN8nA +ViZZepOJOmba639e8E/uXsXF5z0l7Y1XEiuU6xofjmX8i9Px3MG5G1mXQGgaozW7 +fimU81f5DlLFv3W9lrZ1iQdpfZQYpBMdE6PuBl4wvElHPB6rVTxBIigjVsQceXMV +b64RttDSX84glqv15rTrPQLPg5duX+YzMOVKyH7tWuuOsPuWaUZejNieX7UubA4s +E1pnpH0OBpw/d8r0Rte9ZifmSavfPygaLC3w5ihXKwPLVikhOIF7PgsVaRRBzJQL +pw7BTt+nGOZIQofW1TM8gOPPrbWzwyCnPEMzjyM6g46zsW8FRxTq8/qRXwB7dg9v +wZRVSX4+Dzuuvyt/p3p8OX5nhv2UrqXSeZx5gcWrof+td7X8lGj4j/kvFI6lotqL ++DTf4ndH8OyVjVL3Kzdc6e1+F/odgjurPW20GiNasLFpRz7aNUTtoSMc1zHi7tmW +EB0HMrCvdTwUuDOHVcebaR0xOPVcPcJhLoJDDQPRCFC93RvWL8qf5XPXwxYu9+tk +Kx22lFNJqnQeYH6s0QqJowcGwchpM23JlAyQ4y8qCb8Rng4V2KvmonWO5iadM+/9 +sNFmf7APUzeCMP0LGO+YLKgf3aPe2lQZOF3nQXpQ7iSDW33C45QpiQIzBBABCgAd +FiEEUKMDCY6i3XvL7iraCeAfoDwMUE4FAmANHuMACgkQCeAfoDwMUE47RxAAsbz1 +94m0hNMFUkzXc947B9qozcQMQJRhKsouBaMMwR+F1RgLH0oSAhYESsl+o8ngsyTo +AKYAP5p/N/wMzSZY0/B1XoQkTJT7HCX6G1gBKr6C5US4wL4Y2xQtBBVipAONK21p +RiSVUcvtOVfdUTSd2NNBUcVq9NCnWjtawu+8Z8fwJYa74gy8u9QQi9QjNPcupz63 +PKzB8WG1NjEI6Jx1TkZbGLoyXDQ/J7lfnoqGQoqIXMJjQHiDuNV8gIaPo2isHx6H +VOXYm+kx3mG/3cpTlWS1yfehHPrRYg/CB6joHYUUu9oe8HI1C8GF/4VxRsW6bfaZ +6rByBoPiCIb39xTLyCASXrXZ3n5wJ2blSCN3GPRxOcrNKQRgfNiXEc2jZtVA6sKU +a5DvvHYvIBqD6M9E8hPd5EomOW7t8zNCaFCvqWOanMmJmCqSlgSavZqEMOyTvcOM +ARyjZBseIIQZxwcfiKfJyI17adP/0fRdB5ypUUGaLPcbdh7JWJHzEbplGPj9VHrX ++xBN9fk2l8iXwPxD85C7lvup4SX+HEav3ofIJSrL47yC0DDrmia/JS3U/omD4raL +yfLSoVu0Qf6G6Z1MSLV1sfaMLNWssuwKYx2wHEsjRoURlWuQVR78KuCE8x+GZQ++ +Qa75Wuf0h6myzktUkfvddz6oW5W2yfVbAkuFR+yJAjMEEAEKAB0WIQSt/bcJ/h6m +guWFWXHVgyEO9RRxpwUCYA0e6AAKCRDVgyEO9RRxp277EACTAyRqNIaZPaSMAdw/ +AcYNX+/0G5+3m2+baSEPjcJUYOdwqQeUFAFZ3Sf9H4cm4zfNafQ0AjWUm9NYpwt8 +YKhN78dOpFaNdER43SAjVGmJb7Vs/yEX4EQZ3j7uRtypwAm6tehdo8kiKtMr774H +DZHGUp7NYdbBnCwiQWHFcwcK1ZWdgIY4Nw61pK5/iDl0ZIOZDXPgZWutB3ULNwBg +2PHBLOJaSvzl9jhC7Zjgpus6dEiTU/Ij6dKX+U0X9Hh5c2O3FQ08UwBffTBjTZTm +ThXGN8RN3a3cuBlpP5rTArU412yV4/+GkDPP/hv9iAgRAhwXomskyoC1Wq7I/1O1 +Ipzac19walDjLDvIBEVZmzi7YODEMU0F/EobW6+aByp9/cBGlBBn2Ppy+RQRevHm +Lf6jpvHcmdSEMvIDXDDJfUHVYfcpVnZJ3LfPE1kfdOhOKpCju8ZF9OPHUokhjKRM +frKLWOD2rxNQmqrfhvVsh8NSBNNaL9NkwnwevGo4ap2PaKwA2gxzZrMSrH3au9jE +K9+pnE94hdhRRfKINNME5r2Uo5Rcs6OIiuDM1wCmrIT2f4n0imXJoTiA/jwHWFAK +27EPnxXWZkbjR/oiIm5vaKqB9NbZDVtTw/4H7+pQ3E47THD+KY718FVUuV3cnOtM +MdoRGDkrd8ZS/I6ze7pOnCJy4IkBMwQQAQoAHRYhBK0g4aqLQTZwpkJS2L0nbS5v +z6iFBQJgDR7wAAoJEL0nbS5vz6iFz58IAIJRgMKRz4cOUy5iIPvtswXMb9tFR37U +PyLGJR1CbclXwUxTe6brN+8kWGka/g40qoG0Wr6GgQheYBjmV1CvXwOvZv83/FkK +GkGUZDjNhbfSXlrBMUczUEk3d6w2h8XHOoHozmWgf7fJk06MIJAwEt4ENK3Bfm+k +CCrCJuma8WzccyBLyU2iMLS14w7GOxJVyV37L8XcwmhysNyCpF0TVLPlPeGrvHO3 +hsw+lJZiZeXKUrU2hnzoM29A71PmkLVUYLN1JzvASwWCVsMfIO5T/bUzSLBysuEU +msqRL+vJQvzNDJs9gVrAtCnfZRQFHRYVYHsqqayhsj3/mk7x9a8Q6ZCJAjMEEAEK +AB0WIQQpbJTb0CgCRb/Tkdd7UpZI7oVyZAUCYA0e+AAKCRB7UpZI7oVyZKXLD/96 +55HOR12CYECMhU33Y5fqs10tYTdyoJjjStp+t2oApyaswr+DQPs6UVFUJWgMy478 +ro2DqW9kYHZeX0BumiQ5zrCeyBQYU+RUUNH7MU0pzdGuYWiL9PXqHNacuzV8GrIs +r4NFB1SJ66nbaKRMdJJnnfvtnJyHPfJ2VloxizYLNYptKUVbcP0j5ahXPbhy6Cyy +qlsAK28/gSRhDOqdq4/mKcNrc656bsmOqoaOl5po1N0sGStYQCuFWKjawujG5ZvF +x4hbwJUSU5gOFrBZgm2cYjypIO/GQz6CYbhGt77qV7f7hzo9qwA6UeIqrECvr83W +Jtp4e+FnjVQ6AfSwLI8oOPRa6DvJDdU+EGYPaWLbXnmq1fMu1nNn9SfHtkR+uDlN +GiQJk9EZSz30msacuEXZlXiypA2zTQFYAvtBZmYR4qjBX0qHImqmukjZZFhJ0sxR +LXE66HgvdxMTbYCVCWJY6u21yXF0O0a+nEvx0v1doux1247jzGXwyQTKXZsUZhv1 +qLv8igtMaJkSLZz1E1U703PdsMhU9jH6RKlwkW3KI/2NHEsxw7nDuhS6ez0UIM6O +sur53HCnDcA7k9eUaa+Sm0yCBeccZ9zmUgG2K3cFKdpQlljyt4WJTsKDrK4AkAHR +FjAJ0wOvv4apnz5LYNobKc/oTbjJacbTczB2lwGe17kCDQRgDR08ARAA9XG4WjRg +7cOfk6ur3Tj0TsmoiZ5jDKQ+ObZqk2aeIk5WutraEFe0OkI46F4oEbIwLB8rChHX +uVq18EM1mDD99tM3xTUoSm6BCdQeNx0Hh6enLZK49LBSMqTn3Fd9PNLL/QBABYWc +wgrazwxOlTrLOpX+XcgvRuxK36CisNr5i7Ocuc7EIuUurF6YoSaaxDT9XZHpuSSV +AI//sH+GmeBVgIs6f+8MSGe5R4g3aiyYqykwMtgSVgKqxi6Bo5UD8HeXpEAIgtNT +2gOxLgvar6vwlbTFamv+vy4C2RXY+7paEjGnlwI4nJrIWh0c1z3qIvAkEzhN88/J +fVCtjtKFjPAhGf48LxnRnURGb9anyexrRTPkGcmxx7/sxGMe/M31lpHOVKUZduWV +83/9/7NpSWU0BRmWyzK9CzQC/97Vb8JJhZG3N7RmTZgiO1GKAWFAKgd6X6oo6O/V +n5zngHY3jfKkb+wlcVa76IIDv3dc3JIENkmghfvuzdrx2IIqK+NSrBzp7OeTtgJE +vR5yTysS6wdlihY5zJgIBJh+GAy7lA8gzB3MhZe/qPSvnmK3ZTb1RnM5y5ySMZsU +mZnUVqjqjgUbY7NdXRpPeYLzwzzsvT+vlQX4P7LjGaienI6EP+AO7v2Ei+zv1NMI +jkXPPNtPwp01B3M09nYihjDnM/dviPF8J1sAEQEAAYkCNgQYAQoAIBYhBPTOImMh +AlPWqfl5sExm6o1L7hvuBQJgDR08AhsMAAoJEExm6o1L7hvuW6gP/iTNEyA96lc6 +3WxvkrpqiyZN4vdDwWv9FoEuZohlOCwQZpQy8wZlbtmjYcKAz1mRF3uBqZRvgzu4 +7ggzny8lF0m93PnyroRO5O6I8lT95HWH5+7mcoYpbDY1XII+QbP+Xdxi2mkUXqkY +3TRcp9VzwWyQb/0sgGch7ZOnd7bK12Q8wd2YmkCq5dQ8BXxFbnom6VoRpHnu1AsU +6ZKYbK5ogKXUoBxYKRqX6vMxMjALd/yJFKZwrCWkOxj0ipXCgHOlqbqgi5wH/gRu +qGkMYJ6fAnVcEdyfK5IRrtMB/3ZHlIDFXyEIA+K0AxpqE098KwnemOjrSYZV8Ek5 +48tVsKlmqqgJ1QkacR54OLw9CjNm0bXX1iqMfR89NfdIWqfyq732vqKb7UDfcjOK +IV4VP4sS8rBNrlzGpnkCOejE6YqxqwUt9ggtk9Q3SjqTrPTDZ3hExjcigchwnG5m +rZzBKYo7vQxoK+Y6Kx+BZHo2tUloURtsgqW7mLrfbY68Vbm4O1Ev5mjWA4bmOTrD +ivZF0HKBAdHG0B8JolpbSmoPVB0V9UAQvbb/amMK1zo36/cDrSZ9fid3Pbwyuupg +058rgvZPvBknm6p+k1mGb9XBGJlJaOR9Q0cmKobZhVmnSuCkRBJdLixHRvzcfygi +ra/bqVWSpZTlHZ0xT9seCUSs1urxGw9Z +=3HCo +-----END PGP PUBLIC KEY BLOCK----- + + pub rsa4096/0xD583210EF51471A7 2020-04-08 [SC] Key fingerprint = ADFD B709 FE1E A682 E585 5971 D583 210E F514 71A7 uid [ full ] Sendmail Signing Key/2020 @@ -3715,4 +3891,3 @@ Nq5nZ04BGHdVToZvUf2ABdQnWx94uOCRJp2bLJiEepNtaL2OPqe2EQVF7ia2Y0PT q8WNeh9erYZriQ== =VuMX -----END PGP PUBLIC KEY BLOCK----- - diff --git a/contrib/sendmail/README b/contrib/sendmail/README index 5b11d025b866..50cbce25e169 100644 --- a/contrib/sendmail/README +++ b/contrib/sendmail/README @@ -371,11 +371,6 @@ for a response. As of 8.10.0, the default Timeout.ident is 5 seconds as many sites have adopted the practice of dropping IDENT queries. This has lead to delays processing mail. -No ident server is included with this distribution. It is available -from: - - ftp://ftp.lysator.liu.se/pub/ident/servers/ - http://sf.www.lysator.liu.se/~pen/pidentd/ +-------------------------+ | INTEROPERATION PROBLEMS | diff --git a/contrib/sendmail/RELEASE_NOTES b/contrib/sendmail/RELEASE_NOTES index 6a0f0c52f844..d8186f05e0f4 100644 --- a/contrib/sendmail/RELEASE_NOTES +++ b/contrib/sendmail/RELEASE_NOTES @@ -5,6 +5,121 @@ This listing shows the version of the sendmail binary, the version of the sendmail configuration files, the date of release, and a summary of the changes in that release. +8.17.1/8.17.1 2021/08/17 + Deprecation notice: due to compatibility problems with some + third party code, we plan to finally switch from K&R + to ANSI C. If you are using sendmail on a system + which does not have a compiler for ANSI C contact us + with details as soon as possible so we can determine + how to proceed. + Experimental support for SMTPUTF8 (EAI, see RFC 6530-6533) + is available when using the compile time option USE_EAI + (see also devtools/Site/site.config.m4.sample for other + required settings) and the cf option SMTPUTF8. + If a mail submission via the command line requires + the use of SMTPUTF8, e.g., because a header uses UTF-8 + encoding, but the addresses on the command line are all + ASCII, then the new option -U must be used, and + the cf option SMTPUTF8 must be set in submit.cf. + Please test and provide feedback. + Experimental support for SMTP MTA Strict Transport Security + (MTA-STS, see RFC 8461) is available when using + - the compile time option _FFR_MTA_STS (which requires + STARTTLS, MAP_REGEX, SOCKETMAP, and _FFR_TLS_ALTNAMES), + - FEATURE(sts), which implicitly sets the cf option + StrictTransportSecurity, + - postfix-mta-sts-resolver, see + https://github.com/Snawoot/postfix-mta-sts-resolver.git + New ruleset check_other which is called for all unknown SMTP + commands in the server and for commands which do not + have specific rulesets, e.g., NOOP and VERB. + New ruleset clt_features which can be used to select features + in the SMTP client per server. Currently only two + flags are available: D/M to disable DANE/MTA-STS, + respectively. + Avoid leaking session macros for an envelope between + delivery attempts to different servers. This problem + could have affected check_compat. + Avoid leaking actual SMTP replies between delivery attempts + to different servers which could cause bogus logging + of reply= entries. + Change default SMTP reply code for STARTTLS related problems + from 403 to 454 to better match the RFCs. + Fix a theoretical buffer overflow when encountering an + unknown/unsupported socket address family on an + operating system where sa_data is larger than 30 + (the standard is 14). Based on patch by Toomas Soome. + Several potential memory leaks and other similar problems + (mostly in error handling code) have been fixed. + Problems reported by Tomas Korbar of RedHat. + Previously the commands GET, POST, CONNECT, or USER terminate + a connection immediately only if sent as first command. + Now this is also done if any of these is sent directly + after STARTTLS or if the 'h' option is set via + srv_features. + CDB map locking has been changed so a sendmail process which + does have a CDB map open does not block an in-place + update of the map by makemap. The simple workaround + for that problem in earlier versions is to create + the map under a different name and then move it + into place. + On some systems the rejection of a RCPT by a milter could + silently fail. + CONFIG: New FEATURE(`check_other') to provide a default + check_other ruleset. + CONFIG: FEATURE(`tls_failures') is deprecated and will be + removed in future versions because it has a fundamental + problem: it is message oriented but STARTTLS is + session oriented. For example, having multiple + RCPTs in one envelope for different destinations, + with different temporary errors, does not work + properly, as the persistent macro applies to all + RCPTs and hence implicitly to all destinations (servers). + The option TLSFallbacktoClear should be used if needed. + MAIL.LOCAL: Enhance some error messages to simplify + troubleshooting. + Portability: + Add support for Darwin 19 & 20. + NOTE: File locking using fcntl() does not interoperate + with Berkeley DB 5.x (and probably later). Use + CDB, flock() (-DHASFLOCK), or an earlier Berkeley + DB version. Problem noted by Harald Hannelius. + New Files: + cf/feature/check_other.m4 + cf/feature/sts.m4 + devtools/OS/Darwin.19.x + devtools/OS/Darwin.20.x + include/sm/ixlen.h + libsm/ilenx.c + libsm/lowercase.c + libsm/strcaseeq.c + libsm/t-ixlen.c + libsm/t-ixlen.sh + libsm/t-streq.c + libsm/t-streq.sh + libsm/utf8_valid.c + libsm/uxtext_unquote.c + libsm/xleni.c + libsmutil/t-lockfile.c + libsmutil/t-lockfile-0.sh + libsmutil/t-maplock-0.sh + +8.16.2/8.16.2 202X/XX/XX + New compile time option NO_EOH_FIELDS to disable the special + meaning of the headers Message: and Text: to denote the + end of the message header. + CONTRIB: AuthRealm.p0 has been modified for 8.16.1 by Anne Bennett. + CONTRIB: Added cidrexpand -O option for suppressing duplicates from + a CIDR expansion that overlaps a later entry and -S option + for skipping comments exactly like makemap does. + Portability: + Add support for Darwin 19 (Mac OS X 10.15). + Use proper FreeBSD version define to allow for cross + compiling. Fix from Brooks Davis of the FreeBSD + project. + New Files: + devtools/OS/Darwin.19.x + 8.16.1/8.16.1 2020/07/05 SECURITY: If sendmail tried to reuse an SMTP session which had already been closed by the server, then the connection @@ -114,14 +229,22 @@ summary of the changes in that release. changes in sys/sem.h On Linux set MAXHOSTNAMELEN (the maximum length of a FQHN) to 256 if it is less than that value. - Added Files: + New Files: cf/feature/blocklist_recipients.m4 + cf/feature/check_cert_altnames.m4 cf/feature/tls_failures.m4 devtools/OS/Darwin.14.x devtools/OS/Darwin.15.x devtools/OS/Darwin.16.x + devtools/OS/Darwin.17.x + devtools/OS/Darwin.18.x + include/sm/notify.h + libsm/notify.c + libsm/t-notify.c libsmdb/smcdb.c sendmail/ratectrl.h + sendmail/tls.h + sendmail/tlsh.c 8.15.2/8.15.2 2015/07/03 If FEATURE(`nopercenthack') is used then some bogus input triggered @@ -335,7 +458,7 @@ summary of the changes in that release. On Linux use socklen_t as the type for the 3rd argument for getsockname/getpeername if the glibc version is at least 2.1. - Added Files: + New Files: devtools/OS/Darwin.12.x devtools/OS/Darwin.13.x @@ -416,7 +539,7 @@ summary of the changes in that release. Add support for Darwin 11.x (Mac OS X 10.7). Add support for SunOS 5.12 (aka Solaris 12). Patch from John Beck of Oracle. - Added Files: + New Files: devtools/OS/Darwin.11.x devtools/OS/SunOS.5.12 @@ -601,7 +724,7 @@ summary of the changes in that release. Chris Behrens of Concentric. Add support for SCO OpenServer 6, patch from Boyd Gerber. DEVTOOLS: Clarify that confSHAREDLIBDIR requires a trailing slash. - Added Files: + New Files: devtools/OS/Darwin.9.x devtools/OS/OSR.i386 @@ -1825,7 +1948,7 @@ summary of the changes in that release. Use strerror(3) on Linux. If this causes a problem on your Linux distribution, compile with -DHASSTRERROR=0 and tell sendmail.org about it. - Added Files: + New Files: devtools/OS/AIX.5.2 8.12.9/8.12.9 2003/03/29 @@ -3987,7 +4110,7 @@ summary of the changes in that release. have a From line. VACATION: Read all of the headers before deciding whether or not to respond instead of stopping after finding recipient. - Added Files: + New Files: cf/ostype/darwin.m4 contrib/cidrexpand contrib/link_hash.sh @@ -4004,7 +4127,7 @@ summary of the changes in that release. Purczynski of elzabsoft.pl. SECURITY: Add more vigilance around set*uid(), setgid(), setgroups(), initgroups(), and chroot() calls. - Added Files: + New Files: test/t_setuid.c 8.10.1/8.10.1 2000/04/06 @@ -4109,7 +4232,7 @@ summary of the changes in that release. VACATION: Fix -t option which is ignored but available for compatibility with Sun's version, based on patch from Volker Dobler of Infratest Burke. - Added Files: + New Files: devtools/M4/UNIX/smlib.m4 devtools/OS/OSF1.V5.0 Deleted Files: diff --git a/contrib/sendmail/cf/README b/contrib/sendmail/cf/README index 983aa2821a1a..cfabe5eefe45 100644 --- a/contrib/sendmail/cf/README +++ b/contrib/sendmail/cf/README @@ -1120,9 +1120,8 @@ local_procmail Use procmail or another delivery agent as the local mailer. setreuid() call, you may need to add -f $f to the procmail argument vector to pass the proper sender to procmail. - For example, this allows it to use the maildrop - (http://www.flounder.net/~mrsam/maildrop/) mailer instead - by specifying: + For example, this allows it to use the maildrop mailer + instead by specifying: FEATURE(`local_procmail', `/usr/local/bin/maildrop', `maildrop -d $u') @@ -1132,7 +1131,7 @@ local_procmail Use procmail or another delivery agent as the local mailer. FEATURE(`local_procmail', `/usr/local/bin/scanmails') WARNING: This feature sets LOCAL_MAILER_FLAGS unconditionally, - i.e., without respecting any definitions in an OSTYPE setting. + i.e., without respecting any definitions in an OSTYPE setting. bestmx_is_local Accept mail as though locally addressed for any host that lists us as the best possible MX record. This generates @@ -1267,6 +1266,12 @@ delay_checks The rulesets check_mail and check_relay will not be called section. Note: this feature is incompatible to the versions in 8.10 and 8.11. +check_other Enable a default check_other ruleset which terminates + an SMTP session when it encounters a command which matches + a regular expression given as argument. If no argument + is given, then the default (to match potential headers) is: + ^[[:print:]]+ *: + use_client_ptr If this feature is enabled then check_relay will override its first argument with $&{client_ptr}. This is useful for rejections based on the unverified hostname of client, @@ -1578,9 +1583,9 @@ require_rdns Reject mail from connecting SMTP clients without proper Entries such as Connect:1.2.3.4 OK - Connect:1.2 RELAY - will allowlist IP address 1.2.3.4, so that the rDNS - blocking does apply to that IP address + Connect:1.3 RELAY + will allowlist IP address 1.2.3.4 and IP net 1.3.* + so that the rDNS blocking does apply not to those IPs. Entries such as Connect:1.2.3.4 REJECT @@ -1603,6 +1608,14 @@ badmx Reject envelope sender addresses (MAIL) whose domain part has been compiled with the options MAP_REGEX and DNSMAP. +sts Experimental support for Strict Transport Security + (MTA-STS, see RFC 8461). It sets the option + StrictTransportSecurity and takes one optional + argument: the socket map specification to access + postfix-mta-sts-resolver (see feature/sts.m4 + for the default value). + For more information see doc/op/op.me. + +-------+ | HACKS | +-------+ @@ -2581,7 +2594,7 @@ top level domain TLD, 192.168.212.* network, and the IPv6 address 2002:c0a8:02c7::/48. Entries in the access map should be tagged according to their type. -Three tags are available: +These tags are applicable: Connect: connection information (${client_addr}, ${client_name}) From: envelope sender @@ -2818,7 +2831,7 @@ regex map: # check address against various regex checks R$* $: $>Parse0 $>3 $1 R$+ < @ bigisp.com. > $* $: $(allnumbers $1 $) - R@MATCH $#error $: 553 Header Error + R@MATCH $#error $: 553 Address Error These rules are called with the original arguments of the corresponding check_* ruleset. If the local ruleset returns $#OK, no further checking @@ -3081,8 +3094,8 @@ Darth+20Mail+20+28Cert+29/emailAddress=darth+2Bcert@endmail.org (line breaks have been inserted for readability). -The macros which are subject to this encoding are ${cert_subject}, -${cert_issuer}, ${cn_subject}, and ${cn_issuer}. +The macros which are subject to this encoding are ${cert_subject}, +${cert_issuer}, ${cn_subject}, and ${cn_issuer}. Examples: @@ -3223,13 +3236,13 @@ options: - CertFile, KeyFile: {Server,Client}{Cert,Key}File - Flags: see doc/op/op.me for details. -If FEATURE(`tls_session_features') is used, then default rulesets -are activated which look up entries in the access map with the tags -TLS_Srv_features and TLS_Clt_features, respectively. -For example, these entries: +If FEATURE(`tls_session_features') and FEATURE(`access_db') are +used, then default rulesets are activated which look up entries in +the access map with the tags TLS_Srv_features and TLS_Clt_features, +respectively. For example, these entries: - TLS_Srv_features:10.0.2.4 CipherList=MEDIUM+aRSA; - TLS_Clt_features:10.1.0.1 Options=SSL_OP_NO_TLSv1_2; CipherList=ALL:-EXPORT +TLS_Srv_features:10.0.2.4 CipherList=MEDIUM+aRSA; +TLS_Clt_features:10.1.0.1 Options=SSL_OP_NO_TLSv1_2; CipherList=ALL:-EXPORT specify a cipherlist with MEDIUM strength ciphers that use RSA certificates only for the client with the IP address 10.0.2.4, @@ -3240,21 +3253,23 @@ their own rulesets which must return the appropriate data. If the rulesets are not defined or do not return a value, the default TLS options are not modified. -About 2): the ruleset try_tls (srv_features) can be used together -with the access map. Entries for the access map must be tagged -with Try_TLS (Srv_Features) and refer to the hostname or IP address -of the connecting system. A default case can be specified by using -just the tag. For example, the following entries in the access map: +About 2): the rulesets try_tls, srv_features, and clt_features can +be used together with the access map. Entries for the access map +must be tagged with Try_TLS, Srv_Features, Clt_Features and refer +to the hostname or IP address of the connecting system. A default +case can be specified by using just the tag. For example, the +following entries in the access map: Try_TLS:broken.server NO Srv_Features:my.domain v Srv_Features: V + Clt_Features:broken.sts M will turn off STARTTLS when sending to broken.server (or any host -in that domain), and request a client certificate during the TLS -handshake only for hosts in my.domain. The valid entries on the RHS -for Srv_Features are listed in the Sendmail Installation and -Operations Guide. +in that domain), request a client certificate during the TLS handshake +only for hosts in my.domain, and disable MTA-STS for broken.sts. +The valid entries on the RHS for Srv_Features and Clt_Features are +listed in the Sendmail Installation and Operations Guide. Received: Header @@ -3377,11 +3392,11 @@ LOCAL_RULESETS respectively. For example: Smyruleset ... -Local additions for the rulesets srv_features, try_tls, tls_rcpt, -tls_client, and tls_server can be made using LOCAL_SRV_FEATURES, -LOCAL_TRY_TLS, LOCAL_TLS_RCPT, LOCAL_TLS_CLIENT, and LOCAL_TLS_SERVER, -respectively. For example, to add a local ruleset that decides -whether to try STARTTLS in a sendmail client, use: +Local additions for the rulesets srv_features, clt_features, try_tls, +tls_rcpt, tls_client, and tls_server can be made using LOCAL_SRV_FEATURES, +LOCAL_CLT_FEATURES, LOCAL_TRY_TLS, LOCAL_TLS_RCPT, LOCAL_TLS_CLIENT, +and LOCAL_TLS_SERVER, respectively. For example, to add a local +ruleset that decides whether to try STARTTLS in a sendmail client, use: LOCAL_TRY_TLS R... @@ -4288,7 +4303,7 @@ confXF_BUFFER_SIZE XScriptFileBufferSize memory-buffered transcript (xf) file before a disk-based file is used. -confAUTH_MECHANISMS AuthMechanisms [GSSAPI KERBEROS_V4 DIGEST-MD5 +confAUTH_MECHANISMS AuthMechanisms [EXTERNAL GSSAPI KERBEROS_V4 DIGEST-MD5 CRAM-MD5] List of authentication mechanisms for AUTH (separated by spaces). The advertised list of diff --git a/contrib/sendmail/cf/cf/Makefile b/contrib/sendmail/cf/cf/Makefile index bf6f031b2f11..bdb9f9e9b426 100644 --- a/contrib/sendmail/cf/cf/Makefile +++ b/contrib/sendmail/cf/cf/Makefile @@ -100,23 +100,36 @@ M4FILES=\ ${CFDIR}/feature/access_db.m4 \ ${CFDIR}/feature/allmasquerade.m4 \ ${CFDIR}/feature/always_add_domain.m4 \ + ${CFDIR}/feature/authinfo.m4 \ + ${CFDIR}/feature/badmx.m4 \ ${CFDIR}/feature/bcc.m4 \ ${CFDIR}/feature/bestmx_is_local.m4 \ ${CFDIR}/feature/bitdomain.m4 \ + ${CFDIR}/feature/block_bad_helo.m4 \ ${CFDIR}/feature/blocklist_recipients.m4 \ + ${CFDIR}/feature/check_cert_altnames.m4 \ + ${CFDIR}/feature/check_other.m4 \ + ${CFDIR}/feature/compat_check.m4 \ ${CFDIR}/feature/conncontrol.m4 \ + ${CFDIR}/feature/delay_checks.m4 \ ${CFDIR}/feature/dnsbl.m4 \ ${CFDIR}/feature/domaintable.m4 \ + ${CFDIR}/feature/enhdnsbl.m4 \ ${CFDIR}/feature/generics_entire_domain.m4 \ ${CFDIR}/feature/genericstable.m4 \ + ${CFDIR}/feature/greet_pause.m4 \ ${CFDIR}/feature/ldap_routing.m4 \ ${CFDIR}/feature/limited_masquerade.m4 \ ${CFDIR}/feature/local_lmtp.m4 \ + ${CFDIR}/feature/local_no_masquerade.m4 \ ${CFDIR}/feature/local_procmail.m4 \ + ${CFDIR}/feature/lookupdotdomain.m4 \ ${CFDIR}/feature/loose_relay_check.m4 \ ${CFDIR}/feature/mailertable.m4 \ ${CFDIR}/feature/masquerade_entire_domain.m4 \ ${CFDIR}/feature/masquerade_envelope.m4 \ + ${CFDIR}/feature/msp.m4 \ + ${CFDIR}/feature/mtamark.m4 \ ${CFDIR}/feature/no_default_msa.m4 \ ${CFDIR}/feature/nocanonify.m4 \ ${CFDIR}/feature/nopercenthack.m4 \ @@ -124,17 +137,24 @@ M4FILES=\ ${CFDIR}/feature/nouucp.m4 \ ${CFDIR}/feature/nullclient.m4 \ ${CFDIR}/feature/prefixmod.m4 \ + ${CFDIR}/feature/preserve_local_plus_detail.m4 \ + ${CFDIR}/feature/preserve_luser_host.m4 \ ${CFDIR}/feature/promiscuous_relay.m4 \ - ${CFDIR}/feature/redirect.m4 \ + ${CFDIR}/feature/queuegroup.m4 \ ${CFDIR}/feature/ratecontrol.m4 \ + ${CFDIR}/feature/redirect.m4 \ ${CFDIR}/feature/relay_based_on_MX.m4 \ ${CFDIR}/feature/relay_entire_domain.m4 \ ${CFDIR}/feature/relay_hosts_only.m4 \ ${CFDIR}/feature/relay_local_from.m4 \ ${CFDIR}/feature/relay_mail_from.m4 \ + ${CFDIR}/feature/require_rdns.m4 \ ${CFDIR}/feature/smrsh.m4 \ ${CFDIR}/feature/stickyhost.m4 \ + ${CFDIR}/feature/sts.m4 \ + ${CFDIR}/feature/tls_failures.m4 \ ${CFDIR}/feature/tls_session_features.m4 \ + ${CFDIR}/feature/use_client_ptr.m4 \ ${CFDIR}/feature/use_ct_file.m4 \ ${CFDIR}/feature/use_cw_file.m4 \ ${CFDIR}/feature/uucpdomain.m4 \ @@ -147,6 +167,7 @@ M4FILES=\ ${CFDIR}/m4/proto.m4 \ ${CFDIR}/m4/version.m4 \ ${CFDIR}/mailer/cyrus.m4 \ + ${CFDIR}/mailer/cyrusv2.m4 \ ${CFDIR}/mailer/fax.m4 \ ${CFDIR}/mailer/local.m4 \ ${CFDIR}/mailer/mail11.m4 \ @@ -159,6 +180,7 @@ M4FILES=\ ${CFDIR}/mailer/uucp.m4 \ ${CFDIR}/ostype/aix3.m4 \ ${CFDIR}/ostype/aix4.m4 \ + ${CFDIR}/ostype/aix5.m4 \ ${CFDIR}/ostype/altos.m4 \ ${CFDIR}/ostype/amdahl-uts.m4 \ ${CFDIR}/ostype/a-ux.m4 \ @@ -167,6 +189,7 @@ M4FILES=\ ${CFDIR}/ostype/bsdi.m4 \ ${CFDIR}/ostype/bsdi1.0.m4 \ ${CFDIR}/ostype/bsdi2.0.m4 \ + ${CFDIR}/ostype/darwin.m4 \ ${CFDIR}/ostype/dgux.m4 \ ${CFDIR}/ostype/domainos.m4 \ ${CFDIR}/ostype/dragonfly.m4 \ @@ -200,10 +223,14 @@ M4FILES=\ ${CFDIR}/ostype/solaris2.ml.m4 \ ${CFDIR}/ostype/solaris2.pre5.m4 \ ${CFDIR}/ostype/solaris8.m4 \ + ${CFDIR}/ostype/solaris11.m4 \ ${CFDIR}/ostype/sunos3.5.m4 \ ${CFDIR}/ostype/sunos4.1.m4 \ ${CFDIR}/ostype/svr4.m4 \ ${CFDIR}/ostype/ultrix4.m4 \ + ${CFDIR}/ostype/unicos.m4 \ + ${CFDIR}/ostype/unicosmk.m4 \ + ${CFDIR}/ostype/unicosmp.m4 \ ${CFDIR}/ostype/unixware7.m4 \ ${CFDIR}/ostype/unknown.m4 \ ${CFDIR}/ostype/uxpds.m4 diff --git a/contrib/sendmail/cf/cf/submit.cf b/contrib/sendmail/cf/cf/submit.cf index 63d7cb720eb7..1faab23e9e39 100644 --- a/contrib/sendmail/cf/cf/submit.cf +++ b/contrib/sendmail/cf/cf/submit.cf @@ -16,8 +16,8 @@ ##### ##### SENDMAIL CONFIGURATION FILE ##### -##### built by ca@lab.smi.sendmail.com on Thu Jul 2 22:41:57 PDT 2020 -##### in /var/tmp/ca/sm8.git/sendmail/OpenSource/sendmail-8.16.1/cf/cf +##### built by ca@lab.smi.sendmail.com on Sun Aug 15 23:05:00 PDT 2021 +##### in /var/tmp/ca/sm8.head/sendmail/OpenSource/sendmail-8.17.1/cf/cf ##### using ../ as configuration include directory ##### ###################################################################### @@ -88,6 +88,7 @@ C{ResOk}OKR # Hosts for which relaying is permitted ($=R) FR-o /etc/mail/relay-domains + # arithmetic map Karith arith @@ -114,7 +115,7 @@ D{MTAHost}[127.0.0.1] # Configuration version number -DZ8.16.1/Submit +DZ8.17.1/Submit ############### @@ -362,7 +363,7 @@ O TimeZoneSpec= # maximum number of new connections per second #O ConnectionRateThrottle=0 -# Width of the window +# Width of the window #O ConnectionRateWindowSize=60s # work recipient factor @@ -536,7 +537,7 @@ O PidFile=/var/spool/clientmqueue/sm-client.pid #O ClientCertFile # Client private key #O ClientKeyFile -# File containing certificate revocation lists +# File containing certificate revocation lists #O CRLFile # Directory containing hashes pointing to certificate revocation status files #O CRLPath @@ -802,7 +803,7 @@ R< @ $=w . > : $* $@ $>Parse0 $>canonify $2 @here:... -> ... R$- < @ $=w . > $: $(dequote $1 $) < @ $2 . > dequote "foo"@here R< @ $+ > $#error $@ 5.1.3 $: "553 User address required" R$* $=O $* < @ $=w . > $@ $>Parse0 $>canonify $1 $2 $3 ...@here -> ... -R$- $: $(dequote $1 $) < @ *LOCAL* > dequote "foo" +R$- $: $(dequote $1 $) < @ *LOCAL* > dequote "foo" R< @ *LOCAL* > $#error $@ 5.1.3 $: "553 User address required" R$* $=O $* < @ *LOCAL* > $@ $>Parse0 $>canonify $1 $2 $3 ...@*LOCAL* -> ... @@ -896,7 +897,7 @@ R< $+ > $+ $@ $>MailerToTriple < $1 > $2 < @ $1 > SMailerToTriple=95 R< > $* $@ $1 strip off null relay -R< error : $-.$-.$- : $+ > $* $#error $@ $1.$2.$3 $: $4 +R< error : $-.$-.$- : $+ > $* $#error $@ $1.$2.$3 $: $4 R< error : $- : $+ > $* $#error $@ $(dequote $1 $) $: $2 R< error : $+ > $* $#error $: $1 R< local : $* > $* $>CanonLocal < $1 > $2 @@ -926,7 +927,7 @@ R< $+ @ $+ > $* < @ $* > $: < $1 > $3 < @ $4 > # handle local:user syntax R< $+ > $* <@ $* > $* $#local $@ $2@$3 $: $1 -R< $+ > $* $#local $@ $2 $: $1 +R< $+ > $* $#local $@ $2 $: $1 ################################################################### ### Ruleset 93 -- convert header names to masqueraded form ### @@ -1189,7 +1190,7 @@ R $#error $@ 5.7.1 $: "550 Relaying denied. IP name possibly forged " $ R $#error $@ 5.7.1 $: "550 Relaying denied. IP name lookup failed " $&{client_name} R$* $: <@> $&{client_name} # pass to name server to make hostname canonical -R<@> $* $=P $: $1 $2 +R<@> $* $=P $: $1 $2 R<@> $+ $: $[ $1 $] R$* . $1 strip trailing dots R $=w $@ RELAY @@ -1232,6 +1233,13 @@ SLocal_Relay_Auth Ssrv_features +###################################################################### +### clt_features: which features to use with a server? +### (done in client) +###################################################################### +Sclt_features + + ###################################################################### ### try_tls: try to use STARTTLS? ### (done in client) @@ -1239,6 +1247,7 @@ Ssrv_features Stry_tls + ###################################################################### ### tls_rcpt: is connection with server "good" enough? ### (done in client, per recipient) @@ -1278,8 +1287,12 @@ R$* $@ $>"TLS_connection" $1 ### Requirement: RHS from access map, may be ? for none. ###################################################################### STLS_connection -RSOFTWARE $#error $@ 4.7.0 $: "403 TLS handshake." -RDANE_FAIL $#error $@ 4.7.0 $: "403 DANE check failed." +RSOFTWARE $#error $@ 4.7.0 $: "454 TLS handshake failed." +RDANE_FAIL $#error $@ 4.7.0 $: "454 DANE check failed." +RPROTOCOL $#error $@ 4.7.0 $: "454 STARTTLS failed." +RCONFIG $#error $@ 4.7.0 $: "454 STARTTLS temporarily not possible." + + @@ -1478,6 +1491,7 @@ Mrelay, P=[IPC], F=mDFMuXa8k, S=EnvFromSMTP/HdrFromSMTP, R=MasqSMTP, E=\r\n, L= T=DNS/RFC822/SMTP, A=TCP $h + ### submit.mc ### # divert(-1) # # @@ -1505,3 +1519,6 @@ Mrelay, P=[IPC], F=mDFMuXa8k, S=EnvFromSMTP/HdrFromSMTP, R=MasqSMTP, E=\r\n, L= # dnl # dnl If you use IPv6 only, change [127.0.0.1] to [IPv6:0:0:0:0:0:0:0:1] # FEATURE(`msp', `[127.0.0.1]')dnl +# dnl enable this for SMTPUTF8 support +# dnl LOCAL_CONFIG +# dnl O SMTPUTF8=true diff --git a/contrib/sendmail/cf/cf/submit.mc b/contrib/sendmail/cf/cf/submit.mc index fc3cceec6b8f..99e58ac2629c 100644 --- a/contrib/sendmail/cf/cf/submit.mc +++ b/contrib/sendmail/cf/cf/submit.mc @@ -24,3 +24,6 @@ define(`confDONT_INIT_GROUPS', `True')dnl dnl dnl If you use IPv6 only, change [127.0.0.1] to [IPv6:0:0:0:0:0:0:0:1] FEATURE(`msp', `[127.0.0.1]')dnl +dnl enable this for SMTPUTF8 support +dnl LOCAL_CONFIG +dnl O SMTPUTF8=true diff --git a/contrib/sendmail/cf/feature/check_cert_altnames.m4 b/contrib/sendmail/cf/feature/check_cert_altnames.m4 index 9fae74ef0a3c..baa10697fd95 100644 --- a/contrib/sendmail/cf/feature/check_cert_altnames.m4 +++ b/contrib/sendmail/cf/feature/check_cert_altnames.m4 @@ -10,7 +10,7 @@ divert(-1) # divert(0)dnl -VERSIONID(`$Id: block_bad_helo.m4,v 1.2 2013-11-22 20:51:11 ca Exp $') +VERSIONID(`$Id: check_cert_altnames.m4 1.0 2019-01-01 01:01:01 ca Exp $') divert(-1) define(`_FFR_TLS_ALTNAMES', `1') divert(6)dnl diff --git a/contrib/sendmail/cf/feature/check_other.m4 b/contrib/sendmail/cf/feature/check_other.m4 new file mode 100644 index 000000000000..fa792243e137 --- /dev/null +++ b/contrib/sendmail/cf/feature/check_other.m4 @@ -0,0 +1,46 @@ +divert(-1) +# +# Copyright (c) 2021 Proofpoint, Inc. and its suppliers. +# All rights reserved. +# +# By using this file, you agree to the terms and conditions set +# forth in the LICENSE file which can be found at the top level of +# the sendmail distribution. +# +# +dnl bogus Id, just here to show up in the generated cf file +divert(0) +VERSIONID(`$Id: check_other.m4,v 1.0 2021-04-30 00:01:11 ca Exp $') +divert(-1) +dnl +dnl Options: +dnl first arg: +dnl empty: use default regex +dnl else: use as regex +dnl [not implemented: NOREGEX: do not use any regex] +dnl +dnl Possible enhancements: +dnl select which SMTP reply type(s) should be allowed to match? +dnl maybe add "exceptions": +dnl - via an "OK" regex? +dnl - access map lookup for clients to be "ok" (Connect:... {ok,relay}) +dnl more args? possible matches for rejections: +dnl does not seem to worth the effort: too inflexible. +dnl +dnl Note: sendmail removes whitespace before ':' ("tokenization") +ifelse( + defn(`_ARG_'), `', `define(`CHKORX', `^[[:print:]]+ *:')', + dnl defn(`_ARG_'), `NOREGEX', `define(`CHKORX', `')', + `define(`CHKORX', defn(`_ARG_'))') +LOCAL_CONFIG +ifelse(defn(`CHKORX'), `', `', `dnl +Kbadcmd regex -m -a defn(`CHKORX')') +LOCAL_RULESETS +Scheck_other +dnl accept anything that will be accepted by the MTA +R$* $| 2 $@ ok +ifelse(defn(`CHKORX'), `', `', `dnl +R$+ $| 5 $: $(badcmd $1 $) +R$* $#error $@ 4.7.0 $: 421 bad command') +dnl terminate on any bad command? +dnl R$* $| 5 $#error $@ 4.7.0 $: 421 bad command diff --git a/contrib/sendmail/cf/feature/delay_checks.m4 b/contrib/sendmail/cf/feature/delay_checks.m4 index 368057c991a8..630f94072922 100644 --- a/contrib/sendmail/cf/feature/delay_checks.m4 +++ b/contrib/sendmail/cf/feature/delay_checks.m4 @@ -21,5 +21,5 @@ ifelse(defn(`_ARG_'), `', `', ') ') -dnl be backward compatible by default +dnl be backward compatible by default ifelse(len(X`'_ARG2_), `1', `define(`_DELAY_COMPAT_8_10_', 1)', `') diff --git a/contrib/sendmail/cf/feature/dnsbl.m4 b/contrib/sendmail/cf/feature/dnsbl.m4 index dd8fd52583f9..4a6969687b0b 100644 --- a/contrib/sendmail/cf/feature/dnsbl.m4 +++ b/contrib/sendmail/cf/feature/dnsbl.m4 @@ -14,7 +14,7 @@ divert(0) ifdef(`_DNSBL_R_',`dnl',`dnl VERSIONID(`$Id: dnsbl.m4,v 8.34 2013-11-22 20:51:11 ca Exp $') define(`_DNSBL_R_',`') -ifelse(defn(`_ARG_'), `', +ifelse(defn(`_ARG_'), `', `errprint(`*** ERROR: missing argument for FEATURE(`dnsbl')')') LOCAL_CONFIG # map for DNS based blocklist lookups diff --git a/contrib/sendmail/cf/feature/enhdnsbl.m4 b/contrib/sendmail/cf/feature/enhdnsbl.m4 index f0ba5c50d482..a1f5f62a004b 100644 --- a/contrib/sendmail/cf/feature/enhdnsbl.m4 +++ b/contrib/sendmail/cf/feature/enhdnsbl.m4 @@ -9,7 +9,7 @@ divert(-1) # # -ifelse(defn(`_ARG_'), `', +ifelse(defn(`_ARG_'), `', `errprint(`*** ERROR: missing argument for FEATURE(`enhdnsbl')')') divert(0) ifdef(`_EDNSBL_R_',`dnl',`dnl diff --git a/contrib/sendmail/cf/feature/nopercenthack.m4 b/contrib/sendmail/cf/feature/nopercenthack.m4 index f6c9621c970d..7f5c7485fef0 100644 --- a/contrib/sendmail/cf/feature/nopercenthack.m4 +++ b/contrib/sendmail/cf/feature/nopercenthack.m4 @@ -16,7 +16,7 @@ divert(0) VERSIONID(`$Id: nopercenthack.m4,v 8.14 2013/01/31 15:07:00 ca Exp $') divert(-1) -ifelse(defn(`_ARG_'), `', +ifelse(defn(`_ARG_'), `', `errprint(`*** ERROR: missing argument for FEATURE(nopercenthack): use `reject' or `nospecial'. See cf/README. ')define(`_NO_PERCENTHACK_', `e')', diff --git a/contrib/sendmail/cf/feature/nouucp.m4 b/contrib/sendmail/cf/feature/nouucp.m4 index 5d1ee76f4dd7..98a9d69d2efb 100644 --- a/contrib/sendmail/cf/feature/nouucp.m4 +++ b/contrib/sendmail/cf/feature/nouucp.m4 @@ -16,7 +16,7 @@ divert(0) VERSIONID(`$Id: nouucp.m4,v 8.14 2013-11-22 20:51:11 ca Exp $') divert(-1) -ifelse(defn(`_ARG_'), `', +ifelse(defn(`_ARG_'), `', `errprint(`*** ERROR: missing argument for FEATURE(nouucp): use `reject' or `nospecial'. See cf/README. ')define(`_NO_UUCP_', `e')', diff --git a/contrib/sendmail/cf/feature/sts.m4 b/contrib/sendmail/cf/feature/sts.m4 new file mode 100644 index 000000000000..54ffb96a9b9c --- /dev/null +++ b/contrib/sendmail/cf/feature/sts.m4 @@ -0,0 +1,19 @@ +divert(-1) +# +# Copyright (c) 2020 Proofpoint, Inc. and its suppliers. +# All rights reserved. +# +# By using this file, you agree to the terms and conditions set +# forth in the LICENSE file which can be found at the top level of +# the sendmail distribution. +# + +divert(-1) +define(`_MTA_STS_', `') +define(`_NEED_MACRO_MAP_', `1') +ifelse(_ARG2_,`NO_SAN_TST',`',`define(`_STS_SAN', `1')') +LOCAL_CONFIG +O StrictTransportSecurity=true +ifelse(_ARG2_,`NO_SAN_TST',`',`O SetCertAltnames=true') +Ksts ifelse(defn(`_ARG_'), `', socket -d5 -T inet:5461@127.0.0.1, + defn(`_NARG_'), `', `_ARG_', `_NARG_') diff --git a/contrib/sendmail/cf/m4/cfhead.m4 b/contrib/sendmail/cf/m4/cfhead.m4 index 7e5fa99e0ef5..64981a94f867 100644 --- a/contrib/sendmail/cf/m4/cfhead.m4 +++ b/contrib/sendmail/cf/m4/cfhead.m4 @@ -171,6 +171,12 @@ ifdef(`_MAILER_DEFINED_',,`errprint(`*** WARNING: MAILER() should be before LOCA ')') divert(9) SLocal_srv_features') +define(`LOCAL_CLT_FEATURES', +`define(`_LOCAL_CLT_FEATURES_') +ifdef(`_MAILER_DEFINED_',,`errprint(`*** WARNING: MAILER() should be before LOCAL_CLT_FEATURES +')') +divert(9) +SLocal_clt_features') define(`LOCAL_TRY_TLS', `define(`_LOCAL_TRY_TLS_') ifdef(`_MAILER_DEFINED_',,`errprint(`*** WARNING: MAILER() should be before LOCAL_TRY_TLS @@ -213,7 +219,7 @@ define(`SITE', `ifelse(CONCAT($'2`, $3), SU, sinclude(_CF_DIR_`'siteconfig/$1.m4)') define(`EXPOSED_USER', `PUSHDIVERT(5)C{E}$1 POPDIVERT`'dnl`'') -define(`EXPOSED_USER_FILE', `PUSHDIVERT(5)F{E}$1 +define(`EXPOSED_USER_FILE', `PUSHDIVERT(5)F{E}$1 POPDIVERT`'dnl`'') define(`LOCAL_USER', `PUSHDIVERT(5)C{L}$1 POPDIVERT`'dnl`'') diff --git a/contrib/sendmail/cf/m4/proto.m4 b/contrib/sendmail/cf/m4/proto.m4 index 618dde00e24a..cfd71b3f9cad 100644 --- a/contrib/sendmail/cf/m4/proto.m4 +++ b/contrib/sendmail/cf/m4/proto.m4 @@ -183,16 +183,20 @@ ifdef(`confCR_FILE', `dnl FR`'confCR_FILE', `dnl') +ifdef(`_ACCESS_TABLE_', `dnl +define(`_FULL_TLS_CONNECTION_CHECK_', `1')', `dnl +ifdef(`_MTA_STS_', `define(`_FULL_TLS_CONNECTION_CHECK_', `1')')') define(`TLS_SRV_TAG', `"TLS_Srv"')dnl define(`TLS_CLT_TAG', `"TLS_Clt"')dnl define(`TLS_RCPT_TAG', `"TLS_Rcpt"')dnl define(`TLS_TRY_TAG', `"Try_TLS"')dnl define(`SRV_FEAT_TAG', `"Srv_Features"')dnl +define(`CLT_FEAT_TAG', `"Clt_Features"')dnl dnl this may be useful in other contexts too ifdef(`_ARITH_MAP_', `', `# arithmetic map define(`_ARITH_MAP_', `1')dnl Karith arith') -ifdef(`_ACCESS_TABLE_', `dnl +ifdef(`_FULL_TLS_CONNECTION_CHECK_', `dnl ifdef(`_MACRO_MAP_', `', `# macro storage map define(`_MACRO_MAP_', `1')dnl Kmacro macro') @@ -206,6 +210,13 @@ KCERTIssuer regex _CERT_REGEX_ISSUER_', `dnl') ifdef(`_CERT_REGEX_SUBJECT_', `dnl # extract relevant part from cert subject KCERTSubject regex _CERT_REGEX_SUBJECT_', `dnl') +ifdef(`_MTA_STS_', `dnl +Kstsxsni regex -a: -s3 (.*)(servername=)(.*) +Kstsxsni2 regex -a: -s2 (.*)(servername=.*) +Kstsxmatch regex -a: -s2 (match=)(.*) +# flag d: turn off DANE +Kstsxnodaneflag regex -a@ -s3 (.*)(flags=)([^;]*d)(.*) +', `dnl') ifdef(`LOCAL_RELAY', `dnl # who I send unqualified names to if `FEATURE(stickyhost)' is used @@ -502,7 +513,7 @@ _OPTION(MaxDaemonChildren, `confMAX_DAEMON_CHILDREN', `0') # maximum number of new connections per second _OPTION(ConnectionRateThrottle, `confCONNECTION_RATE_THROTTLE', `0') -# Width of the window +# Width of the window _OPTION(ConnectionRateWindowSize, `confCONNECTION_RATE_WINDOW_SIZE', `60s') # work recipient factor @@ -688,7 +699,7 @@ _OPTION(ServerKeyFile, `confSERVER_KEY', `') _OPTION(ClientCertFile, `confCLIENT_CERT', `') # Client private key _OPTION(ClientKeyFile, `confCLIENT_KEY', `') -# File containing certificate revocation lists +# File containing certificate revocation lists _OPTION(CRLFile, `confCRL', `') # Directory containing hashes pointing to certificate revocation status files _OPTION(CRLPath, `confCRL_PATH', `') @@ -868,7 +879,7 @@ R$* < @@ $+ > $* $@ $1 < @ $2 > $3 canon IP addr ifdef(`_DOMAIN_TABLE_', `dnl # look up domains in the domain table -R$* < @ $+ > $* $: $1 < @ $(domaintable $2 $) > $3', `dnl') +R$* < @ $+ > $* $: $1 < @ $(domaintable $2 $) > $3', `dnl') undivert(2)dnl LOCAL_RULE_3 @@ -953,14 +964,14 @@ ifdef(`_MASQUERADE_ENTIRE_DOMAIN_', ifdef(`_VIRTUSER_TABLE_', `dnl dnl virtual hosts are also canonical ifdef(`_VIRTUSER_ENTIRE_DOMAIN_', -`R$* < @ $* $={VirtHost} > $* $: $1 < @ $2 $3 . > $4', -`R$* < @ $={VirtHost} > $* $: $1 < @ $2 . > $3')', +`R$* < @ $* $={VirtHost} > $* $: $1 < @ $2 $3 . > $4', +`R$* < @ $={VirtHost} > $* $: $1 < @ $2 . > $3')', `dnl') ifdef(`_GENERICS_TABLE_', `dnl dnl hosts for genericstable are also canonical ifdef(`_GENERICS_ENTIRE_DOMAIN_', -`R$* < @ $* $=G > $* $: $1 < @ $2 $3 . > $4', -`R$* < @ $=G > $* $: $1 < @ $2 . > $3')', +`R$* < @ $* $=G > $* $: $1 < @ $2 $3 . > $4', +`R$* < @ $=G > $* $: $1 < @ $2 . > $3')', `dnl') dnl remove superfluous dots (maybe repeatedly) which may have been added dnl by one of the rules before @@ -1062,7 +1073,7 @@ R< @ $=w . > : $* $@ $>Parse0 $>canonify $2 @here:... -> ... R$- < @ $=w . > $: $(dequote $1 $) < @ $2 . > dequote "foo"@here R< @ $+ > $#error $@ 5.1.3 $: "_CODE553 User address required" R$* $=O $* < @ $=w . > $@ $>Parse0 $>canonify $1 $2 $3 ...@here -> ... -R$- $: $(dequote $1 $) < @ *LOCAL* > dequote "foo" +R$- $: $(dequote $1 $) < @ *LOCAL* > dequote "foo" R< @ *LOCAL* > $#error $@ 5.1.3 $: "_CODE553 User address required" R$* $=O $* < @ *LOCAL* > $@ $>Parse0 $>canonify $1 $2 $3 ...@*LOCAL* -> ... @@ -1105,17 +1116,17 @@ dnl are identical, i.e., if address A is mapped to A. dnl it does not deal with multi-level recursion # handle full domains in RHS of virtusertable R$+ < @ $+ > $: $(macro {RecipientAddress} $) $1 < @ $2 > -R$+ < @ $+ > $: $1 < @ $2 > $| $>final $1 < @ $2 > +R$+ < @ $+ > $: $1 < @ $2 > $| $>final $1 < @ $2 > R $+ $| $+ $: $1 $(macro {RecipientAddress} $@ $2 $) R $+ $| $* $: $1', `dnl') R$+ $: $1 Mark for lookup dnl input: local<@domain> ifdef(`_VIRTUSER_ENTIRE_DOMAIN_', -`R $+ < @ $* $={VirtHost} . > $: < $(virtuser $1 @ $2 $3 $@ $1 $: @ $) > $1 < @ $2 $3 . >', -`R $+ < @ $={VirtHost} . > $: < $(virtuser $1 @ $2 $@ $1 $: @ $) > $1 < @ $2 . >') +`R $+ < @ $* $={VirtHost} . > $: < $(virtuser $1 @ $2 $3 $@ $1 $: @ $) > $1 < @ $2 $3 . >', +`R $+ < @ $={VirtHost} . > $: < $(virtuser $1 @ $2 $@ $1 $: @ $) > $1 < @ $2 . >') dnl input: local<@domain> | local<@domain> -R $+ < @ $=w . > $: < $(virtuser $1 @ $2 $@ $1 $: @ $) > $1 < @ $2 . > +R $+ < @ $=w . > $: < $(virtuser $1 @ $2 $@ $1 $: @ $) > $1 < @ $2 . > dnl if <@> local<@domain>: no match but try lookup dnl user+detail: try user++@domain if detail not empty R<@> $+ + $+ < @ $* . > @@ -1140,14 +1151,14 @@ dnl no match R<@> $+ $: $1 dnl remove mark R $+ $: $1 -R< error : $-.$-.$- : $+ > $* $#error $@ $1.$2.$3 $: $4 -R< error : $- $+ > $* $#error $@ $(dequote $1 $) $: $2 +R< error : $-.$-.$- : $+ > $* $#error $@ $1.$2.$3 $: $4 +R< error : $- $+ > $* $#error $@ $(dequote $1 $) $: $2 ifdef(`_VIRTUSER_STOP_ONE_LEVEL_RECURSION_',`dnl # check virtuser input address against output address, if same, skip recursion R< $+ > $+ < @ $+ > $: < $1 > $2 < @ $3 > $| $1 # it is the same: stop now R< $+ > $+ < @ $+ > $| $&{RecipientAddress} $: $>ParseLocal $>Parse0 $>canonify $1 -R< $+ > $+ < @ $+ > $| $* $: < $1 > $2 < @ $3 > +R< $+ > $+ < @ $+ > $| $* $: < $1 > $2 < @ $3 > dnl', `dnl') dnl this is not a documented option dnl it performs no looping at all for virtusertable @@ -1176,7 +1187,7 @@ R$* <@ $+ > $* $: < $2 > $1 < @ $2 > $3 extract host name R< $+ . > $* $: < $1 > $2 strip trailing dot R< $+ > $* $: < $(mailertable $1 $) > $2 lookup dnl it is $~[ instead of $- to avoid matches on IPv6 addresses -R< $~[ : $* > $* $>MailerToTriple < $1 : $2 > $3 check -- resolved? +R< $~[ : $* > $* $>MailerToTriple < $1 : $2 > $3 check -- resolved? R< $+ > $* $: $>Mailertable <$1> $2 try domain', `dnl') undivert(4)dnl UUCP rules from `MAILER(uucp)' @@ -1285,7 +1296,7 @@ R< > $+ + $* $: < ? $L > <+ $2> $(user $1 $) look up user+ R< > $+ $: < ? $L > < > $(user $1 $) look up user R< ? $* > < $* > $+ <> $: < > $3 $2 found; strip $L R< ? $* > < $* > $+ $: < $1 > $3 $2 not found', ` -R< > $+ $: < $L > $(user $1 $) look up user +R< > $+ $: < $L > $(user $1 $) look up user R< $* > $+ <> $: < > $2 found; strip $L') ifdef(`_PRESERVE_LUSER_HOST_', `dnl R< $+ > $+ $: < $1 > $2 $&{Host}') @@ -1336,7 +1347,7 @@ R< $+ > $* $#_RELAY_ $@ $1 $: $2 not found, direct relay', `dnl') ################################################################### -### Ruleset 90 -- try domain part of mailertable entry ### +### Ruleset 90 -- try domain part of mailertable entry ### dnl input: LeftPartOfDomain FullAddress ################################################################### @@ -1346,7 +1357,7 @@ dnl %2 is not documented in cf/README R$* <$- . $+ > $* $: $1$2 < $(mailertable .$3 $@ $1$2 $@ $2 $) > $4 dnl it is $~[ instead of $- to avoid matches on IPv6 addresses R$* <$~[ : $* > $* $>MailerToTriple < $2 : $3 > $4 check -- resolved? -R$* < . $+ > $* $@ $>Mailertable $1 . <$2> $3 no -- strip & try again +R$* < . $+ > $* $@ $>Mailertable $1 . <$2> $3 no -- strip & try again dnl is $2 always empty? R$* < $* > $* $: < $(mailertable . $@ $1$2 $) > $3 try "." R< $~[ : $* > $* $>MailerToTriple < $1 : $2 > $3 "." found? @@ -1369,7 +1380,7 @@ dnl address -> relay host address SMailerToTriple=95 R< > $* $@ $1 strip off null relay -R< error : $-.$-.$- : $+ > $* $#error $@ $1.$2.$3 $: $4 +R< error : $-.$-.$- : $+ > $* $#error $@ $1.$2.$3 $: $4 R< error : $- : $+ > $* $#error $@ $(dequote $1 $) $: $2 R< error : $+ > $* $#error $: $1 R< local : $* > $* $>CanonLocal < $1 > $2 @@ -1408,7 +1419,7 @@ R< $+ @ $+ > $* < @ $* > $: < $1 > $3 < @ $4 > # handle local:user syntax R< $+ > $* <@ $* > $* $#_LOCAL_ $@ $2@$3 $: $1 -R< $+ > $* $#_LOCAL_ $@ $2 $: $1 +R< $+ > $* $#_LOCAL_ $@ $2 $: $1 ################################################################### ### Ruleset 93 -- convert header names to masqueraded form ### @@ -1440,9 +1451,9 @@ dnl no match, try @domain for exceptions R< > $+ < @ $+ . > $: < $(generics @$2 $@ $1 $: $) > $1 < @ $2 . > dnl workspace: ... or user <@domain> dnl no match, try local part -R< > $+ < @ $+ > $: < $(generics $1 $: $) > $1 < @ $2 > -R< > $+ + $* < @ $+ > $: < $(generics $1+* $@ $2 $: $) > $1 + $2 < @ $3 > -R< > $+ + $* < @ $+ > $: < $(generics $1 $: $) > $1 + $2 < @ $3 > +R< > $+ < @ $+ > $: < $(generics $1 $: $) > $1 < @ $2 > +R< > $+ + $* < @ $+ > $: < $(generics $1+* $@ $2 $: $) > $1 + $2 < @ $3 > +R< > $+ + $* < @ $+ > $: < $(generics $1 $: $) > $1 + $2 < @ $3 > R< $* @ $* > $* < $* > $@ $>canonify $1 @ $2 found qualified R< $+ > $* < $* > $: $>canonify $1 @ *LOCAL* found unqualified R< > $* $: $1 not found', @@ -1594,7 +1605,7 @@ dnl must not be empty ### + does lookup with and without tag ### <$4> -- passthru (additional data passed unchanged through) dnl returns: -dnl +dnl ###################################################################### SD @@ -2183,10 +2194,10 @@ R$* $| $* $: $1 dnl workspace: localpart<@domain> | localpart ifelse(defn(`_NO_UUCP_'), `r', `R$* ! $* < @ $* > $: $2 < @ BANG_PATH > -R$* ! $* $: $2 < @ BANG_PATH >', `dnl') +R$* ! $* $: $2 < @ BANG_PATH >', `dnl') ifelse(defn(`_NO_PERCENTHACK_'), `r', `R$* % $* < @ $* > $: $1 < @ PERCENT_HACK > -R$* % $* $: $1 < @ PERCENT_HACK >', `dnl') +R$* % $* $: $1 < @ PERCENT_HACK >', `dnl') # anything terminating locally is ok ifdef(`_RELAY_ENTIRE_DOMAIN_', `dnl R$+ < @ $* $=m > $@ RELAY', `dnl') @@ -2273,13 +2284,13 @@ RIPv6:::1 $@ RELAY originated locally R$=R $* $@ RELAY relayable IP address ifdef(`_ACCESS_TABLE_', `dnl R$* $: $>A <$1> <+ Connect> <$1> -R $* $@ RELAY relayable IP address +R $* $@ RELAY relayable IP address ifdef(`_FFR_REJECT_IP_IN_CHECK_RCPT_',`dnl dnl this will cause rejections in cases like: dnl Connect:My.Host.Domain RELAY dnl Connect:My.Net REJECT dnl since in check_relay client_name is checked before client_addr -R $* $@ REJECT rejected IP address') +R $* $@ REJECT rejected IP address') ifdef(`_ATMPF_', `R<_ATMPF_> $* $#TEMP $@ 4.3.0 $: _TMPFMSG_(`YOK1')', `dnl') R<$*> <$*> $: $2', `dnl') R$* $: [ $1 ] put brackets around it... @@ -2326,7 +2337,7 @@ dnl nevertheless, removing the rule doesn't hurt. dnl R<@> $@ RELAY dnl workspace: <@> ${client_name} (not empty) # pass to name server to make hostname canonical -R<@> $* $=P $: $1 $2 +R<@> $* $=P $: $1 $2 R<@> $+ $: $[ $1 $] dnl workspace: ${client_name} (canonified) R$* . $1 strip trailing dots @@ -2471,7 +2482,7 @@ dnl must not be empty ### + does lookup with and without tag ### <$4> -- passthru (additional data passed unchanged through) dnl returns: -dnl +dnl ###################################################################### SF @@ -2520,7 +2531,7 @@ dnl must not be empty ### + does lookup with and without tag ### <$4> -- passthru (additional data passed unchanged through) dnl returns: -dnl +dnl ###################################################################### SE @@ -2554,7 +2565,7 @@ dnl must not be empty ### + does lookup with and without tag ### <$4> -- passthru (additional data passed unchanged through) dnl returns: -dnl +dnl ###################################################################### SU @@ -2685,6 +2696,24 @@ ifdef(`_ATMPF_', `dnl tempfail? R<$* _ATMPF_>$* $#temp', `dnl') R<$+>$* $# $1') +###################################################################### +### clt_features: which features to use with a server? +### (done in client) +###################################################################### +Sclt_features +ifdef(`_LOCAL_CLT_FEATURES_', `dnl +R$* $: $1 $| $>"Local_clt_features" $1 +R$* $| $#$* $#$2 +R$* $| $* $: $1', `dnl') +ifdef(`_ACCESS_TABLE_', `dnl +R$* $: $>D <$&{client_name}> <> +R$* $: $>A <$&{client_addr}> <> +R$* $: <$(access CLT_FEAT_TAG`'_TAG_DELIM_ $: ? $)> +R$* $@ OK +ifdef(`_ATMPF_', `dnl tempfail? +R<$* _ATMPF_>$* $#temp', `dnl') +R<$+>$* $# $1') + ###################################################################### ### try_tls: try to use STARTTLS? ### (done in client) @@ -2703,6 +2732,76 @@ ifdef(`_ATMPF_', `dnl tempfail? R<$* _ATMPF_>$* $#error $@ 4.3.0 $: _TMPFMSG_(`TT')', `dnl') R$* $#error $@ 5.7.1 $: "550 do not try TLS with " $&{server_name} " ["$&{server_addr}"]"') +ifdef(`_MTA_STS_', `dnl +STLS_NameInList +R$* :$&{TLS_Name}: $* $@ ok +R$* $@ $1 + +dnl check SAN for STS +SSTS_SAN +ifdef(`_STS_SAN', `dnl +R$* $: $&{server_name} +dnl exact match +R$={cert_altnames} $@ ok +# strip only one level (no recursion!) +R$-.$+ $: $2 +dnl wildcard: *. or just .? +R *.$={cert_altnames} $@ ok +dnl R .$={cert_altnames} $@ ok +dnl always temporary error? make it an option (of the feature)? +R$* $#error $@ 4.7.0 $: 450 $&{server_name} not listed in SANs', `dnl') + +dnl input: ${verify} +dnl output: $# error ... (from TLS_connection) +dnl everything else: ok +SSTS_secure +R$* $: $&{rcpt_addr} $| $1 +# no {rcpt_addr}, no STS check +R $| $* $@ ok +dnl canonify to extract domain part? +R$*@$+ $| $* $2 $| $3 +R$+. $| $* $1 $| $2 +R$+ $| $* $: $(sts $1 $: none $) $| $2 +R$* $| $* $#error $@ 4.7.0 $: 450 STS lookup temp fail +dnl check whether connection is "secure" +dnl always temporary error? make it an option (of the feature)? +R$* secure $* $| $* $@ $>"TLS_connection" $3 $| +R$* $| $* $: $2 + +dnl check STS policy: secure and match? if so, check list +SSTS_Check +R$* $: $&{rcpt_addr} $| $1 +# no {rcpt_addr}, no STS check +R $| $* $@ ok +# use the original argument for the test, not {rcpt_addr} +R$* $| $* $: $2 $| $2 +dnl canonify to extract domain part? +R$*@$+ $| $* $2 $| $3 +R$+. $| $* $1 $| $2 +R$* $| $* $: $(sts $1 $: none $) $| mark +R$* $| $* $#error $@ 4.7.0 $: 450 STS lookup temp fail +dnl STS check only for "secure" +dnl do this only if {sts_sni} is set? +dnl workspace: result of sts lookup $| mark +R$* secure $* $| mark $: $2 $| trmatch +dnl not "secure": no check +R$* $| mark $@ ok +dnl remove servername=hostname, keep match= +R$* servername=hostname $| trmatch $: $1 $| trmatch +dnl extra list of matches, i.e., remove match= +R$+ $| trmatch $: : $(stsxmatch $1 $: : $) +dnl no match= data +R$* $| trmatch $@ $>STS_SAN +dnl Remove trailing dots from each entry in the list; +dnl those should not be there, but better safe than sorry. +R$*:$+.:$* $1:$2:$3 +R:$+: $: $(macro {TLS_Name} $@ $&{server_name} $) $>TLS_NameInList :$1: +R$* ok $@ $>STS_SAN +R$* $: $1 $| $&{server_name} +R:$* $| $-.$+ $: $(macro {TLS_Name} $@ .$3 $) $>TLS_NameInList :$1 +R$* ok $@ $>STS_SAN +R:$*: $#error $@ 4.7.0 $: 450 $&{server_name} not found in " "$1', `dnl') + ###################################################################### ### tls_rcpt: is connection with server "good" enough? ### (done in client, per recipient) @@ -2716,6 +2815,10 @@ ifdef(`_LOCAL_TLS_RCPT_', `dnl R$* $: $1 $| $>"Local_tls_rcpt" $1 R$* $| $#$* $#$2 R$* $| $* $: $1', `dnl') +ifdef(`_MTA_STS_', `dnl +R$* $: $1 $| $>"STS_Check" $1 +R$* $| $#$* $#$2 +R$* $| $* $: $1', `dnl') ifdef(`_ACCESS_TABLE_', `dnl dnl store name of other side R$* $: $(macro {TLS_Name} $@ $&{server_name} $) $1 @@ -2783,6 +2886,10 @@ R$* $| $#$* $#$2 R$* $| $* $: $1', `dnl') ifdef(`_TLS_FAILURES_',`dnl R$* $: $(macro {saved_verify} $@ $1 $) $1') +ifdef(`_MTA_STS_', `dnl +R$* $: $1 $| $>"STS_secure" $1 +R$* $| $#$* $#$2 +R$* $| $* $: $1', `dnl') ifdef(`_ACCESS_TABLE_', `dnl dnl store name of other side R$* $: $(macro {TLS_Name} $@ $&{server_name} $) $1 @@ -2808,11 +2915,22 @@ dnl [(PERM|TEMP)+] (VERIFY[:bits]|ENCR:bits) [+extensions] dnl extensions: could be a list of further requirements dnl for now: CN:string {cn_subject} == string ###################################################################### +ifdef(`TLS_PERM_ERR', `dnl +define(`TLS_DSNCODE', `5.7.0')dnl +define(`TLS_ERRCODE', `554')',`dnl +define(`TLS_DSNCODE', `4.7.0')dnl +define(`TLS_ERRCODE', `454')')dnl +define(`SW_MSG', `TLS handshake failed.')dnl +define(`DANE_MSG', `DANE check failed.')dnl +define(`PROT_MSG', `STARTTLS failed.')dnl +define(`CNF_MSG', `STARTTLS temporarily not possible.')dnl STLS_connection -ifdef(`_ACCESS_TABLE_', `dnl', `dnl use default error +ifdef(`_FULL_TLS_CONNECTION_CHECK_', `dnl', `dnl use default error dnl deal with TLS handshake failures: abort -RSOFTWARE $#error $@ ifdef(`TLS_PERM_ERR', `5.7.0', `4.7.0') $: "ifdef(`TLS_PERM_ERR', `503', `403') TLS handshake." -RDANE_FAIL $#error $@ ifdef(`TLS_PERM_ERR', `5.7.0', `4.7.0') $: "ifdef(`TLS_PERM_ERR', `503', `403') DANE check failed." +RSOFTWARE $#error $@ TLS_DSNCODE $: "TLS_ERRCODE SW_MSG" +RDANE_FAIL $#error $@ TLS_DSNCODE $: "TLS_ERRCODE DANE_MSG" +RPROTOCOL $#error $@ TLS_DSNCODE $: "TLS_ERRCODE PROT_MSG" +RCONFIG $#error $@ TLS_DSNCODE $: "TLS_ERRCODE CNF_MSG" divert(-1)') dnl common ruleset for tls_{client|server} dnl input: ${verify} $| [<>] @@ -2824,23 +2942,21 @@ dnl permanent or temporary error? R$* $| $: $1 $| <503:5.7.0> <$2 $3> R$* $| $: $1 $| <403:4.7.0> <$2 $3> dnl default case depends on TLS_PERM_ERR -R$* $| <$={Tls} $*> $: $1 $| <$2 $3> +R$* $| <$={Tls} $*> $: $1 $| <$2 $3> dnl workspace: ${verify} $| [] +define(`TLS_ERRORS', `dnl +R`'$1 $| <$-:$+> $`'* $`'#error $`'@ $`'2 $: $`'1 " $2" +dnl no i.e. no requirements in the access map +dnl use default error +R`'$1 $| $`'* $`'#error $`'@ TLS_DSNCODE $: "TLS_ERRCODE $2"')dnl # deal with TLS handshake failures: abort -RSOFTWARE $| <$-:$+> $* $#error $@ $2 $: $1 " TLS handshake failed." -dnl no i.e. no requirements in the access map -dnl use default error -RSOFTWARE $| $* $#error $@ ifdef(`TLS_PERM_ERR', `5.7.0', `4.7.0') $: "ifdef(`TLS_PERM_ERR', `503', `403') TLS handshake failed." +TLS_ERRORS(SOFTWARE,SW_MSG) # deal with TLS protocol errors: abort -RPROTOCOL $| <$-:$+> $* $#error $@ $2 $: $1 " STARTTLS failed." -dnl no i.e. no requirements in the access map -dnl use default error -RPROTOCOL $| $* $#error $@ ifdef(`TLS_PERM_ERR', `5.7.0', `4.7.0') $: "ifdef(`TLS_PERM_ERR', `503', `403') STARTTLS failed." +TLS_ERRORS(PROTOCOL,PROT_MSG) # deal with DANE errors: abort -RDANE_FAIL $| <$-:$+> $* $#error $@ $2 $: $1 " DANE check failed." -dnl no i.e. no requirements in the access map -dnl use default error -RDANE_FAIL $| $* $#error $@ ifdef(`TLS_PERM_ERR', `5.7.0', `4.7.0') $: "ifdef(`TLS_PERM_ERR', `503', `403') DANE check failed." +TLS_ERRORS(DANE_FAIL,DANE_MSG) +# deal with CONFIG (tls_clt_features) errors: abort +TLS_ERRORS(CONFIG,CNF_MSG) R$* $| <$*> $: <$2> <> $1 dnl separate optional requirements R$* $| <$*> $: <$2> <$3> $1 @@ -2958,25 +3074,116 @@ R$-:$-:$- $: $2 dnl endif _ACCESS_TABLE_ divert(0) +dnl this must also be activated without _TLS_SESSION_FEATURES_ +ifdef(`_MTA_STS_', `dnl +dnl caller preserves workspace +SSet_SNI +R$* $: <$&{rcpt_addr}> +# no {rcpt_addr}, no STS check +R<> $@ "" +dnl canonify to extract domain part? +R<$*@$+> $2 +R$+. $1 +R$+ $: $(sts $1 $: none $) +R$* $#error $@ 4.7.0 $: 450 STS lookup temp fail +Rnone $@ "" +dnl get servername=sni and store it in {sts_sni} +dnl stsxsni extracts the value of servername= (sni) +dnl stsxsni2 extracts servername=sni so it can be returned to the caller +R$* secure $* $: $(stsxsni $2 $: : $) $| sts=secure; $(stsxsni2 $2 $: : $) +dnl store {server_addr} as sni if there was a match +dnl Note: this implies that only servername=hostname (literally!) +dnl is only ever returned. +R$+: $| $+ : $: $(macro {sts_sni} $@ $&{server_name} $) set $| $2 +R$* $| $* $@ $2 +R$* $@ "" +dnl', `dnl') + ifdef(`_TLS_SESSION_FEATURES_', `dnl +define(`_NEED_TLS_CLT_FEATURES') Stls_srv_features ifdef(`_ACCESS_TABLE_', `dnl R$* $| $* $: $>D <$1> <$2> -R <$*> $: $>A <$1> <$1> -R <$*> $@ "" -R<$+> <$*> $@ $1 +R <$*> $: $>A <$1> <$1> +R <$*> $@ "" +R<$+> <$*> $@ $1 ', `dnl -R$* $@ ""') +R$* $@ ""') +', `dnl +ifdef(`_MTA_STS_',`define(`_NEED_TLS_CLT_FEATURES')')dnl +')dnl + +ifdef(`_NEED_TLS_CLT_FEATURES', `dnl +ifdef(`_ACCESS_TABLE_', `dnl +Stls_clt_feat_acc +R$* $| $* $: $>D <$1> <$2> +R <$*> $: $>A <$1> <$1> +R <$*> $@ "" +R<$+> <$*> $@ $1') + +SDANE_disabled +dnl Note: most of this is handled in the binary. +dnl input: access map lookup for tls_clt_features +dnl output: +dnl <>: disabled +dnl : enabled +R$+ $: < $(stsxnodaneflag $1 $: NOFLAGS $) > +R<$* @> $@ <> +R$* $: < $&{sts_sni} > +R<> $@ <> +# check this too? +# R$* $: $&{client_flags} +# R$* DD $* $@ <> +R$* $@ + +SSTS_disabled +dnl input: ignored +dnl output: +dnl <>: disabled +dnl : enabled +R$* $: $&{client_flags} +R$* MM $* $@ <> +dnl +R$* $: $&{rcpt_addr} $| $1 +# no {rcpt_addr}, no STS check +R $| $* $@ <> +R$* $@ Stls_clt_features +dnl host $| ip +R$* $: $1 $| <> ifdef(`_ACCESS_TABLE_', `dnl -R$* $| $* $: $>D <$1> <$2> -R <$*> $: $>A <$1> <$1> -R <$*> $@ "" -R<$+> <$*> $@ $1 -', `dnl -R$* $@ ""') -') +R$* $| <> $: $1 $| $>"tls_clt_feat_acc" $1 +R$* $| $* $| $* $: $1 $| $2 $| <$3>', `dnl') +ifdef(`_MTA_STS_', `dnl +dnl host $| ip $| +R$* $| $* $| <$*> $: $1 $| $2 $| <$3> $| $>"STS_disabled" sts +dnl host $| ip $| $| STS_disabled result +dnl disable STS check? return access result +R$* $| $* $| <$*> $| <> $@ $3 +dnl host $| ip $| $| STS_disabled result +R$* $| $* $| <$*> $| $* $: $1 $| $2 $| <$3> $| $>"DANE_disabled" $3 +dnl DANE enabled: return access result; take care of empty return +R$* $| $* $| <$+> $| $@ $3 +R$* $| $* $| <> $| $@ "" +dnl host $| ip $| $| +R$* $| $* $| <$*> $| <$*> $: $1 $| $2 $| <$3> $| $>"Set_SNI" $1 +dnl host $| ip $| $| sni result +dnl return sni result if not empty but acc is +R$* $| $* $| <""> $| $+ $@ $3 +dnl return acc + sni result if not empty +R$* $| $* $| <$+;> $| $+ $@ $3 ; $4 +dnl return acc + sni result if not empty +R$* $| $* $| <$+> $| $+ $@ $3 ; $4 +dnl return sni result if not empty +R$* $| $* $| <> $| $+ $@ $3 +dnl remove sni result +R$* $| $* $| $* $| $* $: $1 $| $2 $| $3 +', `dnl') +dnl host $| ip $| +R$* $| $* $| <""> $@ "" +R$* $| $* $| <$+> $@ $3 +R$* $@ ""') ###################################################################### ### RelayTLS: allow relaying based on TLS authentication @@ -3046,7 +3253,7 @@ dnl', `dnl')') ifdef(`_RATE_CONTROL_',`dnl ###################################################################### -### RateControl: +### RateControl: ### Parameters: ignored ### return: $#error or OK ###################################################################### @@ -3068,7 +3275,7 @@ R<$+> $| TRUE $#error $@ 4.3.2 $: _RATE_CONTROL_REPLY Connection rate limit exce ifdef(`_CONN_CONTROL_',`dnl ###################################################################### -### ConnControl: +### ConnControl: ### Parameters: ignored ### return: $#error or OK ###################################################################### @@ -3108,3 +3315,10 @@ _MAIL_FILTERS_ ###################################################################### undivert(7)dnl MAILER_DEFINITIONS + +dnl Helper ruleset for -bt mode to invoke rulesets +dnl which take two arguments separated by $| +dnl For example: +dnl Start,check_relay host.name $| I.P.V.4 +dnl SStart +dnl R$* $$| $* $: $1 $| $2 diff --git a/contrib/sendmail/cf/m4/version.m4 b/contrib/sendmail/cf/m4/version.m4 index dadff627bb2e..3942ca1a0691 100644 --- a/contrib/sendmail/cf/m4/version.m4 +++ b/contrib/sendmail/cf/m4/version.m4 @@ -15,4 +15,4 @@ VERSIONID(`$Id: version.m4,v 8.237 2014-01-27 12:55:17 ca Exp $') # divert(0) # Configuration version number -DZ8.16.1`'ifdef(`confCF_VERSION', `/confCF_VERSION') +DZ8.17.1`'ifdef(`confCF_VERSION', `/confCF_VERSION') diff --git a/contrib/sendmail/cf/mailer/local.m4 b/contrib/sendmail/cf/mailer/local.m4 index 85d24f32a2a6..1a9e4a3a1309 100644 --- a/contrib/sendmail/cf/mailer/local.m4 +++ b/contrib/sendmail/cf/mailer/local.m4 @@ -79,7 +79,7 @@ R$* $: $>MasqHdr $1 do all-masquerading')', # SAddDomain ifdef(`_ALWAYS_ADD_DOMAIN_', `dnl -R$* < @ $* > $* $@ $1 < @ $2 > $3 already fully qualified +R$* < @ $* > $* $@ $1 < @ $2 > $3 already fully qualified ifelse(len(X`'_ALWAYS_ADD_DOMAIN_),`1',` R$+ $@ $1 < @ *LOCAL* > add local qualification', `R$+ $@ $1 < @ _ALWAYS_ADD_DOMAIN_ > add qualification')', diff --git a/contrib/sendmail/contrib/AuthRealm.p0 b/contrib/sendmail/contrib/AuthRealm.p0 index 1ba8f58057fc..d1ac553df01f 100644 --- a/contrib/sendmail/contrib/AuthRealm.p0 +++ b/contrib/sendmail/contrib/AuthRealm.p0 @@ -1,19 +1,18 @@ Patch from John Marshall (slightly modified). +Modified for 8.16.1 by Anne Bennett. -diff --git a/sendmail/srvrsmtp.c b/sendmail/srvrsmtp.c -index 7dba983..bf804ab 100644 ---- a/sendmail/srvrsmtp.c -+++ b/sendmail/srvrsmtp.c -@@ -84,7 +84,7 @@ static int reset_saslconn __P((sasl_conn_t **_conn, char *_hostname, - # define RESET_SASLCONN \ +--- a/sendmail/srvrsmtp.c 2020-09-28 17:51:12.497535563 -0400 ++++ b/sendmail/srvrsmtp.c 2020-09-28 18:21:30.482890337 -0400 +@@ -116,7 +116,7 @@ do \ { \ + RESET_AUTH_FAIL_LOG_USER; \ - result = reset_saslconn(&conn, AuthRealm, remoteip, \ + result = reset_saslconn(&conn, hostname, remoteip, \ localip, auth_id, &ext_ssf); \ if (result != SASL_OK) \ sasl_ok = false; \ -@@ -938,8 +938,6 @@ smtp(nullserver, d_flags, e) +@@ -1018,8 +1018,6 @@ e->e_features = features; hostname = macvalue('j', e); #if SASL @@ -22,7 +21,7 @@ index 7dba983..bf804ab 100644 sasl_ok = bitset(SRV_OFFER_AUTH, features); n_mechs = 0; authenticating = SASL_NOT_AUTH; -@@ -948,8 +946,8 @@ smtp(nullserver, d_flags, e) +@@ -1028,8 +1026,8 @@ if (sasl_ok) { # if SASL >= 20000 @@ -33,7 +32,7 @@ index 7dba983..bf804ab 100644 # elif SASL > 10505 /* use empty realm: only works in SASL > 1.5.5 */ result = sasl_server_new("smtp", AuthRealm, "", NULL, 0, &conn); -@@ -5392,7 +5390,7 @@ reset_saslconn(sasl_conn_t **conn, char *hostname, +@@ -5559,7 +5557,7 @@ sasl_dispose(conn); # if SASL >= 20000 diff --git a/contrib/sendmail/contrib/cidrexpand b/contrib/sendmail/contrib/cidrexpand index ee24ee865275..d43b548ada81 100755 --- a/contrib/sendmail/contrib/cidrexpand +++ b/contrib/sendmail/contrib/cidrexpand @@ -3,7 +3,7 @@ # usage: # cidrexpand < /etc/mail/access | makemap -r hash /etc/mail/access # -# v 0.4 +# v 1.1 # # 17 July 2000 Derek J. Balling (dredd@megacity.org) # @@ -73,16 +73,25 @@ # Report bugs to: # +our $VERSION = '1.1'; use strict; use Net::CIDR qw(cidr2octets cidrvalidate); use Getopt::Std; +$Getopt::Std::STANDARD_HELP_VERSION = 1; +sub VERSION_MESSAGE; +sub HELP_MESSAGE; sub print_expanded_v4network; sub print_expanded_v6network; our %opts; -getopts('ct:', \%opts); +getopts('cfhOSt:', \%opts); + +if ($opts{h}) { + HELP_MESSAGE(\*STDOUT); + exit 0; +} # Delimiter between the key and value my $space_re = exists $opts{t} ? $opts{t} : '\s+'; @@ -94,11 +103,13 @@ my $ipv4_re = qr"(?:\d+\.){3}\d+"; # Further checks are required for verifying that it's really one my $ipv6_re = qr"[0-9A-Fa-f:]{2,39}(?:\.\d+\.\d+\.\d+)?"; +my %pending; while (<>) { chomp; my ($prefix, $network, $len, $right); + next if /^#/ && $opts{S}; if ( (/\#/) && $opts{c} ) { # print "checking...\n"; @@ -129,12 +140,12 @@ while (<>) } if (($prefix, $network, $len, $right) = - m!^(|\S+:)(${ipv4_re})/(\d+)(${space_re}.*)$!) + m!^(|[^\s:]+:)(${ipv4_re})/(\d+)(${space_re}.*)$!) { print_expanded_v4network($network, $len, $prefix, $right); } elsif ((($prefix, $network, $len, $right) = - m!^((?:\S+:)?[Ii][Pp][Vv]6:)(${ipv6_re})(?:/(\d+))?(${space_re}.*)$!) && + m!^((?:[^\s:]+:)?[Ii][Pp][Vv]6:)(${ipv6_re})(?:/(\d+))?(${space_re}.*)$!) && (!defined($len) || $len <= 128) && defined(cidrvalidate($network))) { @@ -142,19 +153,31 @@ while (<>) } else { + if (%pending && m!^(.+?)${space_re}!) + { + delete $pending{$opts{f} ? $1 : lc($1)}; + } print "$_\n"; } } +print foreach values %pending; sub print_expanded_v4network { my ($network, $len, $prefix, $suffix) = @_; + my $fp = $opts{f} ? $prefix : lc($prefix); # cidr2octets() doesn't handle a prefix-length of zero, so do # that ourselves foreach my $nl ($len == 0 ? (0..255) : cidr2octets("$network/$len")) { - print "$prefix$nl$suffix\n"; + my $val = "$prefix$nl$suffix\n"; + if ($opts{O}) + { + $pending{"$fp$nl"} = $val; + next; + } + print $val; } } @@ -171,11 +194,53 @@ sub print_expanded_v6network } else { + my $fp = $opts{f} ? $prefix : lc($prefix); foreach my $nl (cidr2octets("$network/$len")) { # trim leading zeros from each group $nl =~ s/(^|:)0+(?=[^:])/$1/g; - print "$prefix$nl$suffix\n"; + my $val = "$prefix$nl$suffix\n"; + if ($opts{O}) + { + $pending{"$fp$nl"} = $val; + next; + } + print $val; } } } + +sub VERSION_MESSAGE +{ + my ($fh) = @_; + print $fh "cidrexpand - Version $VERSION\n"; +} + +sub HELP_MESSAGE +{ + my ($fh) = @_; + print $fh <<'EOF'; +Usage: cidrexpand [-cfhOS] [-t regexp] files... + +Expand CIDR format inside the keys of map entries for makemap. + + -c Truncate lines at the first unquoted '#' + + -f Treat keys as case-sensitive when doing override detection + for the -O option. By default overlap detection is + case-insensitive. + + -h Print this usage + + -O When a CIDR expansion would generate a partial conflict + with a later entry, suppress the overlap from the earlier + expansion + + -S Skip lines that start with '#' + + -t regexp + Use 'regexp' to match the delimiter between key and value, + defaulting to \s+ + +EOF +} diff --git a/contrib/sendmail/contrib/doublebounce.pl b/contrib/sendmail/contrib/doublebounce.pl old mode 100644 new mode 100755 diff --git a/contrib/sendmail/contrib/link_hash.sh b/contrib/sendmail/contrib/link_hash.sh old mode 100644 new mode 100755 diff --git a/contrib/sendmail/contrib/re-mqueue.pl b/contrib/sendmail/contrib/re-mqueue.pl old mode 100644 new mode 100755 diff --git a/contrib/sendmail/doc/op/op.me b/contrib/sendmail/doc/op/op.me index 4d7ead30d4bb..b5b3cbac9e62 100644 --- a/contrib/sendmail/doc/op/op.me +++ b/contrib/sendmail/doc/op/op.me @@ -92,7 +92,7 @@ Version \\$2 .. .rm Ve .sp -For Sendmail Version 8.16 +For Sendmail Version 8.17 .)l .(f Sendmail is a trademark of Proofpoint, Inc. @@ -1613,6 +1613,20 @@ hosts files dns will not avoid DNS lookups even if a host can be found in /etc/hosts. .pp +Note: in contrast to the +.i sendmail +stub implementation +some operating systems do not preserve temporary failures. +For example, if DNS returns a TRY_AGAIN status for this setup +.(b +hosts files dns myhostname +.)b +but myhostname does not find the requested entry, +then a permanent error is returned to +.i sendmail +which obviously can cause problems, +e.g., an immediate bounce instead of a deferral. +.pp Service switches are not completely integrated. For example, despite the fact that the host entry listed in the above example specifies to look in NIS, @@ -4255,6 +4269,43 @@ ruleset is called after the .sm "SMTP DATA" command, its parameter is the number of recipients. It can accept or reject the command. +.sh 4 "check_other" +.pp +The +.i check_other +ruleset is invoked for all unknown SMTP commands +and for commands which do not have specific rulesets, +e.g., NOOP and VERB. +Internal checks, e.g., those explained in +"Measures against Denial of Service Attacks", +are performed first. +The ruleset is passed +.(b +entire-SMTP-command $| SMTP-reply-first-digit +.)b +where +.b $| +is a metacharacter separating the two parts. +For example, +.(b +VERB $| 2 +.)b +reflects receiving the "VERB" SMTP command and the +intent to return a "2XX" SMTP success reply. +Alternatively, +.(b +JUNK TYPE=I $| 5 +.)b +reflects receiving the unknown "JUNK TYPE=I" SMTP command +and the intent to return a "5XX" SMTP failure reply. +If the ruleset returns the SMTP reply code 421: +.(b +$#error $@ 4.7.0 $: 421 bad command +.)b +the session is terminated. +Note: it is a bad idea to return the original command in the error text +to the client as that might be abused for certain attacks. +The ruleset cannot override a rejection triggered by the built-in rules. .sh 4 "check_compat" .pp The @@ -4359,6 +4410,30 @@ ruleset is passed the user name parameter of the .sm "SMTP VRFY" command. It can accept or reject the command. +.sh 4 "clt_features" +.pp +The +.i clt_features +ruleset is called with the server's host name +when sendmail connects to it. +This ruleset should return +.b $# +followed by a list of options +(single characters delimited by white space). +If the return value starts with anything else it is silently ignored. +Generally upper case characters turn off a feature +while lower case characters turn it on. +Options `D'/`M' cause the client to not use DANE/MTA-STS, +respectively, +which is useful to interact with MTAs/MUs that have broken +DANE/MTA-STS setups by simply not using it. +Note: +The +.i d +option in +.i tls_clt_features +to turn off DANE does not work when the server does not +even offer STARTTLS. .sh 4 "trust_auth" .pp The @@ -4404,11 +4479,11 @@ mailer, the connection is aborted .pp The .i tls_rcpt -ruleset is called each time before a RCPT TO command is sent. +ruleset is called each time before a RCPT command is sent. The parameter is the current recipient. If the ruleset does resolve to the .q error -mailer, the RCPT TO command is suppressed +mailer, the RCPT command is suppressed (treated as non-deliverable with a permanent or temporary error). This ruleset allows to require encryption or verification of the recipient's MTA even if the mail is somehow redirected @@ -4447,6 +4522,10 @@ passive attack (e.g., PLAIN, LOGIN), unless a security layer is active. Option `l' requires SMTP AUTH for a connection. Options 'B', 'D', 'E', and 'X' suppress SMTP VERB, DSN, ETRN, and EXPN, respectively. +If a client sends one of the (HTTP) commands GET, POST, CONNECT, or USER +the connection is immediately terminated in the following cases: +if sent as first command, if sent as first command after STARTTLS, +or if the 'h' option is set. .(b .ta 9n A Do not offer AUTH @@ -4460,6 +4539,7 @@ D Do not offer DSN d Offer DSN (default) E Do not offer ETRN e Offer ETRN (default) +h Terminate session after HTTP commands L Do not require AUTH (default) l Require AUTH P Do not offer PIPELINING @@ -4495,10 +4575,14 @@ by simply not using it. .pp The .i tls_clt_features -ruleset is called when sendmail connects to another MTA +ruleset is called right before sendmail issues the +.i STARTTLS +command to another MTA and the .i tls_srv_features -ruleset is called when a client connects to +ruleset is called when a client sends the +.i STARTTLS +command to .i sendmail . The arguments for the rulesets are the host name and IP address of the other side separated by @@ -5134,6 +5218,13 @@ The current delivery mode sendmail is using. It is initially set to the value of the .b DeliveryMode option. +.ip ${dsn_envid} +The envelope id parameter (ENVID=) passed to sendmail as part of the envelope. +.ip ${dsn_notify} +Value of DSN NOTIFY= parameter +(never, success, failure, delay, or empty string). +.ip ${dsn_ret} +Value of DSN RET= parameter (hdrs, full, or empty string). .ip ${envid} The envelope id parameter (ENVID=) passed to sendmail as part of the envelope. .ip ${hdrlen} @@ -5243,8 +5334,10 @@ Defined in the SMTP server only after a RCPT command. .ip ${server_addr} The address of the server of the current outgoing SMTP connection. For LMTP delivery the macro is set to the name of the mailer. +(only if sendmail is compiled with STARTTLS or SASL.) .ip ${server_name} The name of the server of the current outgoing SMTP or LMTP connection. +(only if sendmail is compiled with STARTTLS or SASL.) .ip ${time} The output of the .i time (3) @@ -5262,6 +5355,7 @@ only defined after STARTTLS has been used (or attempted). Possible values are: .(b .ta 13n +TRUSTED verification via DANE succeeded. OK verification succeeded. NO no cert presented. NOT no cert requested. @@ -5950,7 +6044,7 @@ This flag is ignored if the flag is set. .ip p Use the route-addr style reverse-path in the SMTP -.q "MAIL FROM:" +.sm "SMTP MAIL" command rather than just the return address; although this is required in RFC 821 section 3.1, @@ -6018,7 +6112,7 @@ i.e., must succeed. If not, the mail is bounced. See also the -.b MailBoxDatabase +.b MailboxDatabase option. This is required to get .q \&.forward @@ -6253,7 +6347,7 @@ is used when converting a message to MIME; this is the character set used in the Content-Type: header. If this is not set, the -.b DefaultCharset +.b DefaultCharSet option is used, and if that is not set, the value .q unknown-8bit @@ -6599,7 +6693,7 @@ STARTTLS is already encrypting the communication, because the existing encryption strength is taken into account when choosing an algorithm for the security layer. For example, if STARTTLS is used and the symmetric cipher is 3DES, -then the the keylength (in bits) is 168. +then the keylength (in bits) is 168. Hence setting .b AuthMaxBits to 168 will disable any encryption in SASL. @@ -6617,7 +6711,7 @@ List of options for SMTP AUTH consisting of single characters with intervening white space or commas. .(b .ta 4n -A Use the AUTH= parameter for the MAIL FROM +A Use the AUTH= parameter for the MAIL command only when authentication succeeded. This can be used as a workaround for broken MTAs that do not implement RFC 2554 correctly. @@ -6729,7 +6823,7 @@ File containing the private key belonging to the client certificate .i sendmail runs as client). .ip ClientPortOptions=\fIoptions\fP -[O] +[no short name] Set client SMTP options. The options are .i key=value @@ -7137,7 +7231,7 @@ When the system load average exceeds will sleep for one second on most SMTP commands and before accepting connections. .ip DeliverByMin=\fItime\fP -[0] +[no short name] Set minimum time for Deliver By SMTP Service Extension (RFC 2852). If 0, no time is listed, if less than 0, the extension is not offered, if greater than 0, it is listed as minimum time @@ -7191,7 +7285,7 @@ If not set, is either "CC f" if the option .b \-G is used or "c u" otherwise. -Note that only the the "CC", "c", "f", and "u" flags are checked. +Note that only the "CC", "c", "f", and "u" flags are checked. .ip DontBlameSendmail=\fIoption,option,...\fP [no short name] In order to avoid possible cracking attempts @@ -7450,6 +7544,12 @@ Set the name to be used for HELO/EHLO (instead of $j). [H] Specify the help file for SMTP. If no file name is specified, "helpfile" is used. +If the help file does not exist (cannot be opened for reading) +.i sendmail +will print a note including its version in response to a +.b HELP +command. +To avoid providing this information to a client specify an empty file. .ip HoldExpensive [c] If an outgoing mailer is marked as being expensive, @@ -7716,8 +7816,8 @@ after a certain event occurred. .ta \w'envfrom'u+3n connect After session connection start helo After EHLO/HELO command -envfrom After MAIL From command -envrcpt After RCPT To command +envfrom After MAIL command +envrcpt After RCPT command data After DATA command. eoh After DATA command and header eom After DATA command and terminating ``.'' @@ -8105,7 +8205,7 @@ it is enabled by default for Linux. According to some information this flag is not needed anymore for kernel 2.4.16 and newer. .ip RrtImpliesDsn -[R] +[no short name] If this option is set, a .q Return-Receipt-To: header causes the request of a DSN, which is sent to @@ -8361,6 +8461,8 @@ option. The message printed when the SMTP server starts up. Defaults to .q "$j Sendmail $v ready at $b". +.ip SMTPUTF8 +Enable runtime support for SMTPUTF8. .ip SoftBounce If set, issue temporary errors (4xy) instead of permanent errors (5xy). This can be useful during testing of a new configuration to avoid @@ -9188,7 +9290,7 @@ flag is used. Without it, the key is discarded or if .b \-s if used, it is substituted by the substring matches, delimited by .b $| -or the string specified with the the +or the string specified with the .b \-d option. The options available for the map are @@ -9575,7 +9677,7 @@ number to override the default LDAP port. LDAP service port. .ip "\-H \fILDAPURI\fP" Use the specified LDAP URI instead of specifying the hostname and port -separately with the the +separately with the .b \-h and .b \-p @@ -11184,6 +11286,61 @@ error conditions as required by the RFCs. Moreover, TLSA RRs are not looked up for some features, e.g., .i FallBackSmartHost . +.sh 2 "EAI" +.pp +Experimental support for SMTPUTF8 (EAI, see RFC 6530-6533) +is available when +the compile time option +.b USE_EAI, +(see also +.i devtools/Site/site.config.m4.sample +for other settings that might be needed), +and the cf option +.i SMTPUTF8 +are used. +This allows the use of UTF-8 for envelope addresses +as well as the entire message. +DNS lookups are done using the A-label format (Punycode) +as required by the RFCs. +For all other interactions with external programs and maps, +the actual value are used, +i.e., no conversions between UTF-8 and ASCII encodings are made. +This applies to +.\" how to make a list? +.\" .(l +the keys in map lookups, which might require to specify both versions in a map; +the data exchanged with a milter, i.e., each milter must be "8 bit clean"; +mail delivery agents which must be able to handle 8 bit addresses. +.\" .)l +Some values must be ASCII as those are used before SMTPUTF8 support +can be requested, e.g., +the macros +.b $j +and +.b $m. +Please test and provide feedback. +.sh 2 "MTA-STS" +.pp +Experimental support for SMTP MTA Strict Transport Security +(MTA-STS, see RFC 8461) +is available when using +the compile time option _FFR_MTA_STS +(as well as some others, e.g., _FFR_TLS_ALTNAMES and obviously STARTTLS), +.\"(which requires in a default setting +.\"MAP_REGEX, SOCKETMAP, _FFR_TLS_ALTNAMES, and obviously STARTTLS), +FEATURE(sts) +(which implicitly sets the cf option StrictTransportSecurity), +and +postfix-mta-sts-resolver +(see https://github.com/Snawoot/postfix-mta-sts-resolver.git). +.pp +Note: this implementation uses a socket map to communicate with +postfix-mta-sts-resolver +and handles only the values returned by that program, +which might not fully implement MTA-STS. +.pp +If both DANE and MTA-STS are enabled and available for the receiving domain, +DANE is used because it offers a much higher level of security. .sh 1 "ACKNOWLEDGEMENTS" .pp I've worked on @@ -11517,6 +11674,19 @@ The line will be deleted before sending. Any addresses in the argument vector will be deleted from the send list. +.ip \-U +This option is required when sending mail using UTF-8; +it sets the +.q SMTPUTF8 +argument for +.q MAIL +command. +Only available if +.q EAI +support is enabled, +and the +.q SMTPUTF8 +option is set. .ip "\-V envid" The indicated .i envid @@ -11641,7 +11811,8 @@ file to and send it again. .pp The queue control file is structured as a series of lines -each beginning with a code letter. +each beginning with a code letter; +the file must end with a line containing only a single dot. The lines are as follows: .ip V The version number of the queue file format, @@ -11650,10 +11821,10 @@ used to allow new binaries to read queue files created by older versions. Defaults to version zero. Must be the first line of the file if present. -For 8.12 the version number is 6. +For 8.13 and later the version number is 8. .ip A The information given by the AUTH= parameter of the -.q "MAIL FROM:" +.sm "SMTP MAIL" command or $f@$j if sendmail has been called directly. .ip H @@ -11809,6 +11980,9 @@ H?x?Full-name: Eric Allman H??Message-id: <9207170931.AA22757@foo.bar.baz.de> H??To: sendmail@vangogh.CS.Berkeley.EDU H??Subject: this is an example message +.cc ' +. +'cc .)b This shows the person who sent the message, diff --git a/contrib/sendmail/editmap/editmap.8 b/contrib/sendmail/editmap/editmap.8 index db72102a2153..d6cb5a9a5a4f 100644 --- a/contrib/sendmail/editmap/editmap.8 +++ b/contrib/sendmail/editmap/editmap.8 @@ -30,27 +30,31 @@ directed to standard output. .PP Depending on how it is compiled, .B editmap -handles up to three different database formats, +handles different database formats, selected using the .I maptype -parameter. +parameter. They may be .TP dbm -DBM format maps. -This requires the -ndbm(3) +DBM format maps. +This requires the +ndbm(3) library. .TP btree -B-Tree format maps. -This requires the new Berkeley DB +B-Tree format maps. +This requires the new Berkeley DB library. .TP hash -Hash format maps. -This also requires the Berkeley DB +Hash format maps. +This also requires the Berkeley DB library. +.TP +cdb +CDB (Constant DataBase) format maps. +This requires the tinycdb library. .PP If the .I TrustedUser diff --git a/contrib/sendmail/editmap/editmap.c b/contrib/sendmail/editmap/editmap.c index c3454cb495ae..37532fc0225f 100644 --- a/contrib/sendmail/editmap/editmap.c +++ b/contrib/sendmail/editmap/editmap.c @@ -38,6 +38,7 @@ SM_UNUSED(static char id[]) = "@(#)$Id: editmap.c,v 1.26 2013-11-22 20:51:26 ca #endif #include #include +#include #include #include #include @@ -194,13 +195,13 @@ main(argc, argv) if (foldcase) { - char *p; + char *lower; - for (p = keyname; *p != '\0'; p++) - { - if (isascii(*p) && isupper(*p)) - *p = tolower(*p); - } + lower = makelower(keyname); + + /* if it is different then it is a static variable */ + if (keyname != lower) + keyname = lower; } diff --git a/contrib/sendmail/include/libmilter/mfapi.h b/contrib/sendmail/include/libmilter/mfapi.h index 2b012d86af2b..3bb4c1d2a1d2 100644 --- a/contrib/sendmail/include/libmilter/mfapi.h +++ b/contrib/sendmail/include/libmilter/mfapi.h @@ -476,7 +476,7 @@ LIBMILTER_API int smfi_insheader __P((SMFICTX *, int, char *, char *)); ** as a result of adding this header. ** ** SMFICTX *ctx; Opaque context structure -** int idx; index into the header list where the insertion should happen +** int idx; index into the header list where the insertion should happen ** char *headerh; Header field name ** char *headerv; Header field value */ diff --git a/contrib/sendmail/include/libsmdb/smdb.h b/contrib/sendmail/include/libsmdb/smdb.h index 427599ce206c..bf102695a77b 100644 --- a/contrib/sendmail/include/libsmdb/smdb.h +++ b/contrib/sendmail/include/libsmdb/smdb.h @@ -357,12 +357,12 @@ extern smdb_open_func smdb_open_database; # if NEWDB extern smdb_open_func smdb_db_open; # else -# define smdb_db_open NULL +# define smdb_db_open NULL # endif # if NDBM extern smdb_open_func smdb_ndbm_open; # else -# define smdb_ndbm_open NULL +# define smdb_ndbm_open NULL # endif extern int smdb_add_extension __P((char *, int, char *, char *)); extern int smdb_setup_file __P((char *, char *, int, long, @@ -380,6 +380,6 @@ extern int smdb_unlock_map __P((SMDB_DATABASE *)); # if CDB extern smdb_open_func smdb_cdb_open; # else -# define smdb_cdb_open NULL +# define smdb_cdb_open NULL # endif #endif /* ! _SMDB_H_ */ diff --git a/contrib/sendmail/include/sendmail/pathnames.h b/contrib/sendmail/include/sendmail/pathnames.h index 67be3a2d0184..2ac15cdd118b 100644 --- a/contrib/sendmail/include/sendmail/pathnames.h +++ b/contrib/sendmail/include/sendmail/pathnames.h @@ -33,7 +33,7 @@ # endif /* ! _PATH_SENDMAILPID */ # ifndef _PATH_SENDMAIL -# define _PATH_SENDMAIL "/usr/lib/sendmail" +# define _PATH_SENDMAIL "/usr/lib/sendmail" # endif # ifndef _PATH_MAILDIR diff --git a/contrib/sendmail/include/sm/bdb.h b/contrib/sendmail/include/sm/bdb.h index 832de252ca65..2c712d9664dc 100644 --- a/contrib/sendmail/include/sm/bdb.h +++ b/contrib/sendmail/include/sm/bdb.h @@ -37,9 +37,9 @@ # define DBTXN # if !HASFLOCK && defined(DB_FCNTL_LOCKING) # define SM_DB_FLAG_ADD(flag) (flag) |= DB_FCNTL_LOCKING -# else /* !HASFLOCK && defined(DB_FCNTL_LOCKING) */ +# else # define SM_DB_FLAG_ADD(flag) ((void) 0) -# endif /* !HASFLOCK && defined(DB_FCNTL_LOCKING) */ +# endif # endif /* (DB_VERSION_MAJOR == 4 && DB_VERSION_MINOR >= 1) || DB_VERSION_MAJOR >= 5 */ #endif /* NEWDB */ diff --git a/contrib/sendmail/include/sm/conf.h b/contrib/sendmail/include/sm/conf.h index 7bb031bb0bc0..be9f027cac33 100644 --- a/contrib/sendmail/include/sm/conf.h +++ b/contrib/sendmail/include/sm/conf.h @@ -997,6 +997,7 @@ extern unsigned int sleepX __P((unsigned int seconds)); # define USESYSCTL 1 /* use sysctl(3) for getting ncpus */ # include # include +# define HAVE_IFC_BUF_VOID 1 /* void *ifc_buf instead of caddr_t */ # endif # if defined(__DragonFly__) # define HASSETLOGIN 1 /* has setlogin(2) */ diff --git a/contrib/sendmail/include/sm/gen.h b/contrib/sendmail/include/sm/gen.h index a66bae54b46c..4f2066fa7447 100644 --- a/contrib/sendmail/include/sm/gen.h +++ b/contrib/sendmail/include/sm/gen.h @@ -77,4 +77,16 @@ typedef int SM_ATOMIC_INT_T; typedef unsigned int SM_ATOMIC_UINT_T; +#if _FFR_EAI && !defined(USE_EAI) +# define USE_EAI 1 +#endif + +#if USE_EAI && !defined(_FFR_LOGASIS) +# define _FFR_LOGASIS 1 +#endif + +#if USE_EAI || _FFR_EIGHT_BIT_ADDR_OK +# define _FFR_8BITENVADDR 1 +#endif + #endif /* SM_GEN_H */ diff --git a/contrib/sendmail/include/sm/ixlen.h b/contrib/sendmail/include/sm/ixlen.h new file mode 100644 index 000000000000..c3090ee07bbe --- /dev/null +++ b/contrib/sendmail/include/sm/ixlen.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2020 Proofpoint, Inc. and its suppliers. + * All rights reserved. + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the sendmail distribution. + */ + +#ifndef _SM_IXLEN_H +# define _SM_IXLEN_H 1 + +#define SM_IS_MQ(ch) (((ch) & 0377) == METAQUOTE) + +#if _FFR_8BITENVADDR +# define XLENDECL bool mq=false; \ + int xlen = 0; +# define XLENRESET mq=false, xlen = 0 +# define XLEN(c) do { \ + if (mq) { ++xlen; mq=false; } \ + else if (SM_IS_MQ(c)) mq=true; \ + else ++xlen; \ + } while (0) + +extern int ilenx __P((const char *)); +extern int xleni __P((const char *)); + +# if USE_EAI +extern bool asciistr __P((const char *)); +extern int uxtext_unquote __P((const char *, char *, int)); +extern char *sm_lowercase __P((const char *)); +extern bool utf8_valid __P((const char *, size_t)); +# endif + +#else /* _FFR_8BITENVADDR */ +# define XLENDECL int xlen = 0; +# define XLENRESET xlen = 0 +# define XLEN(c) ++xlen +# define ilenx(str) strlen(str) +# define xleni(str) strlen(str) +#endif /* _FFR_8BITENVADDR */ + +#endif /* ! _SM_IXLEN_H */ diff --git a/contrib/sendmail/include/sm/notify.h b/contrib/sendmail/include/sm/notify.h index e5c193e09b37..4256ea94e747 100644 --- a/contrib/sendmail/include/sm/notify.h +++ b/contrib/sendmail/include/sm/notify.h @@ -10,10 +10,13 @@ #ifndef SM_NOTIFY_H #define SM_NOTIFY_H +/* microseconds */ +#define SM_MICROS 1000000L + int sm_notify_init __P((int)); int sm_notify_start __P((bool, int)); int sm_notify_stop __P((bool, int)); -int sm_notify_rcv __P((char *, size_t, int)); +int sm_notify_rcv __P((char *, size_t, long)); int sm_notify_snd __P((char *, size_t)); #endif /* ! SM_MSG_H */ diff --git a/contrib/sendmail/include/sm/os/sm_os_freebsd.h b/contrib/sendmail/include/sm/os/sm_os_freebsd.h index 9c355c9be8e0..fb00760a4301 100644 --- a/contrib/sendmail/include/sm/os/sm_os_freebsd.h +++ b/contrib/sendmail/include/sm/os/sm_os_freebsd.h @@ -26,6 +26,11 @@ # define SM_CONF_STRL 1 # endif # endif +# if __FreeBSD_version >= 1200059 +# ifndef SM_CONF_SEM +# define SM_CONF_SEM 2 /* union semun is no longer declared by default */ +# endif +# endif #endif #ifndef SM_CONF_SHM diff --git a/contrib/sendmail/include/sm/os/sm_os_openbsd.h b/contrib/sendmail/include/sm/os/sm_os_openbsd.h index a1d94e949c3f..21e0a1010852 100644 --- a/contrib/sendmail/include/sm/os/sm_os_openbsd.h +++ b/contrib/sendmail/include/sm/os/sm_os_openbsd.h @@ -18,13 +18,27 @@ #define SM_OS_NAME "openbsd" +/* +** Temporary HACK for newer icu4c versions which include stdbool.h: +** pretend that it is already included +** otherwise compilation will break because bool is then +** redefined between the prototype declaration and +** the function definition, e.g., +** lowercase.c: error: conflicting types for 'asciistr' +** ../../include/sm/ixlen.h:29:13: note: previous declaration is here +*/ + +#if USE_EAI && !SM_CONF_STDBOOL_H +# define _STDBOOL_H_ 1 +#endif + #define SM_CONF_SYS_CDEFS_H 1 #ifndef SM_CONF_SHM # define SM_CONF_SHM 1 -#endif /* SM_CONF_SHM */ +#endif #ifndef SM_CONF_SEM # define SM_CONF_SEM 1 -#endif /* SM_CONF_SEM */ +#endif #ifndef SM_CONF_MSG # define SM_CONF_MSG 1 -#endif /* SM_CONF_MSG */ +#endif diff --git a/contrib/sendmail/include/sm/rpool.h b/contrib/sendmail/include/sm/rpool.h index 52f76771321a..833474d9c911 100644 --- a/contrib/sendmail/include/sm/rpool.h +++ b/contrib/sendmail/include/sm/rpool.h @@ -15,7 +15,7 @@ */ #ifndef SM_RPOOL_H -# define SM_RPOOL_H +# define SM_RPOOL_H 1 # include # include @@ -166,7 +166,15 @@ sm_rpool_malloc __P(( # endif /* SM_HEAP_CHECK */ #if DO_NOT_USE_STRCPY + +# if SM_HEAP_CHECK > 2 +extern char *sm_rpool_strdup_tagged_x __P((SM_RPOOL_T *rpool, const char *s, char *, int, int)); +# define sm_rpool_strdup_x(rpool, str) sm_rpool_strdup_tagged_x(rpool, str, "sm_rpool_strdup_x:" __FILE__, __LINE__, SmHeapGroup) +# else extern char *sm_rpool_strdup_x __P((SM_RPOOL_T *rpool, const char *s)); +# define sm_rpool_strdup_tagged_x(rpool, str, tag, line, group) sm_rpool_strdup_x(rpool, str) +# endif + #else # define sm_rpool_strdup_x(rpool, str) \ strcpy(sm_rpool_malloc_x(rpool, strlen(str) + 1), str) diff --git a/contrib/sendmail/include/sm/sem.h b/contrib/sendmail/include/sm/sem.h index f188a15deca3..548ee0858ff4 100644 --- a/contrib/sendmail/include/sm/sem.h +++ b/contrib/sendmail/include/sm/sem.h @@ -47,7 +47,7 @@ extern int sm_sem_stop __P((int)); extern int sm_sem_acq __P((int, int, int)); extern int sm_sem_rel __P((int, int, int)); extern int sm_sem_get __P((int, int)); -extern int sm_semsetowner __P((int, uid_t, gid_t, mode_t)); +extern int sm_semsetowner __P((int, uid_t, gid_t, MODE_T)); # else /* SM_CONF_SEM > 0 */ # define sm_sem_start(key, nsem, semflg, owner) 0 diff --git a/contrib/sendmail/include/sm/sendmail.h b/contrib/sendmail/include/sm/sendmail.h index 56a247f92183..8913b32d4fb4 100644 --- a/contrib/sendmail/include/sm/sendmail.h +++ b/contrib/sendmail/include/sm/sendmail.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006 Proofpoint, Inc. and its suppliers. + * Copyright (c) 2006, 2020 Proofpoint, Inc. and its suppliers. * All rights reserved. * * By using this file, you agree to the terms and conditions set @@ -14,11 +14,31 @@ #ifndef _SM_SENDMAIL_H # define _SM_SENDMAIL_H 1 +#include + /* "out of band" indicator */ #define METAQUOTE ((unsigned char)0377) /* quotes the next octet */ +#define SM_MM_QUOTE(ch) (((ch) & 0377) == METAQUOTE || (((ch) & 0340) == 0200)) extern int dequote_internal_chars __P((char *, char *, int)); -extern char *quote_internal_chars __P((char *, char *, int *)); +#if SM_HEAP_CHECK > 2 +extern char *quote_internal_chars_tagged __P((char *, char *, int *, SM_RPOOL_T *, char *, int, int)); +# define quote_internal_chars(ibp, obp, bsp, rpool) quote_internal_chars_tagged(ibp, obp, bsp, rpool, "quote_internal_chars:" __FILE__, __LINE__, SmHeapGroup) +#else +extern char *quote_internal_chars __P((char *, char *, int *, SM_RPOOL_T *)); +# define quote_internal_chars_tagged(ibp, obp, bsp, rpool, file, line, group) quote_internal_chars(ibp, obp, bsp, rpool) +#endif extern char *str2prt __P((char *)); +extern char *makelower __P((char *)); +#if USE_EAI +extern bool sm_strcaseeq __P((const char *, const char *)); +extern bool sm_strncaseeq __P((const char *, const char *, size_t)); +# define SM_STRCASEEQ(a, b) sm_strcaseeq((a), (b)) +# define SM_STRNCASEEQ(a, b, n) sm_strncaseeq((a), (b), (n)) +#else +# define SM_STRCASEEQ(a, b) (sm_strcasecmp((a), (b)) == 0) +# define SM_STRNCASEEQ(a, b, n) (sm_strncasecmp((a), (b), (n)) == 0) +#endif + #endif /* ! _SM_SENDMAIL_H */ diff --git a/contrib/sendmail/include/sm/shm.h b/contrib/sendmail/include/sm/shm.h index f6c5ad56c1c9..2ee0fdbc9387 100644 --- a/contrib/sendmail/include/sm/shm.h +++ b/contrib/sendmail/include/sm/shm.h @@ -28,7 +28,7 @@ extern void *sm_shmstart __P((key_t, int , int , int *, bool)); extern int sm_shmstop __P((void *, int, bool)); -extern int sm_shmsetowner __P((int, uid_t, gid_t, mode_t)); +extern int sm_shmsetowner __P((int, uid_t, gid_t, MODE_T)); /* for those braindead systems... (e.g., SunOS 4) */ diff --git a/contrib/sendmail/include/sm/sysexits.h b/contrib/sendmail/include/sm/sysexits.h index 53767d54e7c4..0505e8a0c3e0 100644 --- a/contrib/sendmail/include/sm/sysexits.h +++ b/contrib/sendmail/include/sm/sysexits.h @@ -74,6 +74,10 @@ ** CANTCREAT, but rather for higher level permissions. */ +# ifdef EX_OK +# undef EX_OK /* for SVr4.2 SMP */ +# endif + # if SM_CONF_SYSEXITS_H # include # else /* SM_CONF_SYSEXITS_H */ diff --git a/contrib/sendmail/include/sm/time.h b/contrib/sendmail/include/sm/time.h index b6d31a4bd292..40134fdb557c 100644 --- a/contrib/sendmail/include/sm/time.h +++ b/contrib/sendmail/include/sm/time.h @@ -13,7 +13,6 @@ # define SM_TIME_H 1 # include - # include /* should be defined in sys/time.h */ @@ -52,5 +51,4 @@ ((tvp)->tv_sec cmp (uvp)->tv_sec)) #endif /* !timercmp */ - #endif /* ! SM_TIME_H */ diff --git a/contrib/sendmail/libmilter/docs/overview.html b/contrib/sendmail/libmilter/docs/overview.html index b88e45136965..5bbdc2f96a85 100644 --- a/contrib/sendmail/libmilter/docs/overview.html +++ b/contrib/sendmail/libmilter/docs/overview.html @@ -75,8 +75,9 @@ MESSAGE:For each message in this connection (sequentially) process recipient (xxfi_envrcpt) } For each filter - { process DATA (xxfi_data) + For each filter + { For each header process header (xxfi_header) process end of headers (xxfi_eoh) diff --git a/contrib/sendmail/libmilter/docs/smfi_setmlreply.html b/contrib/sendmail/libmilter/docs/smfi_setmlreply.html index 3f36e5296430..35ed16b4b891 100644 --- a/contrib/sendmail/libmilter/docs/smfi_setmlreply.html +++ b/contrib/sendmail/libmilter/docs/smfi_setmlreply.html @@ -33,7 +33,8 @@ other than xxfi_connect. Effects Directly set the SMTP error reply code for this connection to the given -lines after the xcode. The list of arguments must be NULL terminated. +lines after the xcode. +The list of arguments must be NULL terminated. This code will be used on subsequent error replies resulting from actions taken by this filter. @@ -48,25 +49,28 @@ taken by this filter. Opaque context structure. rcode - The three-digit (RFC 821/2821) SMTP reply code, as a - null-terminated string. rcode cannot be NULL, and must be a valid - 4XX or 5XX reply code. + The three-digit (RFC 821/2821) SMTP reply code, + as a null-terminated string. + rcode cannot be NULL, and must be a valid 4XX or 5XX reply code. xcode - The extended (RFC 1893/2034) reply code. If xcode is NULL, no - extended code is used. Otherwise, xcode must conform to RFC 1893/2034. + The extended (RFC 1893/2034) reply code. + If xcode is NULL, a generic X.0.0 code is used, + where X is the first digit of rcode. + Otherwise, xcode must conform to RFC 1893/2034. ... - The remainder of the arguments are single lines of text, up to - 32 arguments, which will be used as the text part of the SMTP - reply. The list must be NULL terminated. + The remainder of the arguments are single lines of text, + up to 32 arguments, + which will be used as the text part of the SMTP reply. + The list must be NULL terminated. -RETURN VALUES +EXAMPLE For example, the code:
diff --git a/contrib/sendmail/libmilter/engine.c b/contrib/sendmail/libmilter/engine.c
index 2619395a6eb1..5872c166ce0a 100644
--- a/contrib/sendmail/libmilter/engine.c
+++ b/contrib/sendmail/libmilter/engine.c
@@ -64,28 +64,28 @@ typedef struct cmdfct_t cmdfct;
 #define CI_EOH		6
 #define CI_LAST		CI_EOH
 #if CI_LAST < CI_DATA
-ERROR: do not compile with CI_LAST < CI_DATA
+# ERROR "do not compile with CI_LAST < CI_DATA"
 #endif
 #if CI_LAST < CI_EOM
-ERROR: do not compile with CI_LAST < CI_EOM
+# ERROR "do not compile with CI_LAST < CI_EOM"
 #endif
 #if CI_LAST < CI_EOH
-ERROR: do not compile with CI_LAST < CI_EOH
+# ERROR "do not compile with CI_LAST < CI_EOH"
 #endif
 #if CI_LAST < CI_RCPT
-ERROR: do not compile with CI_LAST < CI_RCPT
+# ERROR "do not compile with CI_LAST < CI_RCPT"
 #endif
 #if CI_LAST < CI_MAIL
-ERROR: do not compile with CI_LAST < CI_MAIL
+# ERROR "do not compile with CI_LAST < CI_MAIL"
 #endif
 #if CI_LAST < CI_HELO
-ERROR: do not compile with CI_LAST < CI_HELO
+# ERROR "do not compile with CI_LAST < CI_HELO"
 #endif
 #if CI_LAST < CI_CONN
-ERROR: do not compile with CI_LAST < CI_CONN
+# ERROR "do not compile with CI_LAST < CI_CONN"
 #endif
 #if CI_LAST >= MAX_MACROS_ENTRIES
-ERROR: do not compile with CI_LAST >= MAX_MACROS_ENTRIES
+# ERROR "do not compile with CI_LAST >= MAX_MACROS_ENTRIES"
 #endif
 
 /* function prototypes */
@@ -1190,7 +1190,7 @@ st_connectinfo(g)
 	s = g->a_buf;
 	i = 0;
 	l = g->a_len;
-	while (i <= l && s[i] != '\0')  
+	while (i <= l && s[i] != '\0')
 		++i;
 	if (i + 1 >= l)
 		return _SMFIS_ABORT;
@@ -1216,7 +1216,7 @@ st_connectinfo(g)
 		/* make sure string is terminated */
 		if (s[l - 1] != '\0')
 			return _SMFIS_ABORT;
-# if NETINET
+#if NETINET
 		if (family == SMFIA_INET)
 		{
 			if (inet_aton(s + i, (struct in_addr *) &sockaddr.sin.sin_addr)
@@ -1233,8 +1233,8 @@ st_connectinfo(g)
 				sockaddr.sin.sin_port = port;
 		}
 		else
-# endif /* NETINET */
-# if NETINET6
+#endif /* NETINET */
+#if NETINET6
 		if (family == SMFIA_INET6)
 		{
 			if (mi_inet_pton(AF_INET6, s + i,
@@ -1251,8 +1251,8 @@ st_connectinfo(g)
 				sockaddr.sin6.sin6_port = port;
 		}
 		else
-# endif /* NETINET6 */
-# if NETUNIX
+#endif /* NETINET6 */
+#if NETUNIX
 		if (family == SMFIA_UNIX)
 		{
 			if (sm_strlcpy(sockaddr.sunix.sun_path, s + i,
@@ -1268,7 +1268,7 @@ st_connectinfo(g)
 			sockaddr.sunix.sun_family = AF_UNIX;
 		}
 		else
-# endif /* NETUNIX */
+#endif /* NETUNIX */
 		{
 			smi_log(SMI_LOG_ERR,
 				"%s: connect[%ld]: unknown family %d",
@@ -1870,21 +1870,21 @@ mi_rd_socket_ready (sd)
 {
 	int n;
 	int nerr = 0;
-#if SM_CONF_POLL
+# if SM_CONF_POLL
 	struct pollfd pfd;
-#else
+# else
 	fd_set	rd_set, exc_set;
-#endif
+# endif
 
 	do
 	{
-#if SM_CONF_POLL
+# if SM_CONF_POLL
 		pfd.fd = sd;
 		pfd.events = POLLIN;
 		pfd.revents = 0;
 
 		n = poll(&pfd, 1, MI_RD_CMD_TO);
-#else /* SM_CONF_POLL */
+# else /* SM_CONF_POLL */
 		struct timeval timeout;
 
 		FD_ZERO(&rd_set);
@@ -1895,7 +1895,7 @@ mi_rd_socket_ready (sd)
 		timeout.tv_sec = MI_RD_CMD_TO / 1000;
 		timeout.tv_usec = 0;
 		n = select(sd + 1, &rd_set, NULL, &exc_set, &timeout);
-#endif /* SM_CONF_POLL */
+# endif /* SM_CONF_POLL */
 
 		if (n < 0)
 		{
@@ -1914,10 +1914,10 @@ mi_rd_socket_ready (sd)
 	if (nerr >= MI_RD_MAX_ERR)
 		return false;
 
-#if SM_CONF_POLL
+# if SM_CONF_POLL
 	return (pfd.revents != 0);
-#else
+# else
 	return FD_ISSET(sd, &rd_set) || FD_ISSET(sd, &exc_set);
-#endif
+# endif
 }
 #endif /* _FFR_WORKERS_POOL */
diff --git a/contrib/sendmail/libmilter/listener.c b/contrib/sendmail/libmilter/listener.c
index 0468a6231cce..25ff895d339e 100644
--- a/contrib/sendmail/libmilter/listener.c
+++ b/contrib/sendmail/libmilter/listener.c
@@ -22,13 +22,13 @@ SM_RCSID("@(#)$Id: listener.c,v 8.127 2013-11-22 20:51:36 ca Exp $")
 #include 
 
 
-# if NETINET || NETINET6
-#  include 
-# endif
-# if SM_CONF_POLL
-#  undef SM_FD_OK_SELECT
-#  define SM_FD_OK_SELECT(fd)		true
-# endif
+#if NETINET || NETINET6
+# include 
+#endif
+#if SM_CONF_POLL
+# undef SM_FD_OK_SELECT
+# define SM_FD_OK_SELECT(fd)		true
+#endif
 
 static smutex_t L_Mutex;
 static int L_family;
@@ -815,9 +815,9 @@ mi_listener(conn, dbg, smfi, timeout, backlog)
 
 		if (ValidSocket(connfd) &&
 		    (clilen == 0 ||
-# ifdef BSD4_4_SOCKADDR
+#ifdef BSD4_4_SOCKADDR
 		     cliaddr.sa.sa_len == 0 ||
-# endif
+#endif
 		     cliaddr.sa.sa_family != L_family))
 		{
 			(void) closesocket(connfd);
@@ -961,7 +961,7 @@ mi_listener(conn, dbg, smfi, timeout, backlog)
 			tcnt++;
 			smi_log(SMI_LOG_ERR,
 				LOG_CRT_FAIL,
-				smfi->xxfi_name,  r,
+				smfi->xxfi_name, r,
 				tcnt >= MAX_FAILS_T ? "abort" : "try again");
 			MI_SLEEP(tcnt);
 			(void) closesocket(connfd);
@@ -980,8 +980,10 @@ mi_listener(conn, dbg, smfi, timeout, backlog)
 	else
 	{
 		if (mistop != MILTER_CONT)
-			smi_log(SMI_LOG_INFO, "%s: mi_stop=%d",
-				smfi->xxfi_name, mistop);
+			smi_log(SMI_LOG_INFO, "%s=%s",
+				smfi->xxfi_name,
+				MILTER_STOP == mistop ? "terminating"
+							: "aborting");
 		mi_closener();
 	}
 	(void) smutex_destroy(&L_Mutex);
diff --git a/contrib/sendmail/libmilter/sm_gethost.c b/contrib/sendmail/libmilter/sm_gethost.c
index 262edb6c541c..9bbf9b78b848 100644
--- a/contrib/sendmail/libmilter/sm_gethost.c
+++ b/contrib/sendmail/libmilter/sm_gethost.c
@@ -149,9 +149,9 @@ mi_gethostbyname(name, family)
 	/* the function is supposed to return only the requested family */
 	if (h != NULL && h->h_addrtype != family)
 	{
-# if NETINET6
+#if NETINET6
 		freehostent(h);
-# endif
+#endif
 		h = NULL;
 		SM_SET_H_ERRNO(NO_DATA);
 	}
diff --git a/contrib/sendmail/libsm/Makefile.m4 b/contrib/sendmail/libsm/Makefile.m4
index 0dcb2d02b68d..173427970f62 100644
--- a/contrib/sendmail/libsm/Makefile.m4
+++ b/contrib/sendmail/libsm/Makefile.m4
@@ -6,11 +6,12 @@ define(`confREQUIRE_LIBSM', `true')
 define(`confREQUIRE_SM_OS_H', `true')
 PREPENDDEF(`confENVDEF', `confMAPDEF')
 bldPRODUCT_START(`library', `libsm')
-define(`bldSOURCES', ` assert.c debug.c errstring.c exc.c heap.c match.c rpool.c strdup.c strerror.c strl.c clrerr.c fclose.c feof.c ferror.c fflush.c fget.c fpos.c findfp.c flags.c fopen.c fprintf.c fpurge.c fput.c fread.c fscanf.c fseek.c fvwrite.c fwalk.c fwrite.c get.c makebuf.c put.c refill.c rewind.c setvbuf.c smstdio.c snprintf.c sscanf.c stdio.c strio.c ungetc.c vasprintf.c vfprintf.c vfscanf.c vprintf.c vsnprintf.c wbuf.c wsetup.c string.c stringf.c xtrap.c strto.c test.c strcasecmp.c strrevcmp.c signal.c clock.c config.c shm.c sem.c mbdb.c strexit.c cf.c ldap.c niprop.c mpeix.c memstat.c util.c inet6_ntop.c notify.c ')
+define(`bldSOURCES', ` assert.c debug.c errstring.c exc.c heap.c match.c rpool.c strdup.c strerror.c strl.c clrerr.c fclose.c feof.c ferror.c fflush.c fget.c fpos.c findfp.c flags.c fopen.c fprintf.c fpurge.c fput.c fread.c fscanf.c fseek.c fvwrite.c fwalk.c fwrite.c get.c makebuf.c put.c refill.c rewind.c setvbuf.c smstdio.c snprintf.c sscanf.c stdio.c strio.c ungetc.c vasprintf.c vfprintf.c vfscanf.c vprintf.c vsnprintf.c wbuf.c wsetup.c string.c stringf.c xtrap.c strto.c test.c strcasecmp.c strrevcmp.c signal.c clock.c config.c shm.c sem.c mbdb.c strexit.c cf.c ldap.c niprop.c mpeix.c memstat.c util.c inet6_ntop.c notify.c ilenx.c xleni.c utf8_valid.c uxtext_unquote.c lowercase.c strcaseeq.c ')
 bldPRODUCT_END
 dnl msg.c
 dnl syslogio.c
 
+srcdir=${SRCDIR}/libsm
 define(`confCHECK_LIBS',`libsm.a')dnl
 include(confBUILDTOOLSDIR`/M4/'bldM4_TYPE_DIR`/check.m4')
 smcheck(`t-event', `compile-run')
@@ -32,8 +33,6 @@ smcheck(`t-scanf', `compile-run')
 smcheck(`t-shm', `compile-run')
 smcheck(`t-sem', `compile-run')
 smcheck(`t-inet6_ntop', `compile-run')
-smcheck(`t-notify', `compile-run')
-dnl smcheck(`t-msg', `compile-run')
 smcheck(`t-cf')
 smcheck(`b-strcmp')
 dnl SM_CONF_STRL cannot be turned off
@@ -41,6 +40,13 @@ dnl smcheck(`b-strl')
 smcheck(`t-memstat')
 
 smcheck(`t-qic', `compile-run')
+smcheck(`t-str2prt', `compile-run')
+dnl ? smcheck(`t-isprint', `compile-run')
+smcheck(`t-ixlen', `compile')
+smcheck(`t-ixlen.sh', `run')
+smcheck(`t-streq', `compile')
+smcheck(`t-utf8_valid', `compile')
+smcheck(`t-streq.sh', `run')
 divert(bldTARGETS_SECTION)
 divert(0)
 
diff --git a/contrib/sendmail/libsm/cf.c b/contrib/sendmail/libsm/cf.c
index 62ca39728321..5d35ebbfb32e 100644
--- a/contrib/sendmail/libsm/cf.c
+++ b/contrib/sendmail/libsm/cf.c
@@ -18,6 +18,7 @@ SM_RCSID("@(#)$Id: cf.c,v 1.8 2013-11-22 20:51:42 ca Exp $")
 #include 
 #include 
 #include 
+#include 
 
 /*
 **  SM_CF_GETOPT -- look up option values in the sendmail.cf file
@@ -80,7 +81,7 @@ sm_cf_getopt(path, optc, optv)
 
 		for (i = 0; i < optc; ++i)
 		{
-			if (sm_strcasecmp(optv[i].opt_name, id) == 0)
+			if (SM_STRCASEEQ(optv[i].opt_name, id))
 			{
 				optv[i].opt_val = sm_strdup_x(val);
 				break;
diff --git a/contrib/sendmail/libsm/clock.c b/contrib/sendmail/libsm/clock.c
index 6e300b0a5ca4..7bc921fc36cd 100644
--- a/contrib/sendmail/libsm/clock.c
+++ b/contrib/sendmail/libsm/clock.c
@@ -162,7 +162,7 @@ sm_sigsafe_seteventm(intvl, func, arg)
 	LEAVE_CRITICAL();
 
 	(void) sm_signal(SIGALRM, sm_tick);
-# if SM_CONF_SETITIMER
+#if SM_CONF_SETITIMER
 	timersub(&SmEventQueue->ev_time, &now, &itime.it_value);
 	itime.it_interval.tv_sec = 0;
 	itime.it_interval.tv_usec = 0;
@@ -171,10 +171,10 @@ sm_sigsafe_seteventm(intvl, func, arg)
 	if (itime.it_value.tv_sec == 0 && itime.it_value.tv_usec == 0)
 		itime.it_value.tv_usec = 1000;
 	(void) setitimer(ITIMER_REAL, &itime, NULL);
-# else /* SM_CONF_SETITIMER */
+#else /* SM_CONF_SETITIMER */
 	intvl = SmEventQueue->ev_time - now;
 	(void) alarm((unsigned) (intvl < 1 ? 1 : intvl));
-# endif /* SM_CONF_SETITIMER */
+#endif /* SM_CONF_SETITIMER */
 	if (wasblocked == 0)
 		(void) sm_releasesignal(SIGALRM);
 	return ev;
@@ -198,9 +198,9 @@ sm_clrevent(ev)
 {
 	register SM_EVENT **evp;
 	int wasblocked;
-# if SM_CONF_SETITIMER
+#if SM_CONF_SETITIMER
 	struct itimerval clr;
-# endif
+#endif
 
 	if (ev == NULL)
 		return;
@@ -233,15 +233,15 @@ sm_clrevent(ev)
 	else
 	{
 		/* nothing left in event queue, no need for an alarm */
-# if SM_CONF_SETITIMER
+#if SM_CONF_SETITIMER
 		clr.it_interval.tv_sec = 0;
 		clr.it_interval.tv_usec = 0;
 		clr.it_value.tv_sec = 0;
 		clr.it_value.tv_usec = 0;
 		(void) setitimer(ITIMER_REAL, &clr, NULL);
-# else /* SM_CONF_SETITIMER */
+#else /* SM_CONF_SETITIMER */
 		(void) alarm(0);
-# endif /* SM_CONF_SETITIMER */
+#endif /* SM_CONF_SETITIMER */
 	}
 }
 /*
@@ -496,10 +496,10 @@ sm_tick(sig)
 */
 
 
-# if !HAVE_NANOSLEEP
+#if !HAVE_NANOSLEEP
 static void	sm_endsleep __P((int));
 static bool	volatile SmSleepDone;
-# endif
+#endif
 
 #ifndef SLEEP_T
 # define SLEEP_T	unsigned int
@@ -521,70 +521,70 @@ sleep(intvl)
 #else /* HAVE_NANOSLEEP */
 	int was_held;
 	SM_EVENT *ev;
-#if _FFR_SLEEP_USE_SELECT > 0
+# if _FFR_SLEEP_USE_SELECT > 0
 	int r;
-# if _FFR_SLEEP_USE_SELECT > 0
+#  if _FFR_SLEEP_USE_SELECT > 0
 	struct timeval sm_io_to;
-# endif
-#endif /* _FFR_SLEEP_USE_SELECT > 0 */
-#if SM_CONF_SETITIMER
+#  endif
+# endif /* _FFR_SLEEP_USE_SELECT > 0 */
+# if SM_CONF_SETITIMER
 	struct timeval now, begin, diff;
-# if _FFR_SLEEP_USE_SELECT > 0
+#  if _FFR_SLEEP_USE_SELECT > 0
 	struct timeval slpv;
-# endif
-#else /*  SM_CONF_SETITIMER */
+#  endif
+# else /*  SM_CONF_SETITIMER */
 	time_t begin, now;
-#endif /*  SM_CONF_SETITIMER */
+# endif /*  SM_CONF_SETITIMER */
 
 	if (intvl == 0)
 		return (SLEEP_T) 0;
-#if defined(_FFR_MAX_SLEEP_TIME) && _FFR_MAX_SLEEP_TIME > 2
+# if defined(_FFR_MAX_SLEEP_TIME) && _FFR_MAX_SLEEP_TIME > 2
 	if (intvl > _FFR_MAX_SLEEP_TIME)
 	{
 		syslog(LOG_ERR, "sleep: interval=%u exceeds max value %d",
 			intvl, _FFR_MAX_SLEEP_TIME);
-# if 0
+#  if 0
 		SM_ASSERT(intvl < (unsigned int) INT_MAX);
-# endif
+#  endif
 		intvl = _FFR_MAX_SLEEP_TIME;
 	}
-#endif /* defined(_FFR_MAX_SLEEP_TIME) && _FFR_MAX_SLEEP_TIME > 2 */
+# endif /* defined(_FFR_MAX_SLEEP_TIME) && _FFR_MAX_SLEEP_TIME > 2 */
 	SmSleepDone = false;
 
-#if SM_CONF_SETITIMER
-# if _FFR_SLEEP_USE_SELECT > 0
+# if SM_CONF_SETITIMER
+#  if _FFR_SLEEP_USE_SELECT > 0
 	slpv.tv_sec = intvl;
 	slpv.tv_usec = 0;
-# endif
+#  endif
 	(void) gettimeofday(&now, NULL);
 	begin = now;
-#else /*  SM_CONF_SETITIMER */
+# else /*  SM_CONF_SETITIMER */
 	now = begin = time(NULL);
-#endif /*  SM_CONF_SETITIMER */
+# endif /*  SM_CONF_SETITIMER */
 
 	ev = sm_setevent((time_t) intvl, sm_endsleep, 0);
 	if (ev == NULL)
 	{
 		/* COMPLAIN */
-#if 0
+# if 0
 		syslog(LOG_ERR, "sleep: sm_setevent(%u) failed", intvl);
-#endif
+# endif
 		SmSleepDone = true;
 	}
 	was_held = sm_releasesignal(SIGALRM);
 
 	while (!SmSleepDone)
 	{
-#if SM_CONF_SETITIMER
+# if SM_CONF_SETITIMER
 		(void) gettimeofday(&now, NULL);
 		timersub(&now, &begin, &diff);
 		if (diff.tv_sec < 0 ||
 		    (diff.tv_sec == 0 && diff.tv_usec == 0))
 			break;
-# if _FFR_SLEEP_USE_SELECT > 0
+#  if _FFR_SLEEP_USE_SELECT > 0
 		timersub(&slpv, &diff, &sm_io_to);
-# endif
-#else /* SM_CONF_SETITIMER */
+#  endif
+# else /* SM_CONF_SETITIMER */
 		now = time(NULL);
 
 		/*
@@ -595,14 +595,14 @@ sleep(intvl)
 
 		if (!(begin + (time_t) intvl + 1 > now))
 			break;
-# if _FFR_SLEEP_USE_SELECT > 0
+#  if _FFR_SLEEP_USE_SELECT > 0
 		sm_io_to.tv_sec = intvl - (now - begin);
 		if (sm_io_to.tv_sec <= 0)
 			sm_io_to.tv_sec = 1;
 		sm_io_to.tv_usec = 0;
-# endif /* _FFR_SLEEP_USE_SELECT > 0 */
-#endif /* SM_CONF_SETITIMER */
-#if _FFR_SLEEP_USE_SELECT > 0
+#  endif /* _FFR_SLEEP_USE_SELECT > 0 */
+# endif /* SM_CONF_SETITIMER */
+# if _FFR_SLEEP_USE_SELECT > 0
 		if (intvl <= _FFR_SLEEP_USE_SELECT)
 		{
 			r = select(0, NULL, NULL, NULL, &sm_io_to);
@@ -610,7 +610,8 @@ sleep(intvl)
 				break;
 		}
 		else
-#endif /* _FFR_SLEEP_USE_SELECT > 0 */
+# endif /* _FFR_SLEEP_USE_SELECT > 0 */
+		/* "else" in #if code above */
 		(void) pause();
 	}
 
diff --git a/contrib/sendmail/libsm/config.c b/contrib/sendmail/libsm/config.c
index b96a4a0c8aba..cb0c941ff5be 100644
--- a/contrib/sendmail/libsm/config.c
+++ b/contrib/sendmail/libsm/config.c
@@ -219,12 +219,10 @@ char *SmCompileOptions[] =
 	"SM_CONF_STDDEF_H",
 #endif
 
-#if 0
 /* XXX this is always enabled (for now) */
-#if SM_CONF_STRL
+#if SM_CONF_STRL && 0
 	"SM_CONF_STRL",
 #endif
-#endif /* 0 */
 
 #if SM_CONF_SYS_CDEFS_H
 	"SM_CONF_SYS_CDEFS_H",
diff --git a/contrib/sendmail/libsm/heap.c b/contrib/sendmail/libsm/heap.c
index dd5e64b0bfdd..330739acef22 100644
--- a/contrib/sendmail/libsm/heap.c
+++ b/contrib/sendmail/libsm/heap.c
@@ -25,6 +25,13 @@ SM_RCSID("@(#)$Id: heap.c,v 1.52 2013-11-22 20:51:43 ca Exp $")
 #include 
 #include 
 
+#if SM_HEAP_CHECK
+# include 
+# include 
+# include 
+# include 
+#endif
+
 /* undef all macro versions of the "functions" so they can be specified here */
 #undef sm_malloc
 #undef sm_malloc_x
@@ -458,7 +465,7 @@ sm_malloc_tagged_x(size, tag, num, group)
 **	Parameters:
 **		ptr -- pointer to register.
 **		size -- size of requested memory.
-**		tag -- tag for debugging.
+**		tag -- tag for debugging (this is NOT copied!)
 **		num -- additional value for debugging.
 **		group -- heap group for debugging.
 **
@@ -754,6 +761,9 @@ sm_heap_report(stream, verbosity)
 {
 	int i;
 	unsigned long group0total, group1total, otherstotal, grandtotal;
+	static char str[32] = "[1900-00-00/00:00:00] ";
+	struct tm *tmp;
+	time_t currt;
 
 	if (!HEAP_CHECK || verbosity <= 0)
 		return;
@@ -804,11 +814,18 @@ sm_heap_report(stream, verbosity)
 			hi = hi->hi_next;
 		}
 	}
+
+	currt = time((time_t *)0);
+	tmp = localtime(&currt);
+	snprintf(str, sizeof(str), "[%d-%02d-%02d/%02d:%02d:%02d] ",
+		1900 + tmp->tm_year,	/* HACK */
+		tmp->tm_mon + 1,
+		tmp->tm_mday,
+		tmp->tm_hour, tmp->tm_min, tmp->tm_sec);
 	sm_io_fprintf(stream, SM_TIME_DEFAULT,
-		"heap max=%lu, total=%lu, ",
-		(unsigned long) SmHeapMaxTotal, grandtotal);
-	sm_io_fprintf(stream, SM_TIME_DEFAULT,
-		"group 0=%lu, group 1=%lu, others=%lu\n",
+		"pid=%ld time=%s\nheap max=%lu, total=%lu, group 0=%lu, group 1=%lu, others=%lu\n",
+		(long) getpid(), str,
+		(unsigned long) SmHeapMaxTotal, grandtotal,
 		group0total, group1total, otherstotal);
 	if (grandtotal != SmHeapTotal)
 	{
diff --git a/contrib/sendmail/libsm/ilenx.c b/contrib/sendmail/libsm/ilenx.c
new file mode 100644
index 000000000000..ef7e7427929a
--- /dev/null
+++ b/contrib/sendmail/libsm/ilenx.c
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2020 Proofpoint, Inc. and its suppliers.
+ *	All rights reserved.
+ *
+ * By using this file, you agree to the terms and conditions set
+ * forth in the LICENSE file which can be found at the top level of
+ * the sendmail distribution.
+ *
+ */
+
+#include 
+#include 
+#include 
+
+#if _FFR_8BITENVADDR
+/*
+**  ILENX -- determine the e'x'ternal length of a string in 'i'internal format
+**
+**	Parameters:
+**		str -- string [i]
+**
+**	Returns:
+**		e'x'ternal length of a string in 'i'internal format
+*/
+
+int
+ilenx(str)
+	const char *str;
+{
+	char c;
+	int idx;
+	XLENDECL
+
+	if (NULL == str)
+		return -1;
+	for (idx = 0; (c = str[idx]) != '\0'; idx++)
+		XLEN(c);
+	return xlen;
+}
+#endif /* _FFR_8BITENVADDR */
diff --git a/contrib/sendmail/libsm/ldap.c b/contrib/sendmail/libsm/ldap.c
index 116dc8fac343..a78ab4daa567 100644
--- a/contrib/sendmail/libsm/ldap.c
+++ b/contrib/sendmail/libsm/ldap.c
@@ -27,10 +27,8 @@ SM_RCSID("@(#)$Id: ldap.c,v 1.86 2013-11-22 20:51:43 ca Exp $")
 # include 
 # include 
 # include 
-#  ifdef EX_OK
-#   undef EX_OK			/* for SVr4.2 SMP */
-#  endif
 # include 
+# include 
 
 SM_DEBUG_T SmLDAPTrace = SM_DEBUG_INITIALIZER("sm_trace_ldap",
 	"@(#)$Debug: sm_trace_ldap - trace LDAP operations $");
@@ -49,17 +47,17 @@ static SM_LDAP_RECURSE_ENTRY *sm_ldap_add_recurse __P((SM_LDAP_RECURSE_LIST **,
 **
 */
 
-#if _FFR_LDAP_VERSION
-# if defined(LDAP_VERSION_MAX) && _FFR_LDAP_VERSION > LDAP_VERSION_MAX
-    ERROR FFR_LDAP_VERSION > _LDAP_VERSION_MAX
-# endif
-# if defined(LDAP_VERSION_MIN) && _FFR_LDAP_VERSION < LDAP_VERSION_MIN
-    ERROR FFR_LDAP_VERSION < _LDAP_VERSION_MIN
-# endif
-# define SM_LDAP_VERSION_DEFAULT	_FFR_LDAP_VERSION
-#else /* _FFR_LDAP_VERSION */
-# define SM_LDAP_VERSION_DEFAULT	0
-#endif /* _FFR_LDAP_VERSION */
+# if _FFR_LDAP_VERSION
+#  if defined(LDAP_VERSION_MAX) && _FFR_LDAP_VERSION > LDAP_VERSION_MAX
+#   ERROR "_FFR_LDAP_VERSION > LDAP_VERSION_MAX"
+#  endif
+#  if defined(LDAP_VERSION_MIN) && _FFR_LDAP_VERSION < LDAP_VERSION_MIN
+#   ERROR "_FFR_LDAP_VERSION < LDAP_VERSION_MAX"
+#  endif
+#  define SM_LDAP_VERSION_DEFAULT	_FFR_LDAP_VERSION
+# else /* _FFR_LDAP_VERSION */
+#  define SM_LDAP_VERSION_DEFAULT	0
+# endif /* _FFR_LDAP_VERSION */
 
 void
 sm_ldap_clear(lmap)
@@ -100,7 +98,7 @@ sm_ldap_clear(lmap)
 	lmap->ldap_multi_args = false;
 }
 
-#  if _FFR_SM_LDAP_DBG && defined(LBER_OPT_LOG_PRINT_FN)
+# if _FFR_SM_LDAP_DBG && defined(LBER_OPT_LOG_PRINT_FN)
 static void ldap_debug_cb __P((const char *msg));
 
 static void
@@ -110,7 +108,7 @@ ldap_debug_cb(msg)
 	if (sm_debug_active(&SmLDAPTrace, 4))
 		sm_dprintf("%s", msg);
 }
-#  endif /* _FFR_SM_LDAP_DBG && defined(LBER_OPT_LOG_PRINT_FN) */
+# endif /* _FFR_SM_LDAP_DBG && defined(LBER_OPT_LOG_PRINT_FN) */
 
 
 # if LDAP_NETWORK_TIMEOUT && defined(LDAP_OPT_NETWORK_TIMEOUT)
@@ -298,7 +296,7 @@ do									\
 	if (ev != NULL)							\
 		sm_clrevent(ev);					\
 } while (0)
-#endif /* !USE_LDAP_INIT || !LDAP_NETWORK_TIMEOUT */
+# endif /* !USE_LDAP_INIT || !LDAP_NETWORK_TIMEOUT */
 
 bool
 sm_ldap_start(name, lmap)
@@ -336,7 +334,7 @@ sm_ldap_start(name, lmap)
 
 	if (lmap->ldap_uri != NULL)
 	{
-#if SM_CONF_LDAP_INITIALIZE
+# if SM_CONF_LDAP_INITIALIZE
 		if (sm_debug_active(&SmLDAPTrace, 9))
 			sm_dprintf("ldap_initialize(%s)\n", lmap->ldap_uri);
 		/* LDAP server supports URIs so use them directly */
@@ -345,7 +343,7 @@ sm_ldap_start(name, lmap)
 			sm_dprintf("ldap_initialize(%s)=%d, ld=%p\n", lmap->ldap_uri, save_errno, ld);
 		sm_ldap_setoptsg(lmap);
 
-#else /* SM_CONF_LDAP_INITIALIZE */
+# else /* SM_CONF_LDAP_INITIALIZE */
 		LDAPURLDesc *ludp = NULL;
 
 		/* Blast apart URL and use the ldap_init/ldap_open below */
@@ -365,7 +363,7 @@ sm_ldap_start(name, lmap)
 		}
 		lmap->ldap_port = ludp->lud_port;
 		ldap_free_urldesc(ludp);
-#endif /* SM_CONF_LDAP_INITIALIZE */
+# endif /* SM_CONF_LDAP_INITIALIZE */
 	}
 
 	if (ld == NULL)
@@ -537,9 +535,9 @@ sm_ldap_search_m(lmap, argv)
 
 		if (lmap->ldap_multi_args)
 		{
-#if SM_LDAP_ARGS < 10
-# ERROR _SM_LDAP_ARGS must be 10
-#endif /* SM_LDAP_ARGS < 10 */
+# if SM_LDAP_ARGS < 10
+#  ERROR _SM_LDAP_ARGS must be 10
+# endif
 			if (q[1] == 's')
 				key = argv[0];
 			else if (q[1] >= '0' && q[1] <= '9')
@@ -549,9 +547,9 @@ sm_ldap_search_m(lmap, argv)
 				{
 # if SM_LDAP_ERROR_ON_MISSING_ARGS
 					return SM_LDAP_ERR_ARG_MISS;
-# else /* SM_LDAP_ERROR_ON_MISSING_ARGS */
+# else
 					key = "";
-# endif /* SM_LDAP_ERROR_ON_MISSING_ARGS */
+# endif
 				}
 			}
 			else
@@ -907,7 +905,7 @@ sm_ldap_results(lmap, msgid, flags, delim, rpool, result,
 				continue;
 			}
 
-#if _FFR_LDAP_SINGLEDN
+# if _FFR_LDAP_SINGLEDN
 			if (bitset(SM_LDAP_SINGLEDN, flags) && *result != NULL)
 			{
 				/* only wanted one match */
@@ -915,7 +913,7 @@ sm_ldap_results(lmap, msgid, flags, delim, rpool, result,
 				errno = ENOENT;
 				return EX_NOTFOUND;
 			}
-#endif /* _FFR_LDAP_SINGLEDN */
+# endif /* _FFR_LDAP_SINGLEDN */
 
 			/* record completed DN's to prevent loops */
 			dn = ldap_get_dn(lmap->ldap_ld, entry);
@@ -970,8 +968,8 @@ sm_ldap_results(lmap, msgid, flags, delim, rpool, result,
 				type = SM_LDAP_ATTR_NONE;
 				for (i = 0; lmap->ldap_attr[i] != NULL; i++)
 				{
-					if (sm_strcasecmp(lmap->ldap_attr[i],
-							  attr) == 0)
+					if (SM_STRCASEEQ(lmap->ldap_attr[i],
+							 attr))
 					{
 						type = lmap->ldap_attr_type[i];
 						needobjclass = lmap->ldap_attr_needobjclass[i];
@@ -1342,9 +1340,9 @@ sm_ldap_results(lmap, msgid, flags, delim, rpool, result,
 		statp = EX_TEMPFAIL;
 		switch (save_errno)
 		{
-#ifdef LDAP_SERVER_DOWN
+# ifdef LDAP_SERVER_DOWN
 		  case LDAP_SERVER_DOWN:
-#endif /* LDAP_SERVER_DOWN */
+# endif
 		  case LDAP_TIMEOUT:
 		  case ETIMEDOUT:
 		  case LDAP_UNAVAILABLE:
@@ -1504,9 +1502,9 @@ sm_ldap_results(lmap, msgid, flags, delim, rpool, result,
 				statp = EX_TEMPFAIL;
 				switch (save_errno)
 				{
-#ifdef LDAP_SERVER_DOWN
+# ifdef LDAP_SERVER_DOWN
 				  case LDAP_SERVER_DOWN:
-#endif /* LDAP_SERVER_DOWN */
+# endif
 				  case LDAP_TIMEOUT:
 				  case ETIMEDOUT:
 				  case LDAP_UNAVAILABLE:
@@ -1616,4 +1614,4 @@ sm_ldap_geterrno(ld)
 # endif /* defined(LDAP_VERSION_MAX) && LDAP_VERSION_MAX >= 3 */
 	return err;
 }
-# endif /* LDAPMAP */
+#endif /* LDAPMAP */
diff --git a/contrib/sendmail/libsm/lowercase.c b/contrib/sendmail/libsm/lowercase.c
new file mode 100644
index 000000000000..8448eee5ad40
--- /dev/null
+++ b/contrib/sendmail/libsm/lowercase.c
@@ -0,0 +1,162 @@
+/*
+ * Copyright (c) 2020 Proofpoint, Inc. and its suppliers.
+ *	All rights reserved.
+ *
+ * By using this file, you agree to the terms and conditions set
+ * forth in the LICENSE file which can be found at the top level of
+ * the sendmail distribution.
+ *
+ */
+
+#include 
+#include 
+
+#include 
+#include 
+#include 
+#if USE_EAI
+# include 
+# include 
+# include 
+# include 
+
+/*
+**  ASCIISTR -- check whether a string is printable ASCII
+**
+**	Parameters:
+**		str -- string
+**
+**	Returns:
+**		TRUE iff printable ASCII
+*/
+
+bool
+asciistr(str)
+	const char *str;
+{
+	unsigned char ch;
+
+	if  (str == NULL)
+		return true;
+	while ((ch = (unsigned char)*str) != '\0' && ch >= 32 && ch < 127)
+		str++;
+	return ch == '\0';
+}
+#endif /* USE_EAI */
+
+/*
+**  MAKELOWER -- Translate a line into lower case
+**
+**	Parameters:
+**		p -- string to translate (modified in place if possible). [A]
+**
+**	Returns:
+**		lower cased string
+**
+**	Side Effects:
+**		String p is translated to lower case if possible.
+*/
+
+char *
+makelower(p)
+	char *p;
+{
+	char c;
+	char *orig;
+
+	if (p == NULL)
+		return p;
+	orig = p;
+#if USE_EAI
+	if (!asciistr(p))
+		return (char *)sm_lowercase(p);
+#endif
+	for (; (c = *p) != '\0'; p++)
+		if (isascii(c) && isupper(c))
+			*p = tolower(c);
+	return orig;
+}
+
+#if USE_EAI
+/*
+**  SM_LOWERCASE -- lower case a UTF-8 string
+**	Note: this should ONLY be applied to a UTF-8 string,
+**	i.e., the caller should check first if it isn't an ASCII string.
+**
+**	Parameters:
+**		str -- original string
+**
+**	Returns:
+**		lower case version of string [S]
+**
+**	How to return an error description due to failed unicode calls?
+**	However, is that even relevant?
+*/
+
+char *
+sm_lowercase(str)
+	const char *str;
+{
+	int olen, ilen;
+	UErrorCode error;
+	ssize_t req;
+	int n;
+	static UCaseMap *csm = NULL;
+	static char *out = NULL;
+	static int outlen = 0;
+
+# if SM_CHECK_REQUIRE
+	if (sm_debug_active(&SmExpensiveRequire, 3))
+		SM_REQUIRE(!asciistr(str));
+# endif
+	/* an empty string is always ASCII */
+	SM_REQUIRE(NULL != str && '\0' != *str);
+
+	if (NULL == csm)
+	{
+		error = U_ZERO_ERROR;
+		csm = ucasemap_open("en_US", U_FOLD_CASE_DEFAULT, &error);
+		if (U_SUCCESS(error) == 0)
+		{
+			/* syserr("ucasemap_open error: %s", u_errorName(error)); */
+			return NULL;
+		}
+	}
+
+	ilen = strlen(str);
+	olen = ilen + 1;
+	if (olen > outlen)
+	{
+		outlen = olen;
+		out = sm_realloc_x(out, outlen);
+	}
+
+	for (n = 0; n < 3; n++)
+	{
+		error = U_ZERO_ERROR;
+		req = ucasemap_utf8FoldCase(csm, out, olen, str, ilen, &error);
+		if (U_SUCCESS(error))
+		{
+			if (req >= olen)
+			{
+				outlen = req + 1;
+				out = sm_realloc_x(out, outlen);
+				out[req] = '\0';
+			}
+			break;
+		}
+		else if (error == U_BUFFER_OVERFLOW_ERROR)
+		{
+			outlen = req + 1;
+			out = sm_realloc_x(out, outlen);
+			olen = outlen;
+		}
+		else
+		{
+			/* syserr("conversion error for \"%s\": %s", str, u_errorName(error)); */
+			return NULL;
+		}
+	}
+	return out;
+}
+#endif /* USE_EAI */
diff --git a/contrib/sendmail/libsm/makebuf.c b/contrib/sendmail/libsm/makebuf.c
index 2542d2982d6a..0afdfc45256b 100644
--- a/contrib/sendmail/libsm/makebuf.c
+++ b/contrib/sendmail/libsm/makebuf.c
@@ -124,7 +124,7 @@ sm_whatbuf(fp, bufsize, couldbetty)
 			st.st_blksize = SM_IO_MAX_BUF;
 #  if SM_IO_MIN_BUF > 0
 		else
-#  endif /* SM_IO_MIN_BUF > 0 */
+#  endif
 # endif /* SM_IO_MAX_BUF > 0 */
 # if SM_IO_MIN_BUF > 0
 		if (st.st_blksize < SM_IO_MIN_BUF)
diff --git a/contrib/sendmail/libsm/mbdb.c b/contrib/sendmail/libsm/mbdb.c
index ff2d3d8150fa..86f3a2d6e5f8 100644
--- a/contrib/sendmail/libsm/mbdb.c
+++ b/contrib/sendmail/libsm/mbdb.c
@@ -27,16 +27,11 @@ SM_RCSID("@(#)$Id: mbdb.c,v 1.43 2014-01-08 17:03:15 ca Exp $")
 #include 
 #include 
 #include 
-# ifdef EX_OK
-#  undef EX_OK			/* for SVr4.2 SMP */
-# endif
 #include 
 
-#if LDAPMAP
-# if _LDAP_EXAMPLE_
-#  include 
-# endif
-#endif /* LDAPMAP */
+#if LDAPMAP && _LDAP_EXAMPLE_
+# include 
+#endif
 
 typedef struct
 {
@@ -50,23 +45,19 @@ static int	mbdb_pw_initialize __P((char *));
 static int	mbdb_pw_lookup __P((char *name, SM_MBDB_T *user));
 static void	mbdb_pw_terminate __P((void));
 
-#if LDAPMAP
-# if _LDAP_EXAMPLE_
+#if LDAPMAP && _LDAP_EXAMPLE_
 static struct sm_ldap_struct LDAPLMAP;
 static int	mbdb_ldap_initialize __P((char *));
 static int	mbdb_ldap_lookup __P((char *name, SM_MBDB_T *user));
 static void	mbdb_ldap_terminate __P((void));
-# endif /* _LDAP_EXAMPLE_ */
-#endif /* LDAPMAP */
+#endif /* LDAPMAP && _LDAP_EXAMPLE_ */
 
 static SM_MBDB_TYPE_T SmMbdbTypes[] =
 {
 	{ "pw", mbdb_pw_initialize, mbdb_pw_lookup, mbdb_pw_terminate },
-#if LDAPMAP
-# if _LDAP_EXAMPLE_
+#if LDAPMAP && _LDAP_EXAMPLE_
 	{ "ldap", mbdb_ldap_initialize, mbdb_ldap_lookup, mbdb_ldap_terminate },
-# endif
-#endif /* LDAPMAP */
+#endif
 	{ NULL, NULL, NULL, NULL }
 };
 
@@ -268,6 +259,7 @@ sm_pwfullname(gecos, user, buf, buflen)
 				*bp++ = Latin1ToASCII[(unsigned char) *p - 128];
 			else
 #endif
+				/* "else" in #if code above */
 				*bp++ = *p;
 		}
 	}
@@ -365,8 +357,7 @@ mbdb_pw_terminate()
 	endpwent();
 }
 
-#if LDAPMAP
-# if _LDAP_EXAMPLE_
+#if LDAPMAP && _LDAP_EXAMPLE_
 /*
 **  LDAP example implementation based on RFC 2307, "An Approach for Using
 **  LDAP as a Network Information Service":
@@ -404,19 +395,19 @@ mbdb_pw_terminate()
 **
 */
 
-#  define MBDB_LDAP_LABEL		"MailboxDatabase"
+# define MBDB_LDAP_LABEL		"MailboxDatabase"
 
-#  ifndef MBDB_LDAP_FILTER
-#   define MBDB_LDAP_FILTER		"(&(objectClass=posixAccount)(uid=%0))"
-#  endif
+# ifndef MBDB_LDAP_FILTER
+#  define MBDB_LDAP_FILTER		"(&(objectClass=posixAccount)(uid=%0))"
+# endif
 
-#  ifndef MBDB_DEFAULT_LDAP_BASEDN
-#   define MBDB_DEFAULT_LDAP_BASEDN	NULL
-#  endif
+# ifndef MBDB_DEFAULT_LDAP_BASEDN
+#  define MBDB_DEFAULT_LDAP_BASEDN	NULL
+# endif
 
-#  ifndef MBDB_DEFAULT_LDAP_SERVER
-#   define MBDB_DEFAULT_LDAP_SERVER	NULL
-#  endif
+# ifndef MBDB_DEFAULT_LDAP_SERVER
+#  define MBDB_DEFAULT_LDAP_SERVER	NULL
+# endif
 
 /*
 **  MBDB_LDAP_INITIALIZE -- initialize LDAP version
@@ -526,13 +517,13 @@ mbdb_ldap_lookup(name, user)
 	if (msgid == -1)
 	{
 		save_errno = sm_ldap_geterrno(LDAPLMAP.ldap_ld) + E_LDAPBASE;
-#  ifdef LDAP_SERVER_DOWN
+# ifdef LDAP_SERVER_DOWN
 		if (errno == LDAP_SERVER_DOWN)
 		{
 			/* server disappeared, try reopen on next search */
 			sm_ldap_close(&LDAPLMAP);
 		}
-#  endif /* LDAP_SERVER_DOWN */
+# endif /* LDAP_SERVER_DOWN */
 		errno = save_errno;
 		return EX_TEMPFAIL;
 	}
@@ -776,5 +767,4 @@ mbdb_ldap_terminate()
 {
 	sm_ldap_close(&LDAPLMAP);
 }
-# endif /* _LDAP_EXAMPLE_ */
-#endif /* LDAPMAP */
+#endif /* LDAPMAP && _LDAP_EXAMPLE_ */
diff --git a/contrib/sendmail/libsm/memstat.c b/contrib/sendmail/libsm/memstat.c
index 4ef49fad31d5..88b205821668 100644
--- a/contrib/sendmail/libsm/memstat.c
+++ b/contrib/sendmail/libsm/memstat.c
@@ -79,7 +79,7 @@ sm_memstat_get(resource, pvalue)
 		return (errno != 0) ? errno : -1;
 	r = ai.ani_max - ai.ani_resv;
 	r *= sc_page_size >> 10;
-   	*pvalue = r;
+	*pvalue = r;
 	return 0;
 }
 
@@ -167,7 +167,7 @@ sm_memstat_get(resource, pvalue)
 			(resource != NULL) ? resource: "freemem");
 	if (kn == NULL)
 		return (errno != 0) ? errno : -3;
-   	*pvalue = kn->value.ul;
+	*pvalue = kn->value.ul;
 	return 0;
 }
 
diff --git a/contrib/sendmail/libsm/niprop.c b/contrib/sendmail/libsm/niprop.c
index a5056deb34c6..9d8653362ac4 100644
--- a/contrib/sendmail/libsm/niprop.c
+++ b/contrib/sendmail/libsm/niprop.c
@@ -95,11 +95,11 @@ ni_propval(keydir, keyprop, keyval, valprop, sepchar)
 	}
 	(void) sm_strlcat(keybuf, keyval, sizeof keybuf);
 
-#if 0
+# if 0
 	if (tTd(38, 21))
 		sm_dprintf("ni_propval(%s, %s, %s, %s, %d) keybuf='%s'\n",
 			keydir, keyprop, keyval, valprop, sepchar, keybuf);
-#endif
+# endif
 
 	/*
 	**  If the passed directory and property name are found
@@ -114,10 +114,10 @@ ni_propval(keydir, keyprop, keyval, valprop, sepchar)
 		if (i == 0)
 		{
 			nis = ni_open(NULL, LOCAL_NETINFO_DOMAIN, &ni);
-#if 0
+# if 0
 			if (tTd(38, 20))
 				sm_dprintf("ni_open(LOCAL) = %d\n", nis);
-#endif
+# endif
 		}
 		else
 		{
@@ -125,10 +125,10 @@ ni_propval(keydir, keyprop, keyval, valprop, sepchar)
 				ni_free(lastni);
 			lastni = ni;
 			nis = ni_open(lastni, PARENT_NETINFO_DOMAIN, &ni);
-#if 0
+# if 0
 			if (tTd(38, 20))
 				sm_dprintf("ni_open(PARENT) = %d\n", nis);
-#endif
+# endif
 		}
 
 		/*
@@ -156,11 +156,11 @@ ni_propval(keydir, keyprop, keyval, valprop, sepchar)
 		if (ni_lookupprop(ni, &nid, valprop, &ninl) != 0)
 			continue;
 
-#if 0
+# if 0
 		if (tTd(38, 20))
 			sm_dprintf("ni_lookupprop: len=%d\n",
 				ninl.ni_namelist_len);
-#endif
+# endif
 
 		/*
 		**  See if we have an acceptable number of values.
@@ -203,10 +203,10 @@ ni_propval(keydir, keyprop, keyval, valprop, sepchar)
 		ni_free(ni);
 	if (lastni != NULL && ni != lastni)
 		ni_free(lastni);
-#if 0
+# if 0
 	if (tTd(38, 20))
 		sm_dprintf("ni_propval returns: '%s'\n", propval);
-#endif
+# endif
 
 	return propval;
 }
diff --git a/contrib/sendmail/libsm/notify.c b/contrib/sendmail/libsm/notify.c
index 9f4cca27d7a8..4ed3fd7f2bd1 100644
--- a/contrib/sendmail/libsm/notify.c
+++ b/contrib/sendmail/libsm/notify.c
@@ -10,6 +10,14 @@
 
 #include 
 
+#if _FFR_DMTRIGGER
+#include 	/* FDSET_CAST */
+#include 
+#include 
+#include 
+#include 
+#include 
+
 #include 
 #include 
 #include 
@@ -20,11 +28,6 @@
 #include 
 #include 	/* for memset() */
 
-#include 	/* FDSET_CAST */
-#include 
-#include 
-#include 
-
 #if SM_NOTIFY_DEBUG
 #define SM_DBG(p)	fprintf p
 #else
@@ -126,6 +129,9 @@ sm_notify_stop(owner, flags)
 **		<0: -errno
 */
 
+#define MAX_NETSTR 1024
+#define NETSTRPRE 5
+
 int
 sm_notify_snd(buf, buflen)
 	char *buf;
@@ -133,13 +139,18 @@ sm_notify_snd(buf, buflen)
 {
 	int r;
 	int save_errno;
+	size_t len;
+	char netstr[MAX_NETSTR];
 
 	SM_REQUIRE(buf != NULL);
 	SM_REQUIRE(buflen > 0);
 	if (NotifyWRpipe < 0)
 		return -EINVAL;
+	if (buflen >= MAX_NETSTR - 7)
+		return -E2BIG;	/* XXX "TOO LARGE"? */
 
-	r = write(NotifyWRpipe, buf, buflen);
+	len = sm_snprintf(netstr, sizeof(netstr), "%04d:%s,", (int)buflen, buf);
+	r = write(NotifyWRpipe, netstr, len);
 	save_errno = errno;
 	SM_DBG((stderr, "write=%d, fd=%d, e=%d\n", r, NotifyWRpipe, save_errno));
 	return r >= 0 ? 0 : -save_errno;
@@ -151,7 +162,7 @@ sm_notify_snd(buf, buflen)
 **	Parameters:
 **		buf -- where to write data
 **		buflen -- len of buffer
-**		tmo -- timeout
+**		tmo -- timeout (micro seconds)
 **
 **	Returns:
 **		0: success
@@ -162,24 +173,30 @@ int
 sm_notify_rcv(buf, buflen, tmo)
 	char *buf;
 	size_t buflen;
-	int tmo;
+	long tmo;
 {
-	int r;
+	int r, len;
 	int save_errno;
 	fd_set readfds;
-	struct timeval timeout;
+	struct timeval timeout, *tval;
 
 	SM_REQUIRE(buf != NULL);
-	SM_REQUIRE(buflen > 0);
+	SM_REQUIRE(buflen > NETSTRPRE + 2);
 	if (NotifyRDpipe < 0)
 		return -EINVAL;
 	FD_ZERO(&readfds);
 	SM_FD_SET(NotifyRDpipe, &readfds);
-	timeout.tv_sec = tmo;
-	timeout.tv_usec = 0;
+	if (tmo < 0)
+		tval = NULL;
+	else
+	{
+		timeout.tv_sec = (long) (tmo / SM_MICROS);
+		timeout.tv_usec = tmo % SM_MICROS;
+		tval = &timeout;
+	}
 
 	do {
-		r = select(NotifyRDpipe + 1, FDSET_CAST &readfds, NULL, NULL, &timeout);
+		r = select(NotifyRDpipe + 1, FDSET_CAST &readfds, NULL, NULL, tval);
 		save_errno = errno;
 		SM_DBG((stderr, "select=%d, fd=%d, e=%d\n", r, NotifyRDpipe, save_errno));
 	} while (r < 0 && save_errno == EINTR);
@@ -194,12 +211,30 @@ sm_notify_rcv(buf, buflen, tmo)
 	if (!FD_ISSET(NotifyRDpipe, &readfds))
 		return -ETIMEDOUT;
 
-	r = read(NotifyRDpipe, buf, buflen);
+	r = read(NotifyRDpipe, buf, NETSTRPRE);
+	if (NETSTRPRE != r)
+		return -1;	/* ??? */
+
+	if (sm_io_sscanf(buf, "%4u:", &len) != 1)
+		return -EINVAL;	/* ??? */
+	if (len >= MAX_NETSTR)
+		return -E2BIG;	/* ??? */
+	if (len >= buflen - 1)
+		return -E2BIG;	/* ??? */
+	if (len <= 0)
+		return -EINVAL;	/* ??? */
+	r = read(NotifyRDpipe, buf, len + 1);
 	save_errno = errno;
 	SM_DBG((stderr, "read=%d, e=%d\n", r, save_errno));
 	if (r == 0)
 		return -1;	/* ??? */
 	if (r < 0)
 		return -save_errno;
+	if (len + 1 != r)
+		return -1;	/* ??? */
+	if (buf[len] != ',')
+		return -EINVAL;	/* ??? */
+	buf[len] = '\0';
 	return r;
 }
+#endif /* _FFR_DMTRIGGER */
diff --git a/contrib/sendmail/libsm/refill.c b/contrib/sendmail/libsm/refill.c
index af3a84442f17..6dbb71ffe822 100644
--- a/contrib/sendmail/libsm/refill.c
+++ b/contrib/sendmail/libsm/refill.c
@@ -80,7 +80,7 @@ static int sm_lflush __P((SM_FILE_T *, int *));
 	do								\
 	{								\
 		(sel_ret) = select((fd) + 1, &sm_io_to_mask, NULL,	\
-				&sm_io_x_mask, (to));			\
+				   &sm_io_x_mask, (to));		\
 	} while ((sel_ret) < 0 && errno == EINTR);			\
 	if ((sel_ret) < 0)						\
 	{								\
diff --git a/contrib/sendmail/libsm/rpool.c b/contrib/sendmail/libsm/rpool.c
index 9d883ffca194..8e21877b9526 100644
--- a/contrib/sendmail/libsm/rpool.c
+++ b/contrib/sendmail/libsm/rpool.c
@@ -508,17 +508,44 @@ sm_rpool_attach_x(rpool, rfree, rcontext)
 */
 
 char *
-sm_rpool_strdup_x(rpool, s)
+# if SM_HEAP_CHECK > 2
+sm_rpool_strdup_tagged_x
+# else
+sm_rpool_strdup_x
+# endif
+	(rpool, s
+# if SM_HEAP_CHECK > 2
+	, tag, line, group
+# endif
+	)
 	SM_RPOOL_T *rpool;
 	const char *s;
+# if SM_HEAP_CHECK > 2
+	char *tag;
+	int line;
+	int group;
+# else
+#  define tag  "sm_rpool_strdup_x"
+#  define line 1
+#  define group 1
+# endif
 {
 	size_t l;
 	char *n;
 
 	l = strlen(s);
 	SM_ASSERT(l + 1 > l);
+# if SM_HEAP_CHECK
+	n = sm_rpool_malloc_tagged_x(rpool, l + 1, tag, line, group);
+# else
 	n = sm_rpool_malloc_x(rpool, l + 1);
+# endif
 	sm_strlcpy(n, s, l + 1);
 	return n;
 }
+# if SM_HEAP_CHECK <= 2
+#  undef tag
+#  undef line
+#  undef group
+# endif
 #endif /* DO_NOT_USE_STRCPY */
diff --git a/contrib/sendmail/libsm/sem.c b/contrib/sendmail/libsm/sem.c
index ac28a61ae353..201812e3a290 100644
--- a/contrib/sendmail/libsm/sem.c
+++ b/contrib/sendmail/libsm/sem.c
@@ -13,10 +13,11 @@ SM_RCSID("@(#)$Id: sem.c,v 1.15 2013-11-22 20:51:43 ca Exp $")
 #if SM_CONF_SEM
 # include 
 # include 
-# include 
-# include 
-# include 
 # include 
+# include 
+# include 
+# include 
+# include 
 
 /*
 **  SM_SEM_START -- initialize semaphores
@@ -156,10 +157,10 @@ sm_sem_rel(semid, semnum, timeout)
 	int r;
 	struct sembuf semops[1];
 
-#if PARANOID
+# if PARANOID
 	/* XXX should we check whether the value is already 0 ? */
 	SM_REQUIRE(sm_get_sem(semid, semnum) > 0);
-#endif
+# endif
 
 	semops[0].sem_num = semnum;
 	semops[0].sem_op = 1;
@@ -216,12 +217,17 @@ sm_sem_get(semid, semnum)
 **		< 0 on failure.
 */
 
+#ifdef __STDC__
+int
+sm_semsetowner(int semid, uid_t uid, gid_t gid, MODE_T mode)
+#else /* __STDC__ */
 int
 sm_semsetowner(semid, uid, gid, mode)
 	int semid;
 	uid_t uid;
 	gid_t gid;
-	mode_t mode;
+	MODE_T mode;
+#endif /* __STDC__ */
 {
 	int r;
 	struct semid_ds	semidds;
diff --git a/contrib/sendmail/libsm/shm.c b/contrib/sendmail/libsm/shm.c
index ac3919d5723f..716d5bc05927 100644
--- a/contrib/sendmail/libsm/shm.c
+++ b/contrib/sendmail/libsm/shm.c
@@ -15,6 +15,7 @@ SM_RCSID("@(#)$Id: shm.c,v 1.20 2013-11-22 20:51:43 ca Exp $")
 # include 
 # include 
 # include 
+# include 
 # include 
 
 
@@ -119,12 +120,17 @@ sm_shmstop(shm, shmid, owner)
 **		< 0 on failure.
 */
 
+#ifdef __STDC__
+int
+sm_shmsetowner(int shmid, uid_t uid, gid_t gid, MODE_T mode)
+#else /* __STDC__ */
 int
 sm_shmsetowner(shmid, uid, gid, mode)
 	int shmid;
 	uid_t uid;
 	gid_t gid;
-	mode_t mode;
+	MODE_T mode;
+#endif /* __STDC__ */
 {
 	int r;
 	struct shmid_ds shmid_ds;
diff --git a/contrib/sendmail/libsm/signal.c b/contrib/sendmail/libsm/signal.c
index bfbeaf555c2e..352638ec9288 100644
--- a/contrib/sendmail/libsm/signal.c
+++ b/contrib/sendmail/libsm/signal.c
@@ -40,41 +40,41 @@ sm_signal(sig, handler)
 	int sig;
 	sigfunc_t handler;
 {
-# if defined(SA_RESTART) || (!defined(SYS5SIGNALS) && !defined(BSD4_3))
+#if defined(SA_RESTART) || (!defined(SYS5SIGNALS) && !defined(BSD4_3))
 	struct sigaction n, o;
-# endif
+#endif
 
 	/*
 	**  First, try for modern signal calls
 	**  and restartable syscalls
 	*/
 
-# ifdef SA_RESTART
+#ifdef SA_RESTART
 	(void) memset(&n, '\0', sizeof n);
-#  if USE_SA_SIGACTION
+# if USE_SA_SIGACTION
 	n.sa_sigaction = (void(*)(int, siginfo_t *, void *)) handler;
 	n.sa_flags = SA_RESTART|SA_SIGINFO;
-#  else
+# else
 	n.sa_handler = handler;
 	n.sa_flags = SA_RESTART;
-#  endif
+# endif
 	if (sigaction(sig, &n, &o) < 0)
 		return SIG_ERR;
 	return o.sa_handler;
-# else /* SA_RESTART */
+#else /* SA_RESTART */
 
 	/*
 	**  Else check for SYS5SIGNALS or
 	**  BSD4_3 signals
 	*/
 
-#  if defined(SYS5SIGNALS) || defined(BSD4_3)
-#   ifdef BSD4_3
+# if defined(SYS5SIGNALS) || defined(BSD4_3)
+#  ifdef BSD4_3
 	return signal(sig, handler);
-#   else
+#  else
 	return sigset(sig, handler);
-#   endif
-#  else /* defined(SYS5SIGNALS) || defined(BSD4_3) */
+#  endif
+# else /* defined(SYS5SIGNALS) || defined(BSD4_3) */
 
 	/*
 	**  Finally, if nothing else is available,
@@ -86,8 +86,8 @@ sm_signal(sig, handler)
 	if (sigaction(sig, &n, &o) < 0)
 		return SIG_ERR;
 	return o.sa_handler;
-#  endif /* defined(SYS5SIGNALS) || defined(BSD4_3) */
-# endif /* SA_RESTART */
+# endif /* defined(SYS5SIGNALS) || defined(BSD4_3) */
+#endif /* SA_RESTART */
 }
 /*
 **  SM_BLOCKSIGNAL -- hold a signal to prevent delivery
@@ -105,13 +105,13 @@ int
 sm_blocksignal(sig)
 	int sig;
 {
-# ifdef BSD4_3
-#  ifndef sigmask
-#   define sigmask(s)	(1 << ((s) - 1))
-#  endif
+#ifdef BSD4_3
+# ifndef sigmask
+#  define sigmask(s)	(1 << ((s) - 1))
+# endif
 	return (sigblock(sigmask(sig)) & sigmask(sig)) != 0;
-# else /* BSD4_3 */
-#  ifdef ALTOS_SYSTEM_V
+#else /* BSD4_3 */
+# ifdef ALTOS_SYSTEM_V
 	sigfunc_t handler;
 
 	handler = sigset(sig, SIG_HOLD);
@@ -119,7 +119,7 @@ sm_blocksignal(sig)
 		return -1;
 	else
 		return handler == SIG_HOLD;
-#  else /* ALTOS_SYSTEM_V */
+# else /* ALTOS_SYSTEM_V */
 	sigset_t sset, oset;
 
 	(void) sigemptyset(&sset);
@@ -128,8 +128,8 @@ sm_blocksignal(sig)
 		return -1;
 	else
 		return sigismember(&oset, sig);
-#  endif /* ALTOS_SYSTEM_V */
-# endif /* BSD4_3 */
+# endif /* ALTOS_SYSTEM_V */
+#endif /* BSD4_3 */
 }
 /*
 **  SM_RELEASESIGNAL -- release a held signal
@@ -147,10 +147,10 @@ int
 sm_releasesignal(sig)
 	int sig;
 {
-# ifdef BSD4_3
+#ifdef BSD4_3
 	return (sigsetmask(sigblock(0) & ~sigmask(sig)) & sigmask(sig)) != 0;
-# else /* BSD4_3 */
-#  ifdef ALTOS_SYSTEM_V
+#else /* BSD4_3 */
+# ifdef ALTOS_SYSTEM_V
 	sigfunc_t handler;
 
 	handler = sigset(sig, SIG_HOLD);
@@ -158,7 +158,7 @@ sm_releasesignal(sig)
 		return -1;
 	else
 		return handler == SIG_HOLD;
-#  else /* ALTOS_SYSTEM_V */
+# else /* ALTOS_SYSTEM_V */
 	sigset_t sset, oset;
 
 	(void) sigemptyset(&sset);
@@ -167,8 +167,8 @@ sm_releasesignal(sig)
 		return -1;
 	else
 		return sigismember(&oset, sig);
-#  endif /* ALTOS_SYSTEM_V */
-# endif /* BSD4_3 */
+# endif /* ALTOS_SYSTEM_V */
+#endif /* BSD4_3 */
 }
 /*
 **  PEND_SIGNAL -- Add a signal to the pending signal list
@@ -263,10 +263,10 @@ void
 sm_allsignals(block)
 	bool block;
 {
-# ifdef BSD4_3
-#  ifndef sigmask
-#   define sigmask(s)	(1 << ((s) - 1))
-#  endif
+#ifdef BSD4_3
+# ifndef sigmask
+#  define sigmask(s)	(1 << ((s) - 1))
+# endif
 	if (block)
 	{
 		int mask = 0;
@@ -282,8 +282,8 @@ sm_allsignals(block)
 	}
 	else
 		sigsetmask(0);
-# else /* BSD4_3 */
-#  ifdef ALTOS_SYSTEM_V
+#else /* BSD4_3 */
+# ifdef ALTOS_SYSTEM_V
 	if (block)
 	{
 		(void) sigset(SIGALRM, SIG_HOLD);
@@ -302,7 +302,7 @@ sm_allsignals(block)
 		(void) sigset(SIGTERM, SIG_DFL);
 		(void) sigset(SIGUSR1, SIG_DFL);
 	}
-#  else /* ALTOS_SYSTEM_V */
+# else /* ALTOS_SYSTEM_V */
 	sigset_t sset;
 
 	(void) sigemptyset(&sset);
@@ -313,8 +313,8 @@ sm_allsignals(block)
 	(void) sigaddset(&sset, SIGTERM);
 	(void) sigaddset(&sset, SIGUSR1);
 	(void) sigprocmask(block ? SIG_BLOCK : SIG_UNBLOCK, &sset, NULL);
-#  endif /* ALTOS_SYSTEM_V */
-# endif /* BSD4_3 */
+# endif /* ALTOS_SYSTEM_V */
+#endif /* BSD4_3 */
 }
 /*
 **  SM_SIGNAL_NOOP -- A signal no-op function
diff --git a/contrib/sendmail/libsm/strcaseeq.c b/contrib/sendmail/libsm/strcaseeq.c
new file mode 100644
index 000000000000..c252d85e53e0
--- /dev/null
+++ b/contrib/sendmail/libsm/strcaseeq.c
@@ -0,0 +1,114 @@
+/*
+ * Copyright (c) 2020 Proofpoint, Inc. and its suppliers.
+ *	All rights reserved.
+ *
+ * By using this file, you agree to the terms and conditions set
+ * forth in the LICENSE file which can be found at the top level of
+ * the sendmail distribution.
+ *
+ */
+
+#include 
+#include 
+
+#if USE_EAI
+#include 
+#include 
+#include 
+
+/*
+**  SM_STRCASEEQ -- are two strings equal (case-insenstive)?
+**
+**	Parameters:
+**		s1 -- string
+**		s2 -- string
+**
+**	Returns:
+**		true iff s1 == s2
+*/
+
+bool
+sm_strcaseeq(s1, s2)
+	const char *s1;
+	const char *s2;
+{
+	char *l1, *l2;
+	char *f1;
+	bool same;
+
+	if (asciistr(s1))
+	{
+		if (!asciistr(s2))
+			return false;
+		return (sm_strcasecmp(s1, s2) == 0);
+	}
+	if (asciistr(s2))
+		return false;
+	l1 = sm_lowercase(s1);
+	if (l1 != s1)
+	{
+		f1 = sm_strdup_x(l1);
+		l1 = f1;
+	}
+	else
+		f1 = NULL;
+	l2 = sm_lowercase(s2);
+
+	while (*l1 == *l2 && '\0' != *l1)
+		l1++, l2++;
+	same = *l1 == *l2;
+
+	SM_FREE(f1);
+	return same;
+}
+
+/*
+**  SM_STRNCASEEQ -- are two strings (up to a length) equal (case-insenstive)?
+**
+**	Parameters:
+**		s1 -- string
+**		s2 -- string
+**		n -- maximum length to compare
+**
+**	Returns:
+**		true iff s1 == s2 (for up to the first n char)
+*/
+
+bool
+sm_strncaseeq(s1, s2, n)
+	const char *s1;
+	const char *s2;
+	size_t n;
+{
+	char *l1, *l2;
+	char *f1;
+	bool same;
+
+	if (0 == n)
+		return true;
+	if (asciistr(s1))
+	{
+		if (!asciistr(s2))
+			return false;
+		return (sm_strncasecmp(s1, s2, n) == 0);
+	}
+	if (asciistr(s2))
+		return false;
+	l1 = sm_lowercase(s1);
+	if (l1 != s1)
+	{
+		f1 = sm_strdup_x(l1);
+		l1 = f1;
+	}
+	else
+		f1 = NULL;
+	l2 = sm_lowercase(s2);
+
+	while (*l1 == *l2 && '\0' != *l1 && n-- > 0)
+		l1++, l2++;
+	same = *l1 == *l2;
+
+	SM_FREE(f1);
+	return same;
+}
+#endif /* USE_EAI */
diff --git a/contrib/sendmail/libsm/string.c b/contrib/sendmail/libsm/string.c
index 83577f19a84e..9820d93dfe95 100644
--- a/contrib/sendmail/libsm/string.c
+++ b/contrib/sendmail/libsm/string.c
@@ -27,9 +27,6 @@ SM_RCSID("@(#)$Id: string.c,v 1.4 2013-11-22 20:51:43 ca Exp $")
 **
 **	Returns:
 **		none.
-**
-**	Side Effects:
-**		none.
 */
 
 void
diff --git a/contrib/sendmail/libsm/stringf.c b/contrib/sendmail/libsm/stringf.c
index 41b8898542ee..128571d6d12d 100644
--- a/contrib/sendmail/libsm/stringf.c
+++ b/contrib/sendmail/libsm/stringf.c
@@ -71,7 +71,7 @@ sm_stringf_x(fmt, va_alist)
 char *
 sm_vstringf_x(fmt, ap)
 	const char *fmt;
-	SM_VA_LOCAL_DECL
+	va_list ap;
 {
 	char *s;
 
diff --git a/contrib/sendmail/libsm/t-ixlen.c b/contrib/sendmail/libsm/t-ixlen.c
new file mode 100644
index 000000000000..cc29431725a8
--- /dev/null
+++ b/contrib/sendmail/libsm/t-ixlen.c
@@ -0,0 +1,105 @@
+/*
+ * Copyright (c) 2020 Proofpoint, Inc. and its suppliers.
+ *	All rights reserved.
+ *
+ * By using this file, you agree to the terms and conditions set
+ * forth in the LICENSE file which can be found at the top level of
+ * the sendmail distribution.
+ */
+
+#include 
+SM_IDSTR(id, "@(#)$Id: t-qic.c,v 1.10 2013-11-22 20:51:43 ca Exp $")
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+#if _FFR_8BITENVADDR
+extern bool SmTestVerbose;
+
+static void
+chkilenx(str, len)
+	const char *str;
+	int len;
+{
+	int xlen;
+
+	xlen = ilenx(str);
+	SM_TEST(len == xlen);
+	if (len != xlen)
+		fprintf(stderr, "str=\"%s\", len=%d, excpected=%d\n",
+			str, xlen, len);
+}
+
+static void
+chkxleni(str, len)
+	const char *str;
+	int len;
+{
+	int ilen;
+
+	ilen = xleni(str);
+	SM_TEST(len == ilen);
+	if (len != ilen)
+		fprintf(stderr, "str=\"%s\", len=%d, excpected=%d\n",
+			str, ilen, len);
+}
+
+
+static void
+usage(prg)
+	const char *prg;
+{
+	fprintf(stderr, "usage: %s [options]\n", prg);
+	fprintf(stderr, "options:\n");
+	fprintf(stderr, "-x    xleni\n");
+}
+
+int
+main(argc, argv)
+	int argc;
+	char *argv[];
+{
+	int o, len;
+	bool x;
+	char line[1024];
+
+	x = false;
+	while ((o = getopt(argc, argv, "x")) != -1)
+	{
+		switch ((char) o)
+		{
+		  case 'x':
+			x = true;
+			break;
+
+		  default:
+			usage(argv[0]);
+			exit(1);
+		}
+	}
+
+	sm_test_begin(argc, argv, "test ilenx");
+
+	while (fscanf(stdin, "%d:%s\n", &len, line) == 2)
+	{
+		if (x)
+			chkxleni(line, len);
+		else
+			chkilenx(line, len);
+	}
+
+	return sm_test_end();
+}
+#else /* _FFR_8BITENVADDR */
+int
+main(argc, argv)
+	int argc;
+	char *argv[];
+{
+	return 0;
+}
+#endif /* _FFR_8BITENVADDR */
diff --git a/contrib/sendmail/libsm/t-ixlen.sh b/contrib/sendmail/libsm/t-ixlen.sh
new file mode 100755
index 000000000000..6c5cdf771d10
--- /dev/null
+++ b/contrib/sendmail/libsm/t-ixlen.sh
@@ -0,0 +1,41 @@
+#!/bin/sh
+# Copyright (c) 2020 Proofpoint, Inc. and its suppliers.
+#	All rights reserved.
+#
+# By using this file, you agree to the terms and conditions set
+# forth in the LICENSE file which can be found at the top level of
+# the sendmail distribution.
+#
+# ----------------------------------------
+# test ilenx() and xleni(), using t-ixlen
+# ----------------------------------------
+
+PRG=./t-ixlen
+R=0
+${PRG} <
 
+#if _FFR_DMTRIGGER || _FFR_NOTIFY
 # include 
 # include 
-
+# include 
 # include 
 # include 
 # include 
 # include 
-
-# define MAX_CNT	10
+# include 
 
 /*
-**  MSGTEST -- test of message queue.
+**  NOTIFY_WR -- test of notify feature
 **
 **	Parameters:
-**		owner -- create message queue.
+**		pid -- pid of process
 **
 **	Returns:
 **		0 on success
-**		< 0 on failure.
+**		< 0 on failure
 */
 
 static int
-notifytest(owner)
-	int owner;
+notify_wr(pid)
+	pid_t pid;
 {
 	int r;
 	size_t len;
 	char buf[64];
 #define TSTSTR "qf0001"
 
-	r = sm_notify_start(owner, 0);
+	r = sm_notify_start(false, 0);
 	if (r < 0)
 	{
 		perror("sm_notify_start failed");
 		return -1;
 	}
 
-	if (!owner)
-	{
-		len = sm_strlcpy(buf, TSTSTR, sizeof(buf));
-		r = sm_notify_snd(buf, len);
-		SM_TEST(r >= 0);
-		if (r < 0)
-			goto end;
+	len = sm_snprintf(buf, sizeof(buf), "%s-%ld", TSTSTR, (long) pid);
+	r = sm_notify_snd(buf, len);
+	SM_TEST(r >= 0);
+	return r;
+}
 
-  end:
-		return r;
-	}
-	else
+/*
+**  NOTIFY_RD -- test of notify feature
+**
+**	Parameters:
+**
+**	Returns:
+**		0 on success
+**		< 0 on failure
+*/
+
+static int
+notify_rd(nproc)
+	int nproc;
+{
+	int r, i;
+	char buf[64];
+#define TSTSTR "qf0001"
+
+	r = sm_notify_start(true, 0);
+	if (r < 0)
 	{
-		r = sm_notify_rcv(buf, sizeof(buf), 5);
+		perror("sm_notify_start failed");
+		return -1;
+	}
+
+	for (i = 0; i < nproc; i++)
+	{
+		r = sm_notify_rcv(buf, sizeof(buf), 5 * SM_MICROS);
 		SM_TEST(r >= 0);
 		if (r < 0)
+		{
+			fprintf(stderr, "rcv=%d\n", r);
 			return r;
+		}
 		if (r > 0 && r < sizeof(buf))
 			buf[r] = '\0';
 		buf[sizeof(buf) - 1] = '\0';
-		SM_TEST(strcmp(buf, TSTSTR) == 0);
+		SM_TEST(strncmp(buf, TSTSTR, sizeof(TSTSTR) - 1) == 0);
+		SM_TEST(r > sizeof(TSTSTR));
 		fprintf(stderr, "buf=\"%s\"\n", buf);
 	}
 	return 0;
@@ -79,44 +103,68 @@ main(argc, argv)
 	int argc;
 	char *argv[];
 {
-	int ch;
+	int i;
 	int r = 0;
+	int nproc = 1;
 	pid_t pid;
 
-# define OPTIONS	""
-	while ((ch = getopt(argc, argv, OPTIONS)) != -1)
+# define OPTIONS	"p:"
+	while ((i = getopt(argc, argv, OPTIONS)) != -1)
 	{
-		switch ((char) ch)
+		switch ((char) i)
 		{
+		  case 'p':
+			nproc = atoi(optarg);
+			if (nproc < 1)
+			{
+				errno = EINVAL;
+				perror("-p: must be >0\n");
+				return r;
+			}
+			break;
 		  default:
 			break;
 		}
 	}
 
+	sm_test_begin(argc, argv, "test notify");
 	r = sm_notify_init(0);
+	SM_TEST(r >= 0);
 	if (r < 0)
 	{
 		perror("sm_notify_init failed\n");
-		return -1;
+		return r;
 	}
 
-	if ((pid = fork()) < 0)
+	pid = 0;
+	for (i = 0; i < nproc; i++)
 	{
-		perror("fork failed\n");
-		return -1;
-	}
+		if ((pid = fork()) < 0)
+		{
+			perror("fork failed\n");
+			return -1;
+		}
 
-	sm_test_begin(argc, argv, "test notify");
-	if (pid == 0)
-	{
-		/* give the parent the chance to setup data */
-		sleep(1);
-		r = notifytest(false);
-	}
-	else
-	{
-		r = notifytest(true);
+		if (pid == 0)
+		{
+			/* give the parent the chance to set up data */
+			sleep(1);
+			r = notify_wr(getpid());
+			break;
+		}
 	}
+	if (pid > 0)
+		r = notify_rd(nproc);
 	SM_TEST(r >= 0);
 	return sm_test_end();
 }
+#else /* _FFR_DMTRIGGER */
+int
+main(argc, argv)
+	int argc;
+	char *argv[];
+{
+	printf("SKIPPED: no _FFR_DMTRIGGER\n");
+	return 0;
+}
+#endif /* _FFR_DMTRIGGER */
diff --git a/contrib/sendmail/libsm/t-qic.c b/contrib/sendmail/libsm/t-qic.c
index 11375750a196..fea88c95b43d 100644
--- a/contrib/sendmail/libsm/t-qic.c
+++ b/contrib/sendmail/libsm/t-qic.c
@@ -44,14 +44,15 @@ show_diff(s1, s2)
 	}
 }
 
-char *quote_unquote __P((char *, char *, int, int));
+char *quote_unquote __P((char *, char *, int, int, int));
 
 char *
-quote_unquote(in, out, outlen, exp)
+quote_unquote(in, out, outlen, exp, mode)
 	char *in;
 	char *out;
 	int outlen;
 	int exp;
+	int mode;
 {
 	char *obp, *bp;
 	char line_back[1024];
@@ -59,9 +60,14 @@ quote_unquote(in, out, outlen, exp)
 	int cmp;
 
 	sm_strlcpy(line_in, in, sizeof(line_in));
-	obp = quote_internal_chars(in, out, &outlen);
+	obp = quote_internal_chars(in, out, &outlen, NULL);
 	bp = str2prt(line_in);
-	dequote_internal_chars(obp, line_back, sizeof(line_back));
+	if (0 == mode)
+		dequote_internal_chars(obp, line_back, sizeof(line_back));
+	else if (1 == mode)
+		dequote_internal_chars(obp, line_back, strlen(obp));
+	else if (2 == mode)
+		dequote_internal_chars(obp, line_back, strlen(obp) + 1);
 	cmp = strcmp(line_in, line_back);
 	SM_TEST(exp == cmp);
 	if (cmp != exp && !SmTestVerbose)
@@ -98,11 +104,13 @@ main(argc, argv)
 	char *argv[];
 {
 	char line_in[1024], line[256], line_out[32], *obp;
-	int i, los, cmp;
+	int i, los, cmp, mode;
 	sm_qic_T inout[] = {
 		  { "", "",	0 }
 		, { "abcdef", "abcdef",	0 }
 		, { "01234567890123456789", "01234567890123456789",	0 }
+		, { "\\", "\\",	0 }
+		, { "\\A", "\\A",	0 }
 		, { "01234567890123456789\001", "01234567890123456789\001",
 			0 }
 		, { "012345\2067890123456789", "012345\377\2067890123456789",
@@ -121,6 +129,9 @@ main(argc, argv)
 	};
 
 	sm_test_begin(argc, argv, "test meta quoting");
+	mode = 0;
+	if (argc > 1)
+		mode = atoi(argv[1]);
 	for (i = 0; i < sizeof(line_out); i++)
 		line_out[i] = '\0';
 	for (i = 0; i < sizeof(line_in); i++)
@@ -135,7 +146,7 @@ main(argc, argv)
 		line_in[i] = ch;
 	}
 	los = sizeof(line_out) / 2;
-	obp = quote_unquote(line_in, line_out, los, 0);
+	obp = quote_unquote(line_in, line_out, los, 0, mode);
 	if (obp != line_out)
 		SM_FREE(obp);
 
@@ -151,7 +162,7 @@ main(argc, argv)
 		line_in[i] = ch;
 	}
 	los = sizeof(line_in);
-	obp = quote_unquote(line_in, line_in, los, 0);
+	obp = quote_unquote(line_in, line_in, los, 0, mode);
 	if (obp != line_in)
 		SM_FREE(obp);
 
@@ -159,7 +170,7 @@ main(argc, argv)
 	{
 		los = sizeof(line_out) / 2;
 		obp = quote_unquote(inout[i].qic_in, line_out, los,
-				inout[i].qic_exp);
+				inout[i].qic_exp, mode);
 		cmp = strcmp(inout[i].qic_out, obp);
 		SM_TEST(inout[i].qic_exp == cmp);
 		if (inout[i].qic_exp != cmp && !SmTestVerbose)
@@ -186,7 +197,7 @@ main(argc, argv)
 		los = sm_strlcpy(line, inout[i].qic_in, sizeof(line));
 		SM_TEST(los + 1 < sizeof(line));
 		++los;
-		obp = quote_unquote(line, line, los, inout[i].qic_exp);
+		obp = quote_unquote(line, line, los, inout[i].qic_exp, mode);
 		cmp = strcmp(inout[i].qic_out, obp);
 		SM_TEST(inout[i].qic_exp == cmp);
 		if (inout[i].qic_exp != cmp && !SmTestVerbose)
@@ -214,7 +225,7 @@ main(argc, argv)
 	{
 		los = 0;
 		obp = quote_unquote(inout[i].qic_in, NULL, los,
-				inout[i].qic_exp);
+				inout[i].qic_exp, mode);
 		SM_TEST(obp != NULL);
 		cmp = strcmp(inout[i].qic_out, obp);
 		SM_TEST(inout[i].qic_exp == cmp);
diff --git a/contrib/sendmail/libsm/t-sem.c b/contrib/sendmail/libsm/t-sem.c
index b13e159ecebe..7404dd6ae391 100644
--- a/contrib/sendmail/libsm/t-sem.c
+++ b/contrib/sendmail/libsm/t-sem.c
@@ -20,6 +20,7 @@ SM_RCSID("@(#)$Id: t-sem.c,v 1.18 2013-11-22 20:51:43 ca Exp $")
 # include 
 # include 
 # include 
+# include 
 # include 
 
 # define T_SM_SEM_KEY (4321L)
@@ -31,14 +32,14 @@ delay(t, s)
 {
 	if (t > 0)
 	{
-#if DEBUG
+# if DEBUG
 		fprintf(stderr, "sleep(%d) before %s\n", t, s);
-#endif
+# endif
 		sleep(t);
 	}
-#if DEBUG
+# if DEBUG
 	fprintf(stderr, "%s\n", s);
-#endif
+# endif
 }
 
 
diff --git a/contrib/sendmail/libsm/t-shm.c b/contrib/sendmail/libsm/t-shm.c
index a9958fe026ae..286785a2ed89 100644
--- a/contrib/sendmail/libsm/t-shm.c
+++ b/contrib/sendmail/libsm/t-shm.c
@@ -20,6 +20,7 @@ SM_RCSID("@(#)$Id: t-shm.c,v 1.23 2013-11-22 20:51:43 ca Exp $")
 # include 
 # include 
 # include 
+# include 
 # include 
 
 # define SHMSIZE	1024
diff --git a/contrib/sendmail/libsm/t-str2prt.c b/contrib/sendmail/libsm/t-str2prt.c
new file mode 100644
index 000000000000..f9baa568c10a
--- /dev/null
+++ b/contrib/sendmail/libsm/t-str2prt.c
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2006 Proofpoint, Inc. and its suppliers.
+ *	All rights reserved.
+ *
+ * By using this file, you agree to the terms and conditions set
+ * forth in the LICENSE file which can be found at the top level of
+ * the sendmail distribution.
+ */
+
+#include 
+SM_IDSTR(id, "@(#)$Id: t-qic.c,v 1.10 2013-11-22 20:51:43 ca Exp $")
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+extern bool SmTestVerbose;
+
+struct sm_qic_S
+{
+	char		*qic_in;
+	char		*qic_out;
+	int		 qic_exp;
+};
+typedef struct sm_qic_S sm_qic_T;
+
+int
+main(argc, argv)
+	int argc;
+	char *argv[];
+{
+	char *obp;
+	int i, cmp;
+	sm_qic_T inout[] = {
+		  { "", "",	0 }
+		, { "abcdef", "abcdef",	0 }
+		, { "01234567890123456789", "01234567890123456789",	0 }
+		, { "\\", "\\\\",	0 }
+		, { "\\001", "\\\\001",	0 }
+		, { "01234567890123456789\\001", "01234567890123456789\\\\001",
+			0 }
+		, { NULL, NULL,	0 }
+	};
+
+	sm_test_begin(argc, argv, "test meta quoting");
+	for (i = 0; inout[i].qic_in != NULL; i++)
+	{
+		obp = str2prt(inout[i].qic_in);
+		cmp = strcmp(inout[i].qic_out, obp);
+		SM_TEST(inout[i].qic_exp == cmp);
+		if (inout[i].qic_exp != cmp && SmTestVerbose)
+		{
+			fprintf(stderr, "in: %s\n", inout[i].qic_in);
+			fprintf(stderr, "got: %s\n", obp);
+			fprintf(stderr, "exp: %s\n", inout[i].qic_out);
+			fprintf(stderr, "cmp=%d\n", cmp);
+		}
+	}
+
+	return sm_test_end();
+}
diff --git a/contrib/sendmail/libsm/t-streq.c b/contrib/sendmail/libsm/t-streq.c
new file mode 100644
index 000000000000..a193eca5f02d
--- /dev/null
+++ b/contrib/sendmail/libsm/t-streq.c
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2020 Proofpoint, Inc. and its suppliers.
+ *	All rights reserved.
+ *
+ * By using this file, you agree to the terms and conditions set
+ * forth in the LICENSE file which can be found at the top level of
+ * the sendmail distribution.
+ */
+
+#include 
+SM_IDSTR(id, "@(#)$Id: t-qic.c,v 1.10 2013-11-22 20:51:43 ca Exp $")
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+#if _FFR_8BITENVADDR
+extern bool SmTestVerbose;
+
+static int
+tstrncaseeq(s1, s2, len)
+	char *s1;
+	char *s2;
+	size_t len;
+{
+	return SM_STRNCASEEQ(s1, s2, len);
+}
+
+static void
+usage(prg)
+	const char *prg;
+{
+	fprintf(stderr, "usage: %s [options]\n", prg);
+	fprintf(stderr, "options:\n");
+}
+
+int
+main(argc, argv)
+	int argc;
+	char *argv[];
+{
+	int o, len;
+#define MAXL	1024
+	char s1[MAXL], s2[MAXL];
+
+	while ((o = getopt(argc, argv, "h")) != -1)
+	{
+		switch ((char) o)
+		{
+		  default:
+			usage(argv[0]);
+			exit(1);
+		}
+	}
+
+	sm_test_begin(argc, argv, "test strncaseeq");
+
+	while (fscanf(stdin, "%d:%s\n", &len, s1) == 2 &&
+		fscanf(stdin, "%d:%s\n", &o,s2) == 2)
+	{
+		SM_TEST(tstrncaseeq(s1, s2, len) == o);
+	}
+
+	return sm_test_end();
+}
+#else /* _FFR_8BITENVADDR */
+int
+main(argc, argv)
+	int argc;
+	char *argv[];
+{
+	return 0;
+}
+#endif /* _FFR_8BITENVADDR */
diff --git a/contrib/sendmail/libsm/t-streq.sh b/contrib/sendmail/libsm/t-streq.sh
new file mode 100755
index 000000000000..7797e3f848f0
--- /dev/null
+++ b/contrib/sendmail/libsm/t-streq.sh
@@ -0,0 +1,29 @@
+#!/bin/sh
+# Copyright (c) 2020 Proofpoint, Inc. and its suppliers.
+#	All rights reserved.
+#
+# By using this file, you agree to the terms and conditions set
+# forth in the LICENSE file which can be found at the top level of
+# the sendmail distribution.
+#
+# ----------------------------------------
+# test SM_STRNCASEEQ
+# ----------------------------------------
+
+PRG=./t-streq
+R=0
+${PRG} <
+#include 
+#include 
+
+#if USE_EAI
+
+/*
+**  legal utf-8 byte sequence
+**  http://www.unicode.org/versions/Unicode6.0.0/ch03.pdf - page 94
+**
+**   Code Points        1st       2s       3s       4s
+**  U+0000..U+007F     00..7F
+**  U+0080..U+07FF     C2..DF   80..BF
+**  U+0800..U+0FFF     E0       A0..BF   80..BF
+**  U+1000..U+CFFF     E1..EC   80..BF   80..BF
+**  U+D000..U+D7FF     ED       80..9F   80..BF
+**  U+E000..U+FFFF     EE..EF   80..BF   80..BF
+**  U+10000..U+3FFFF   F0       90..BF   80..BF   80..BF
+**  U+40000..U+FFFFF   F1..F3   80..BF   80..BF   80..BF
+**  U+100000..U+10FFFF F4       80..8F   80..BF   80..BF
+*/
+
+/*
+**  based on
+**  https://github.com/lemire/fastvalidate-utf-8.git
+**  which is distributed under an MIT license (besides others).
+*/
+
+bool
+utf8_valid(b, length)
+	const char *b;
+	size_t length;
+{
+	const unsigned char *bytes;
+	size_t index;
+
+	bytes = (const unsigned char *)b;
+	index = 0;
+	while (true)
+	{
+		unsigned char byte1;
+
+		do { /* fast ASCII Path */
+			if (index >= length)
+				return true;
+			byte1 = bytes[index++];
+		} while (byte1 < 0x80);
+		if (byte1 < 0xE0)
+		{
+			/* Two-byte form. */
+			if (index == length)
+				return false;
+			if (byte1 < 0xC2 || bytes[index++] > 0xBF)
+				return false;
+		}
+		else if (byte1 < 0xF0)
+		{
+			/* Three-byte form. */
+			if (index + 1 >= length)
+				return false;
+			unsigned char byte2 = bytes[index++];
+			if (byte2 > 0xBF
+			    /* Overlong? 5 most significant bits must not all be zero. */
+			    || (byte1 == 0xE0 && byte2 < 0xA0)
+			    /* Check for illegal surrogate codepoints. */
+			    || (byte1 == 0xED && 0xA0 <= byte2)
+			    /* Third byte trailing-byte test. */
+			    || bytes[index++] > 0xBF)
+				return false;
+		}
+		else
+		{
+
+			/* Four-byte form. */
+			if (index + 2 >= length)
+				return false;
+			int byte2 = bytes[index++];
+			if (byte2 > 0xBF
+			    /* Check that 1 <= plane <= 16. Tricky optimized form of: */
+			    /* if (byte1 > (byte) 0xF4 */
+			    /*    || byte1 == (byte) 0xF0 && byte2 < (byte) 0x90 */
+			    /*    || byte1 == (byte) 0xF4 && byte2 > (byte) 0x8F) */
+			    || (((byte1 << 28) + (byte2 - 0x90)) >> 30) != 0
+			    /* Third byte trailing-byte test */
+			    || bytes[index++] > 0xBF
+			    /* Fourth byte trailing-byte test */
+			    || bytes[index++] > 0xBF)
+				return false;
+		}
+	}
+	/* NOTREACHED */
+	return false;
+}
+#endif /* USE_EAI */
diff --git a/contrib/sendmail/libsm/util.c b/contrib/sendmail/libsm/util.c
index 1653fbdbb153..dd8bf88f79e5 100644
--- a/contrib/sendmail/libsm/util.c
+++ b/contrib/sendmail/libsm/util.c
@@ -21,12 +21,13 @@ SM_RCSID("@(#)$Id: util.c,v 1.10 2013-11-22 20:51:44 ca Exp $")
 
 /*
 **  STR2PRT -- convert "unprintable" characters in a string to \oct
+**		(except for some special chars, see below)
 **
 **	Parameters:
-**		s -- string to convert
+**		s -- string to convert [A]
 **
 **	Returns:
-**		converted string.
+**		converted string [S][U]
 **		This is a static local buffer, string must be copied
 **		before this function is called again!
 */
@@ -41,6 +42,12 @@ str2prt(s)
 	static int len = 0;
 	static char *buf = NULL;
 
+#if _FFR_LOGASIS >= 1
+#define BADCHAR(ch)	((unsigned char)(ch) <= 31)
+#else
+#define BADCHAR(ch)	(!(isascii(ch) && isprint(ch)))
+#endif
+
 	if (s == NULL)
 		return NULL;
 	ok = true;
@@ -51,7 +58,7 @@ str2prt(s)
 			++l;
 			ok = false;
 		}
-		else if (!(isascii(*h) && isprint(*h)))
+		else if (BADCHAR(*h))
 		{
 			l += 3;
 			ok = false;
@@ -71,7 +78,7 @@ str2prt(s)
 	for (h = buf; *s != '\0' && l > 0; s++, l--)
 	{
 		c = *s;
-		if (isascii(c) && isprint(c) && c != '\\')
+		if (c != '\\' && !BADCHAR(c))
 		{
 			*h++ = c;
 		}
@@ -99,9 +106,8 @@ str2prt(s)
 					(unsigned int)((unsigned char) c));
 
 				/*
-				**  XXX since l is unsigned this may
-				**  wrap around if the calculation is screwed
-				**  up...
+				**  XXX since l is unsigned this may wrap
+				**  around if the calculation is screwed up...
 				*/
 
 				l -= 2;
@@ -123,8 +129,8 @@ str2prt(s)
 **	The input and output pointers can be the same.
 **
 **	Parameters:
-**		ibp -- a pointer to the string to translate
-**		obp -- a pointer to an output buffer
+**		ibp -- a pointer to the string to translate [x]
+**		obp -- a pointer to an output buffer [i][m:A]
 **		bsp -- pointer to the length of the output buffer
 **
 **	Returns:
@@ -137,10 +143,29 @@ str2prt(s)
 #define SM_MM_QUOTE(ch) (((ch) & 0377) == METAQUOTE || (((ch) & 0340) == 0200))
 
 char *
-quote_internal_chars(ibp, obp, bsp)
+#if SM_HEAP_CHECK > 2
+quote_internal_chars_tagged
+#else
+quote_internal_chars
+#endif
+	(ibp, obp, bsp, rpool
+#if SM_HEAP_CHECK > 2
+	, tag, line, group
+#endif
+	)
 	char *ibp;
 	char *obp;
 	int *bsp;
+	SM_RPOOL_T *rpool;
+#if SM_HEAP_CHECK > 2
+	char *tag;
+	int line;
+	int group;
+#else
+# define tag  "quote_internal_chars"
+# define line 1
+# define group 1
+#endif
 {
 	char *ip, *op;
 	int bufused, olen;
@@ -162,7 +187,7 @@ quote_internal_chars(ibp, obp, bsp)
 	/* is the output buffer big enough? */
 	if (olen > *bsp)
 	{
-		obp = sm_malloc_x(olen);
+		obp = sm_rpool_malloc_tagged_x(rpool, olen, tag, line, group);
 		buffer_same = false;
 		*bsp = olen;
 	}
@@ -187,7 +212,7 @@ quote_internal_chars(ibp, obp, bsp)
 
 	if (buffer_same)
 	{
-		obp = sm_malloc_x(olen);
+		obp = sm_malloc_tagged_x(olen, tag, line + 1, group);
 		buffer_same = false;
 		*bsp = olen;
 	}
@@ -205,14 +230,19 @@ quote_internal_chars(ibp, obp, bsp)
 	op[bufused] = '\0';
 	return obp;
 }
+#if SM_HEAP_CHECK <= 2
+# undef tag
+# undef line
+# undef group
+#endif
 
 /*
 **  DEQUOTE_INTERNAL_CHARS -- undo the effect of quote_internal_chars
 **
 **	Parameters:
-**		ibp -- a pointer to the string to be translated.
-**		obp -- a pointer to the output buffer.  Can be the
-**			same as ibp.
+**		ibp -- a pointer to the string to be translated. [i]
+**		obp -- a pointer to the output buffer. [x]
+**			Can be the same as ibp.
 **		obs -- the size of the output buffer.
 **
 **	Returns:
diff --git a/contrib/sendmail/libsm/uxtext_unquote.c b/contrib/sendmail/libsm/uxtext_unquote.c
new file mode 100644
index 000000000000..67e77c306048
--- /dev/null
+++ b/contrib/sendmail/libsm/uxtext_unquote.c
@@ -0,0 +1,204 @@
+/*
+ * Copyright (c) 2020 Proofpoint, Inc. and its suppliers.
+ *	All rights reserved.
+ *
+ * By using this file, you agree to the terms and conditions set
+ * forth in the LICENSE file which can be found at the top level of
+ * the sendmail distribution.
+ *
+ */
+
+#include 
+#include 
+
+/*
+**  based on
+**  https://github.com/aox/encodings/utf.cpp
+**  see license.txt included below.
+*/
+
+#if USE_EAI
+#include 
+#define SM_ISDIGIT(c)	(isascii(c) && isdigit(c))
+
+#include 
+
+/* for prototype */
+#include 
+
+# if 0
+/*
+**   RFC 6533:
+**
+**   In the ABNF below, all productions not defined in this document are
+**   defined in Appendix B of [RFC5234], in Section 4 of [RFC3629], or in
+**   [RFC3464].
+**
+**   utf-8-type-addr     = "utf-8;" utf-8-enc-addr
+**   utf-8-address       = Mailbox ; Mailbox as defined in [RFC6531].
+**   utf-8-enc-addr      = utf-8-addr-xtext /
+**                         utf-8-addr-unitext /
+**                         utf-8-address
+**   utf-8-addr-xtext    = 1*(QCHAR / EmbeddedUnicodeChar)
+**                         ; 7bit form of utf-8-addr-unitext.
+**                         ; Safe for use in the ORCPT [RFC3461]
+**                         ; parameter even when SMTPUTF8 SMTP
+**                         ; extension is not advertised.
+**   utf-8-addr-unitext  = 1*(QUCHAR / EmbeddedUnicodeChar)
+**                       ; MUST follow utf-8-address ABNF when
+**                       ; dequoted.
+**                       ; Safe for using in the ORCPT [RFC3461]
+**                       ; parameter when SMTPUTF8 SMTP extension
+**                       ; is also advertised.
+**   QCHAR              = %x21-2a / %x2c-3c / %x3e-5b / %x5d-7e
+**                       ; ASCII printable characters except
+**                       ; CTLs, SP, '\', '+', '='.
+**   QUCHAR              = QCHAR / UTF8-2 / UTF8-3 / UTF8-4
+**                       ; ASCII printable characters except
+**                       ; CTLs, SP, '\', '+' and '=', plus
+**                       ; other Unicode characters encoded in UTF-8
+**   EmbeddedUnicodeChar =   %x5C.78 "{" HEXPOINT "}"
+**                       ; starts with "\x"
+**   HEXPOINT = ( ( "0"/"1" ) %x31-39 ) / "10" / "20" /
+**              "2B" / "3D" / "7F" /         ; all xtext-specials
+**              "5C" / (HEXDIG8 HEXDIG) /    ; 2-digit forms
+**              ( NZHEXDIG 2(HEXDIG) ) /     ; 3-digit forms
+**              ( NZDHEXDIG 3(HEXDIG) ) /    ; 4-digit forms excluding
+**              ( "D" %x30-37 2(HEXDIG) ) /  ; ... surrogate
+**              ( NZHEXDIG 4(HEXDIG) ) /     ; 5-digit forms
+**              ( "10" 4*HEXDIG )            ; 6-digit forms
+**              ; represents either "\" or a Unicode code point outside
+**              ; the ASCII repertoire
+**   HEXDIG8             = %x38-39 / "A" / "B" / "C" / "D" / "E" / "F"
+**                       ; HEXDIG excluding 0-7
+**   NZHEXDIG            = %x31-39 / "A" / "B" / "C" / "D" / "E" / "F"
+**                       ; HEXDIG excluding "0"
+**   NZDHEXDIG           = %x31-39 / "A" / "B" / "C" / "E" / "F"
+**                       ; HEXDIG excluding "0" and "D"
+*/
+# endif /* 0 */
+
+/*
+**  UXTEXT_UNQUOTE -- "unquote" a utf-8-addr-unitext
+**
+**	Parameters:
+**		quoted -- original string [x]
+**		unquoted -- "decoded" string [x] (buffer provided by caller)
+**			if NULL this is basically a syntax check.
+**		olen -- length of unquoted (must be > 0)
+**
+**	Returns:
+**		>0: length of "decoded" string
+**		<0: error
+*/
+
+int
+uxtext_unquote(quoted, unquoted, olen)
+	const char *quoted;
+	char *unquoted;
+	int olen;
+{
+	const unsigned char *cp;
+	int ch, len;
+
+#define APPCH(ch) do	\
+	{		\
+		if (len >= olen)	\
+			return 0 - olen;	\
+		if (NULL !=  unquoted)	\
+			unquoted[len] = (char) (ch);	\
+		len++;	\
+	} while (0)
+
+	SM_REQUIRE(olen > 0);
+	SM_REQUIRE(NULL != quoted);
+	len = 0;
+	for (cp = (const unsigned char *) quoted; (ch = *cp) != 0; cp++)
+	{
+		if (ch == '\\' && cp[1] == 'x' && cp[2] == '{')
+		{
+			int	 uc = 0;
+
+			cp += 2;
+			while ((ch = *++cp) != '}')
+			{
+				if (SM_ISDIGIT(ch))
+					uc = (uc << 4) + (ch - '0');
+				else if (ch >= 'a' && ch <= 'f')
+					uc = (uc << 4) + (ch - 'a' + 10);
+				else if (ch >= 'A' && ch <= 'F')
+					uc = (uc << 4) + (ch - 'A' + 10);
+				else
+					return 0 - len;
+				if (uc > 0x10ffff)
+					return 0 - len;
+			}
+
+			if (uc < 0x80)
+				APPCH(uc);
+			else if (uc < 0x800)
+			{
+				APPCH(0xc0 | ((char) (uc >> 6)));
+				APPCH(0x80 | ((char) (uc & 0x3f)));
+			}
+			else if (uc < 0x10000)
+			{
+				APPCH(0xe0 | ((char) (uc >> 12)));
+				APPCH(0x80 | ((char) (uc >> 6) & 0x3f));
+				APPCH(0x80 | ((char) (uc & 0x3f)));
+			}
+			else if (uc < 0x200000)
+			{
+				APPCH(0xf0 | ((char) (uc >> 18)));
+				APPCH(0x80 | ((char) (uc >> 12) & 0x3f));
+				APPCH(0x80 | ((char) (uc >> 6) & 0x3f));
+				APPCH(0x80 | ((char) (uc & 0x3f)));
+			}
+			else if (uc < 0x4000000)
+			{
+				APPCH(0xf8 | ((char) (uc >> 24)));
+				APPCH(0x80 | ((char) (uc >> 18) & 0x3f));
+				APPCH(0x80 | ((char) (uc >> 12) & 0x3f));
+				APPCH(0x80 | ((char) (uc >> 6) & 0x3f));
+				APPCH(0x80 | ((char) (uc & 0x3f)));
+			}
+			else
+			{
+				APPCH(0xfc | ((char) (uc >> 30)));
+				APPCH(0x80 | ((char) (uc >> 24) & 0x3f));
+				APPCH(0x80 | ((char) (uc >> 18) & 0x3f));
+				APPCH(0x80 | ((char) (uc >> 12) & 0x3f));
+				APPCH(0x80 | ((char) (uc >> 6) & 0x3f));
+				APPCH(0x80 | ((char) (uc & 0x3f)));
+			}
+		}
+		else
+			APPCH(ch);
+	}
+	APPCH('\0');
+	return len;
+}
+
+# if 0
+aox/doc/readme/license.txt
+
+Copyright (c) 2003-2014, Archiveopteryx and its contributors.
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose, without fee, and without a written
+agreement is hereby granted, provided that the above copyright notice
+and this paragraph and the following two paragraphs appear in all
+copies.
+
+IN NO EVENT SHALL ORYX BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT,
+SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST PROFITS,
+ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
+ORYX HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+ORYX SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT LIMITED
+TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS"
+BASIS, AND ORYX HAS NO OBLIGATIONS TO PROVIDE MAINTENANCE, SUPPORT,
+UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+# endif /* 0 */
+#endif /* USE_EAI */
diff --git a/contrib/sendmail/libsm/vasprintf.c b/contrib/sendmail/libsm/vasprintf.c
index a880889a3d35..3bac485b8ff2 100644
--- a/contrib/sendmail/libsm/vasprintf.c
+++ b/contrib/sendmail/libsm/vasprintf.c
@@ -56,7 +56,7 @@ int
 sm_vasprintf(str, fmt, ap)
 	char **str;
 	const char *fmt;
-	SM_VA_LOCAL_DECL
+	va_list ap;
 {
 	int ret;
 	SM_FILE_T fake;
diff --git a/contrib/sendmail/libsm/vfprintf.c b/contrib/sendmail/libsm/vfprintf.c
index b90a305b04d8..e4e69edbe419 100644
--- a/contrib/sendmail/libsm/vfprintf.c
+++ b/contrib/sendmail/libsm/vfprintf.c
@@ -98,7 +98,7 @@ static int
 sm_bprintf(fp, fmt, ap)
 	SM_FILE_T *fp;
 	const char *fmt;
-	SM_VA_LOCAL_DECL
+	va_list ap;
 {
 	int ret;
 	SM_FILE_T fake;
@@ -174,7 +174,7 @@ sm_io_vfprintf(fp, timeout, fmt0, ap)
 	SM_FILE_T *fp;
 	int timeout;
 	const char *fmt0;
-	SM_VA_LOCAL_DECL
+	va_list ap;
 {
 	register char *fmt;	/* format string */
 	register int ch;	/* character from fmt */
@@ -827,7 +827,7 @@ number:			if ((dprec = prec) >= 0)
 static void
 sm_find_arguments(fmt0, ap, argtable)
 	const char *fmt0;
-	SM_VA_LOCAL_DECL
+	va_list ap;
 	va_list **argtable;
 {
 	register char *fmt;	/* format string */
@@ -1076,6 +1076,7 @@ reswitch:	switch (ch)
 			(void) SM_VA_ARG(ap, void *);
 			break;
 		}
+		SM_VA_END_COPY((*argtable)[n]);
 	}
 
 	if ((typetable != NULL) && (typetable != stattypetable))
diff --git a/contrib/sendmail/libsm/vprintf.c b/contrib/sendmail/libsm/vprintf.c
index 670482960d93..321499f67736 100644
--- a/contrib/sendmail/libsm/vprintf.c
+++ b/contrib/sendmail/libsm/vprintf.c
@@ -33,7 +33,7 @@ int
 sm_vprintf(timeout, fmt, ap)
 	int timeout;
 	char const *fmt;
-	SM_VA_LOCAL_DECL
+	va_list ap;
 {
 	return sm_io_vfprintf(smiostdout, timeout, fmt, ap);
 }
diff --git a/contrib/sendmail/libsm/vsnprintf.c b/contrib/sendmail/libsm/vsnprintf.c
index f0da60285316..14da520f7f9f 100644
--- a/contrib/sendmail/libsm/vsnprintf.c
+++ b/contrib/sendmail/libsm/vsnprintf.c
@@ -42,7 +42,7 @@ sm_vsnprintf(str, n, fmt, ap)
 	char *str;
 	size_t n;
 	const char *fmt;
-	SM_VA_LOCAL_DECL
+	va_list ap;
 {
 	int ret;
 	char dummy;
diff --git a/contrib/sendmail/libsm/xleni.c b/contrib/sendmail/libsm/xleni.c
new file mode 100644
index 000000000000..3d0b9d94d24e
--- /dev/null
+++ b/contrib/sendmail/libsm/xleni.c
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2020 Proofpoint, Inc. and its suppliers.
+ *	All rights reserved.
+ *
+ * By using this file, you agree to the terms and conditions set
+ * forth in the LICENSE file which can be found at the top level of
+ * the sendmail distribution.
+ *
+ */
+
+#include 
+#include 
+#include 
+
+#if _FFR_8BITENVADDR
+/*
+**  XLENI -- determine the 'i'internal length of a string in e'x'ternal format
+**
+**	Parameters:
+**		str -- string [x]
+**
+**	Returns:
+**		'i'internal length of a string in e'x'ternal format
+*/
+
+int
+xleni(str)
+	const char *str;
+{
+	char c;
+	int idx, ilen;
+
+	if (NULL == str)
+		return -1;
+	for (ilen = 0, idx = 0; (c = str[idx]) != '\0'; ilen++, idx++)
+	{
+		if (SM_MM_QUOTE(c))
+			ilen++;
+	}
+
+	return ilen;
+}
+#endif /* _FFR_8BITENVADDR */
diff --git a/contrib/sendmail/libsmdb/smcdb.c b/contrib/sendmail/libsmdb/smcdb.c
index 84c1a9e1f90c..93b3bdf52323 100644
--- a/contrib/sendmail/libsmdb/smcdb.c
+++ b/contrib/sendmail/libsmdb/smcdb.c
@@ -70,14 +70,14 @@ static int smcdb_cursor __P((SMDB_DATABASE *database, SMDB_CURSOR **cursor, SMDB
 **
 */
 
-#if 0
+# if 0
 static int
 smdb_type_to_cdb_type(type)
 	SMDB_DBTYPE type;
 {
 	return 0;	/* XXX */
 }
-#endif
+# endif
 
 /*
 **  CDB_ERROR_TO_SMDB -- Translates cdb errors to smdbe errors
@@ -404,7 +404,7 @@ smcdb_cursor(database, cursor, flags)
 }
 
 /*
-**  SMDB_DB_OPEN -- Opens a db database.
+**  SMDB_CDB_OPEN -- Opens a cdb database.
 **
 **	Parameters:
 **		database -- An unallocated database pointer to a pointer.
@@ -415,19 +415,12 @@ smcdb_cursor(database, cursor, flags)
 **		type -- The type of database to open
 **			See smdb_type_to_cdb_type for valid types.
 **		user_info -- User information for file permissions.
-**		db_params --
-**			An SMDB_DBPARAMS struct including params. These
-**			are processed according to the type of the
-**			database. Currently supported params (only for
-**			HASH type) are:
-**			   num_elements
-**			   cache_size
+**		db_params -- unused
 **
 **	Returns:
 **		SMDBE_OK -- Success, other errno:
 **		SMDBE_MALLOC -- Cannot allocate memory.
-**		SMDBE_BAD_OPEN -- db_open didn't return an error, but
-**				 somehow the DB pointer is NULL.
+**		SMDBE_BAD_OPEN -- various (OS) errors.
 **		Anything else: translated error from cdb
 */
 
@@ -492,7 +485,7 @@ smdb_cdb_open(database, db_name, mode, mode_mask, sff, type, user_info, db_param
 
 	sm_cdbmap->smcdb_lock_fd = lock_fd;
 
-#if 0
+# if 0
 	db = NULL;
 	db_flags = 0;
 	if (bitset(O_CREAT, mode))
@@ -502,7 +495,7 @@ smdb_cdb_open(database, db_name, mode, mode_mask, sff, type, user_info, db_param
 	if (mode == O_RDONLY)
 		db_flags |= DB_RDONLY;
 	SM_DB_FLAG_ADD(db_flags);
-#endif
+# endif
 
 	result = -1; /* smdb_db_open_internal(db_file_name, db_type, db_flags, db_params, &db); */
 	db_fd = open(db_file_name, mode, DBMMODE);
diff --git a/contrib/sendmail/libsmdb/smdb2.c b/contrib/sendmail/libsmdb/smdb2.c
index d208efb40324..2e58f167124a 100644
--- a/contrib/sendmail/libsmdb/smdb2.c
+++ b/contrib/sendmail/libsmdb/smdb2.c
@@ -478,18 +478,18 @@ smdb_db_open_internal(db_name, db_type, db_flags, db_params, db)
 
 static void
 db_err_cb(
-#if DB_VERSION_MAJOR == 4 && DB_VERSION_MINOR >= 3
+#  if DB_VERSION_MAJOR == 4 && DB_VERSION_MINOR >= 3
 	dbenv,
-#endif
+#  endif
 	errpfx, msg)
-#if DB_VERSION_MAJOR == 4 && DB_VERSION_MINOR >= 3
+#  if DB_VERSION_MAJOR == 4 && DB_VERSION_MINOR >= 3
 	const DB_ENV *dbenv;
 	const char *errpfx;
 	const char *msg;
-#else
+#  else
 	const char *errpfx;
 	char *msg;
-#endif
+#  endif
 {
 	/* do not print/log any errors... */
 	return;
diff --git a/contrib/sendmail/libsmutil/Makefile.m4 b/contrib/sendmail/libsmutil/Makefile.m4
index e8efdc2cd955..611555a9f1ed 100644
--- a/contrib/sendmail/libsmutil/Makefile.m4
+++ b/contrib/sendmail/libsmutil/Makefile.m4
@@ -12,4 +12,10 @@ define(`bldSOURCES', `debug.c err.c lockfile.c safefile.c snprintf.c cf.c ')
 APPENDDEF(`confENVDEF', `-DNOT_SENDMAIL')
 bldPRODUCT_END
 
+srcdir=${SRCDIR}/libsmutil
+define(`confCHECK_LIBS',`libsmutil.a ../libsm/libsm.a')dnl
+include(confBUILDTOOLSDIR`/M4/'bldM4_TYPE_DIR`/check.m4')
+smcheck(`t-lockfile', `compile')
+smcheck(`t-lockfile-0.sh', `run')
+
 bldFINISH
diff --git a/contrib/sendmail/libsmutil/cf.c b/contrib/sendmail/libsmutil/cf.c
index f803b95ef607..f445f6293059 100644
--- a/contrib/sendmail/libsmutil/cf.c
+++ b/contrib/sendmail/libsmutil/cf.c
@@ -61,8 +61,11 @@ getcfname(opmode, submitmode, cftype, conffile)
 			(void) sm_strlcpy(cf, cflocation, sizeof cf);
 		else
 #endif /* NETINFO */
+		/* "else" in #if code above */
+		{
 			(void) sm_strlcpyn(cf, sizeof cf, 2, _DIR_SENDMAILCF,
 					   "submit.cf");
+		}
 		if (cftype == SM_GET_SUBMIT_CF || stat(cf, &sbuf) == 0)
 			return cf;
 	}
diff --git a/contrib/sendmail/libsmutil/safefile.c b/contrib/sendmail/libsmutil/safefile.c
index 1c70c24440e5..81adb5842496 100644
--- a/contrib/sendmail/libsmutil/safefile.c
+++ b/contrib/sendmail/libsmutil/safefile.c
@@ -78,12 +78,12 @@ safefile(fn, uid, gid, user, flags, mode, st)
 		flags &= ~SFF_SAFEDIRPATH;
 
 	/* first check to see if the file exists at all */
-# if HASLSTAT
+#if HASLSTAT
 	if ((bitset(SFF_NOSLINK, flags) ? lstat(fn, st)
 					: stat(fn, st)) < 0)
-# else
+#else
 	if (stat(fn, st) < 0)
-# endif
+#endif
 	{
 		file_errno = errno;
 	}
@@ -97,21 +97,21 @@ safefile(fn, uid, gid, user, flags, mode, st)
 		**  soon here!
 		*/
 
-# ifdef SUID_ROOT_FILES_OK
+#ifdef SUID_ROOT_FILES_OK
 		if (bitset(S_ISUID, st->st_mode))
-# else
+#else
 		if (bitset(S_ISUID, st->st_mode) && st->st_uid != 0 &&
 		    st->st_uid != TrustedUid)
-# endif
+#endif
 		{
 			uid = st->st_uid;
 			user = NULL;
 		}
-# ifdef SUID_ROOT_FILES_OK
+#ifdef SUID_ROOT_FILES_OK
 		if (bitset(S_ISGID, st->st_mode))
-# else
+#else
 		if (bitset(S_ISGID, st->st_mode) && st->st_gid != 0)
-# endif
+#endif
 			gid = st->st_gid;
 	}
 
@@ -142,7 +142,7 @@ safefile(fn, uid, gid, user, flags, mode, st)
 		}
 		else
 		{
-# if HASLSTAT
+#if HASLSTAT
 			/* Need lstat() information if called stat() before */
 			if (!bitset(SFF_NOSLINK, flags) && lstat(fn, st) < 0)
 			{
@@ -151,7 +151,7 @@ safefile(fn, uid, gid, user, flags, mode, st)
 					sm_dprintf("\t%s\n", sm_errstring(ret));
 				return ret;
 			}
-# endif /* HASLSTAT */
+#endif /* HASLSTAT */
 			/* directory is writable: disallow links */
 			flags |= SFF_NOLINK;
 		}
@@ -218,7 +218,7 @@ safefile(fn, uid, gid, user, flags, mode, st)
 				if (stbuf.st_gid == gid)
 					/* EMPTY */
 					;
-# ifndef NO_GROUP_SET
+#ifndef NO_GROUP_SET
 				else if (user != NULL && !DontInitGroups &&
 					 ((gr != NULL &&
 					   gr->gr_gid == stbuf.st_gid) ||
@@ -232,7 +232,7 @@ safefile(fn, uid, gid, user, flags, mode, st)
 					if (*gp == NULL)
 						md >>= 3;
 				}
-# endif /* ! NO_GROUP_SET */
+#endif /* ! NO_GROUP_SET */
 				else
 					md >>= 3;
 			}
@@ -252,7 +252,7 @@ safefile(fn, uid, gid, user, flags, mode, st)
 		return ret;
 	}
 
-# ifdef S_ISLNK
+#ifdef S_ISLNK
 	if (bitset(SFF_NOSLINK, flags) && S_ISLNK(st->st_mode))
 	{
 		if (tTd(44, 4))
@@ -260,7 +260,7 @@ safefile(fn, uid, gid, user, flags, mode, st)
 				(unsigned long) st->st_mode);
 		return E_SM_NOSLINK;
 	}
-# endif /* S_ISLNK */
+#endif /* S_ISLNK */
 	if (bitset(SFF_REGONLY, flags) && !S_ISREG(st->st_mode))
 	{
 		if (tTd(44, 4))
@@ -332,7 +332,7 @@ safefile(fn, uid, gid, user, flags, mode, st)
 		if (st->st_gid == gid)
 			/* EMPTY */
 			;
-# ifndef NO_GROUP_SET
+#ifndef NO_GROUP_SET
 		else if (user != NULL && !DontInitGroups &&
 			 ((gr != NULL && gr->gr_gid == st->st_gid) ||
 			  (gr = getgrgid(st->st_gid)) != NULL))
@@ -345,7 +345,7 @@ safefile(fn, uid, gid, user, flags, mode, st)
 			if (*gp == NULL)
 				mode >>= 3;
 		}
-# endif /* ! NO_GROUP_SET */
+#endif /* ! NO_GROUP_SET */
 		else
 			mode >>= 3;
 	}
@@ -470,18 +470,18 @@ safedirpath(fn, uid, gid, user, flags, level, offset)
 		if (tTd(44, 20))
 			sm_dprintf("\t[dir %s]\n", s);
 
-# if HASLSTAT
+#if HASLSTAT
 		ret = lstat(s, &stbuf);
-# else
+#else
 		ret = stat(s, &stbuf);
-# endif
+#endif
 		if (ret < 0)
 		{
 			ret = errno;
 			break;
 		}
 
-# ifdef S_ISLNK
+#ifdef S_ISLNK
 		/* Follow symlinks */
 		if (S_ISLNK(stbuf.st_mode))
 		{
@@ -637,7 +637,7 @@ safedirpath(fn, uid, gid, user, flags, level, offset)
 		if (stbuf.st_gid == gid &&
 		    bitset(S_IXGRP, stbuf.st_mode))
 			continue;
-# ifndef NO_GROUP_SET
+#ifndef NO_GROUP_SET
 		if (user != NULL && !DontInitGroups &&
 		    ((gr != NULL && gr->gr_gid == stbuf.st_gid) ||
 		     (gr = getgrgid(stbuf.st_gid)) != NULL))
@@ -651,7 +651,7 @@ safedirpath(fn, uid, gid, user, flags, level, offset)
 			    bitset(S_IXGRP, stbuf.st_mode))
 				continue;
 		}
-# endif /* ! NO_GROUP_SET */
+#endif /* ! NO_GROUP_SET */
 		if (!bitset(S_IXOTH, stbuf.st_mode))
 		{
 			ret = EACCES;
@@ -869,13 +869,13 @@ filechanged(fn, fd, stb)
 
 	if (stb->st_mode == ST_MODE_NOFILE)
 	{
-# if HASLSTAT && BOGUS_O_EXCL
+#if HASLSTAT && BOGUS_O_EXCL
 		/* only necessary if exclusive open follows symbolic links */
 		if (lstat(fn, stb) < 0 || stb->st_nlink != 1)
 			return true;
-# else
+#else
 		return false;
-# endif
+#endif
 	}
 	if (fstat(fd, &sta) < 0)
 		return true;
@@ -883,9 +883,9 @@ filechanged(fn, fd, stb)
 	if (sta.st_nlink != stb->st_nlink ||
 	    sta.st_dev != stb->st_dev ||
 	    sta.st_ino != stb->st_ino ||
-# if HAS_ST_GEN && 0		/* AFS returns garbage in st_gen */
+#if HAS_ST_GEN && 0		/* AFS returns garbage in st_gen */
 	    sta.st_gen != stb->st_gen ||
-# endif
+#endif
 	    sta.st_uid != stb->st_uid ||
 	    sta.st_gid != stb->st_gid)
 	{
@@ -899,10 +899,10 @@ filechanged(fn, fd, stb)
 			sm_dprintf(" ino	= %llu/%llu\n",
 				(ULONGLONG_T) stb->st_ino,
 				(ULONGLONG_T) sta.st_ino);
-# if HAS_ST_GEN
+#if HAS_ST_GEN
 			sm_dprintf(" gen	= %ld/%ld\n",
 				(long) stb->st_gen, (long) sta.st_gen);
-# endif
+#endif
 			sm_dprintf(" uid	= %ld/%ld\n",
 				(long) stb->st_uid, (long) sta.st_uid);
 			sm_dprintf(" gid	= %ld/%ld\n",
diff --git a/contrib/sendmail/libsmutil/t-lockfile-0.sh b/contrib/sendmail/libsmutil/t-lockfile-0.sh
new file mode 100755
index 000000000000..bc4ed311f410
--- /dev/null
+++ b/contrib/sendmail/libsmutil/t-lockfile-0.sh
@@ -0,0 +1,70 @@
+#!/bin/sh
+# Copyright (c) 2021 Proofpoint, Inc. and its suppliers.
+#	All rights reserved.
+#
+# By using this file, you agree to the terms and conditions set
+# forth in the LICENSE file which can be found at the top level of
+# the sendmail distribution.
+#
+# ----------------------------------------
+# test t-lockfile, analyze result
+# ----------------------------------------
+
+fail()
+{
+  echo "$0: $@"
+  exit 1
+}
+
+PRG=./t-lockfile
+O=l.log
+
+analyze()
+{
+ # the "owner" unlock operation must be before
+ # the "client" lock operation can succeed
+ U=`grep -n 'owner=1, unlock.*done' $O | cut -d: -f1 | head -n1`
+ [ x"$U" = "x" ] && U=`grep -n '_close' $O | cut -d: -f1 | head -n1`
+ L=`grep -n 'owner=0, lock.* ok' $O | cut -d: -f1`
+ [ x"$U" = "x" ] && return 1
+ [ x"$L" = "x" ] && return 1
+ [ $U -lt $L ]
+}
+
+all=true
+while getopts 2a: FLAG
+do
+  case "${FLAG}" in
+    2) all=false;;
+    a) O=${OPTARG}
+       analyze || fail "$opts: unlock1=$U, lock2=$L"
+       exit;;
+  esac
+done
+shift `expr ${OPTIND} - 1`
+
+[ -x ${PRG} ] || fail "missing ${PRG}"
+
+if $all
+then
+for opts in "" "-r" "-n" "-nr"
+do
+  ${PRG} $opts > $O 2>&1 || fail "$opts: $?"
+  analyze || fail "$opts: unlock1=$U, lock2=$L"
+done
+fi
+
+# try with two processes
+for opts in "" "-r"
+do
+rm -f $O
+${PRG} -W >> $O 2>&1 || fail "-W: $?"
+wpid=$!
+${PRG} -R $opts >> $O 2>&1 || fail "-R $opts: $?"
+rpid=$!
+analyze || fail "$opts: unlock1=$U, lock2=$L"
+wait $wpid
+wait $rpid
+done
+
+exit 0
diff --git a/contrib/sendmail/libsmutil/t-lockfile.c b/contrib/sendmail/libsmutil/t-lockfile.c
new file mode 100644
index 000000000000..27818a990873
--- /dev/null
+++ b/contrib/sendmail/libsmutil/t-lockfile.c
@@ -0,0 +1,351 @@
+/*
+ * Copyright (c) 2005 Proofpoint, Inc. and its suppliers.
+ *	All rights reserved.
+ *
+ * By using this file, you agree to the terms and conditions set
+ * forth in the LICENSE file which can be found at the top level of
+ * the sendmail distribution.
+ *
+ */
+
+#include 
+SM_IDSTR(id, "@(#)$Id: t-lockfile.c,v 1.2 2013-11-22 20:51:50 ca Exp $")
+#include 
+#include 
+#include 
+
+#define IOBUFSZ	64
+char iobuf[IOBUFSZ];
+#define FIRSTLINE	"first line\n"
+#define LASTLINE	"last line\n"
+static int noio, chk;
+static pid_t pid;
+
+int
+openfile(owner, filename, flags)
+	int owner;
+	char *filename;
+	int flags;
+{
+	int fd;
+
+	if (owner)
+		flags |= O_CREAT;
+	fd = open(filename, flags, 0640);
+	if (fd >= 0)
+		return fd;
+	fprintf(stderr, "%d: %ld: owner=%d, open(%s) failed\n",
+		(int) pid, (long) time(NULL), owner, filename);
+	return 1;
+}
+
+int
+wrbuf(fd)
+	int fd;
+{
+	int r;
+
+	if (noio)
+		return 0;
+	r = write(fd, iobuf, sizeof(iobuf));
+	if (sizeof(iobuf) == r)
+		return 0;
+	fprintf(stderr, "%d: %ld: owner=1, write(%s)=fail\n",
+		(int) pid, (long) time(NULL), iobuf);
+	return 1;
+}
+
+int
+rdbuf(fd, xbuf)
+	int fd;
+	const char *xbuf;
+{
+	int r;
+
+	if (noio)
+		return 0;
+	r = read(fd, iobuf, sizeof(iobuf));
+	if (sizeof(iobuf) != r)
+	{
+		fprintf(stderr, "%d: %ld: owner=0, read()=fail\n",
+			(int) pid, (long) time(NULL));
+		return 1;
+	}
+	if (strncmp(iobuf, xbuf, strlen(xbuf)))
+	{
+		fprintf(stderr, "%d: %ld: owner=0, read=%s expected=%s\n",
+			(int) pid, (long) time(NULL), iobuf, xbuf);
+		return 1;
+	}
+	return 0;
+}
+
+/*
+**  LOCKTEST -- test of file locking
+**
+**	Parameters:
+**		owner -- create file?
+**		filename -- name of file.
+**		flags -- flags for open(2)
+**		delay -- how long to keep file locked?
+**
+**	Returns:
+**		0 on success
+**		!= 0 on failure.
+*/
+
+#define DBGPRINTR(str)	\
+	do	\
+	{	\
+		fprintf(stderr, "%d: %ld: owner=0, ", (int) pid,	\
+			(long) time(NULL));	\
+		fprintf(stderr, str, filename, shared ? "RD" : "EX");	\
+	} while (0)
+
+int
+locktestwr(filename, flags, delay)
+	char *filename;
+	int flags;
+	int delay;
+{
+	int fd;
+	bool locked;
+
+	fd = openfile(1, filename, flags);
+	if (fd < 0)
+		return errno;
+	locked = lockfile(fd, filename, "[owner]", LOCK_EX);
+	if (!locked)
+	{
+		fprintf(stderr, "%d: %ld: owner=1, lock(%s) failed\n",
+			(int) pid, (long) time(NULL), filename);
+		return 1;
+	}
+	else
+		fprintf(stderr, "%d: %ld: owner=1, lock(%s) ok\n",
+			(int) pid, (long) time(NULL), filename);
+
+	sm_strlcpy(iobuf, FIRSTLINE, sizeof(iobuf));
+	if (wrbuf(fd))
+		return 1;
+	sleep(delay);
+	sm_strlcpy(iobuf, LASTLINE, sizeof(iobuf));
+	if (wrbuf(fd))
+		return 1;
+	locked = lockfile(fd, filename, "[owner]", LOCK_UN);
+	if (!locked)
+	{
+		fprintf(stderr, "%d: %ld: owner=1, unlock(%s) failed\n",
+			(int) pid, (long) time(NULL), filename);
+		return 1;
+	}
+	fprintf(stderr, "%d: %ld: owner=1, unlock(%s) done\n",
+		(int) pid, (long) time(NULL), filename);
+	if (fd > 0)
+	{
+		close(fd);
+		fd = -1;
+	}
+	return 0;
+}
+
+long
+chklck(fd)
+	int fd;
+{
+#if !HASFLOCK
+	int action, i;
+	struct flock lfd;
+
+	(void) memset(&lfd, '\0', sizeof lfd);
+	lfd.l_type = F_RDLCK;
+	action = F_GETLK;
+	while ((i = fcntl(fd, action, &lfd)) < 0 && errno == EINTR)
+		continue;
+	if (i < 0)
+		return (long)i;
+	if (F_WRLCK == lfd.l_type)
+		return (long)lfd.l_pid;
+	return 0L;
+#else /* !HASFLOCK */
+	fprintf(stderr, "%d: %ld: flock: no lock test\n",
+		(int) pid, (long) time(NULL));
+	return -1L;
+#endif /* !HASFLOCK */
+}
+
+int
+locktestrd(filename, flags, delay, shared)
+	char *filename;
+	int flags;
+	int delay;
+	int shared;
+{
+	int fd, cnt;
+	int lt;
+	bool locked;
+
+	fd = openfile(0, filename, flags);
+	if (fd < 0)
+		return errno;
+	if (chk)
+	{
+		long locked;
+
+		locked = chklck(fd);
+		if (locked > 0)
+			fprintf(stderr, "%d: %ld: file=%s status=locked pid=%ld\n",
+				 (int) pid, (long) time(NULL), filename, locked);
+		else if (0 == locked)
+			fprintf(stderr, "%d: %ld: file=%s status=not_locked\n",
+				 (int) pid, (long) time(NULL), filename);
+		else
+			fprintf(stderr, "%d: %ld: file=%s status=unknown\n",
+				 (int) pid, (long) time(NULL), filename);
+		goto end;
+	}
+
+	if (shared)
+		lt = LOCK_SH;
+	else
+		lt = LOCK_EX;
+
+	for (cnt = 0; cnt < delay - 2; cnt++)
+	{
+		/* try to get lock: should fail (nonblocking) */
+		locked = lockfile(fd, filename, "[client]", lt|LOCK_NB);
+		if (locked)
+		{
+			DBGPRINTR("lock(%s)=%s succeeded\n");
+			return 1;
+		}
+		sleep(1);
+	}
+	if (delay > 0)
+		sleep(2);
+	locked = lockfile(fd, filename, "[client]", lt);
+	if (!locked)
+	{
+		DBGPRINTR("lock(%s)=%s failed\n");
+		return 1;
+	}
+	DBGPRINTR("lock(%s)=%s ok\n");
+	if (rdbuf(fd, FIRSTLINE))
+		return 1;
+	if (rdbuf(fd, LASTLINE))
+		return 1;
+	sleep(1);
+	locked = lockfile(fd, filename, "[client]", LOCK_UN);
+	if (!locked)
+	{
+		DBGPRINTR("unlock(%s)=%s failed\n");
+		return 1;
+	}
+	DBGPRINTR("unlock(%s)=%s done\n");
+
+  end:
+	if (fd > 0)
+	{
+		close(fd);
+		fd = -1;
+	}
+	return 0;
+}
+
+static void
+usage(prg)
+	const char *prg;
+{
+	fprintf(stderr, "usage: %s [options]\n"
+		"-f filename	use filename\n"
+		"-i		do not perform I/O\n"
+		"-n		do not try non-blocking locking first\n"
+		"-R		only start reader process\n"
+		"-r		use shared locking for reader\n"
+		"-s delay	sleep delay seconds before unlocking\n"
+		"-W		only start writer process\n"
+		, prg);
+}
+
+int
+main(argc, argv)
+	int argc;
+	char *argv[];
+{
+	int ch, delay, r, status, flags, shared, nb, reader, writer;
+	char *filename;
+	pid_t fpid;
+	extern char *optarg;
+
+	delay = 5;
+	filename = "testlock";
+	flags = O_RDWR;
+	shared = nb = noio = reader = writer = chk = 0;
+#define OPTIONS	"cf:inRrs:W"
+	while ((ch = getopt(argc, argv, OPTIONS)) != -1)
+	{
+		switch ((char) ch)
+		{
+		  case 'c':
+			chk = 1;
+			break;
+
+		  case 'f':
+			filename = optarg;
+			break;
+
+		  case 'i':
+			noio = 1;
+			break;
+
+		  case 'n':
+			nb = 0;
+			break;
+
+		  case 'R':
+			reader = 1;
+			break;
+
+		  case 'r':
+			shared = 1;
+			break;
+
+		  case 's':
+			delay = atoi(optarg);
+			break;
+
+		  case 'W':
+			writer = 1;
+			break;
+
+		  default:
+			usage(argv[0]);
+			exit(69);
+			break;
+		}
+	}
+
+	fpid = -1;
+	if (0 == reader && 0 == writer && (fpid = fork()) < 0)
+	{
+		perror("fork failed\n");
+		return 1;
+	}
+
+	r = 0;
+	if (reader || fpid == 0)
+	{
+		/* give the parent the chance to setup data */
+		pid = getpid();
+		sleep(1);
+		r = locktestrd(filename, flags, nb ? delay : 0, shared);
+	}
+	if (writer || fpid > 0)
+	{
+		fpid = getpid();
+		r = locktestwr(filename, flags, delay);
+		(void) wait(&status);
+	}
+	/* (void) unlink(filename); */
+	return r;
+}
diff --git a/contrib/sendmail/libsmutil/t-maplock-0.sh b/contrib/sendmail/libsmutil/t-maplock-0.sh
new file mode 100755
index 000000000000..1884211baf45
--- /dev/null
+++ b/contrib/sendmail/libsmutil/t-maplock-0.sh
@@ -0,0 +1,111 @@
+#!/bin/sh
+# Copyright (c) 2021 Proofpoint, Inc. and its suppliers.
+#	All rights reserved.
+#
+# By using this file, you agree to the terms and conditions set
+# forth in the LICENSE file which can be found at the top level of
+# the sendmail distribution.
+#
+# ----------------------------------------
+# test map locking.
+# Note: this is mostly for systems which use fcntl().
+# just invoke it from the obj.*/libsmutil/ directory;
+# otherwise use the -l and -m options to specify the paths.
+# ----------------------------------------
+
+fail()
+{
+  echo "$0: $@"
+  exit 1
+}
+
+err()
+{
+  echo "$0: $@"
+  rc=1
+}
+
+O=`basename $0`.0
+V=vt
+M=../makemap/makemap
+CHKL=./t-lockfile
+
+usage()
+{
+  cat <> $O 2>&1
+}
+
+chkl()
+{
+  ${CHKL} -Rrc -f $F >> $O 2>&1
+}
+
+for XT in ${MAPTX}
+do
+
+MT=`echo $XT | cut -d: -f1`
+EXT=`echo $XT | cut -d: -f2`
+
+F=$V.${EXT}
+
+rm -f $O
+mm &
+wpid=$!
+sleep 1
+chkl&
+rpid=$!
+
+while [ $tries -gt 0 ]
+do
+  sleep 1; chkl
+  tries=`expr $tries - 1 `
+done
+
+wait $wpid
+wait $rpid
+
+if grep "status=unknown" $O >/dev/null
+then
+  :
+else
+  # get the makemap pid, not the "mm" pid, for checks?
+  grep "status=locked pid=" $O || err "$MT map not locked"
+fi
+
+done
+
+exit $rc
diff --git a/contrib/sendmail/mail.local/mail.local.c b/contrib/sendmail/mail.local/mail.local.c
index f193f9542375..d62f74eadbf1 100644
--- a/contrib/sendmail/mail.local/mail.local.c
+++ b/contrib/sendmail/mail.local/mail.local.c
@@ -23,14 +23,12 @@ SM_IDSTR(copyright,
 SM_IDSTR(id, "@(#)$Id: mail.local.c,v 8.257 2013-11-22 20:51:51 ca Exp $")
 
 #include 
+#include 
 #include 
 #include 
 #include 
-# include 
-# ifdef EX_OK
-#  undef EX_OK		/* unistd.h may have another use for this */
-# endif
-# define LOCKFILE_PMODE 0
+#include 
+#define LOCKFILE_PMODE 0
 #include 
 #include 
 
@@ -150,6 +148,9 @@ off_t	BodyLength;
 #endif
 
 bool	EightBitMime = true;		/* advertise 8BITMIME in LMTP */
+#if USE_EAI
+bool	EAI = true;			/* advertise SMTPUTF8 in LMTP */
+#endif
 char	ErrBuf[10240];			/* error buffer */
 int	ExitVal = EX_OK;		/* sysexits.h error value. */
 bool	nobiff = false;
@@ -222,11 +223,11 @@ main(argc, argv)
 	/* use a reasonable umask */
 	(void) umask(0077);
 
-# ifdef LOG_MAIL
+#ifdef LOG_MAIL
 	openlog("mail.local", 0, LOG_MAIL);
-# else
+#else
 	openlog("mail.local", 0);
-# endif
+#endif
 
 	from = NULL;
 
@@ -237,14 +238,16 @@ main(argc, argv)
 		mailerr("421", "Configuration error: _PATH_MAILDIR too large");
 		sm_exit(EX_CONFIG);
 	}
+
+	/* HACK: add U to all options - this should be only for USE_EAI */
 #if HASHSPOOL
-	while ((ch = getopt(argc, argv, "7BbdD:f:h:r:lH:p:ns")) != -1)
+	while ((ch = getopt(argc, argv, "7BbdD:f:h:r:lH:p:nsUV")) != -1)
 #else /* HASHSPOOL */
-#  if _FFR_SPOOL_PATH
-	while ((ch = getopt(argc, argv, "7BbdD:f:h:r:lp:s")) != -1)
-#  else
-	while ((ch = getopt(argc, argv, "7BbdD:f:h:r:ls")) != -1)
-#  endif
+# if _FFR_SPOOL_PATH
+	while ((ch = getopt(argc, argv, "7BbdD:f:h:r:lp:sUV")) != -1)
+# else
+	while ((ch = getopt(argc, argv, "7BbdD:f:h:r:lsUV")) != -1)
+# endif
 #endif /* HASHSPOOL */
 	{
 		switch(ch)
@@ -353,6 +356,22 @@ main(argc, argv)
 			break;
 #endif /* HASHSPOOL || _FFR_SPOOL_PATH */
 
+#if USE_EAI
+		  case 'U':
+			EAI = false;
+			break;
+#endif
+		  case 'V':
+			fprintf(stderr, "compiled with\n");
+#if MAIL_LOCAL_TEST
+			fprintf(stderr, "MAIL_LOCAL_TEST\n");
+#endif
+#if USE_EAI
+			/* test scripts should look for SMTPUTF8 */
+			fprintf(stderr, "USE_EAI\n");
+#endif
+			break;
+
 		  case '?':
 		  default:
 			usage();
@@ -585,7 +604,7 @@ dolmtp()
 		{
 		  case 'd':
 		  case 'D':
-			if (sm_strcasecmp(buf, "data") == 0)
+			if (SM_STRCASEEQ(buf, "data"))
 			{
 				bool inbody = false;
 
@@ -639,6 +658,10 @@ dolmtp()
 				printf("250-%s\r\n", myhostname);
 				if (EightBitMime)
 					printf("250-8BITMIME\r\n");
+#if USE_EAI
+				if (EAI)
+					printf("250-SMTPUTF8\r\n");
+#endif
 				printf("250-ENHANCEDSTATUSCODES\r\n");
 				printf("250 PIPELINING\r\n");
 				continue;
@@ -674,7 +697,7 @@ dolmtp()
 
 		  case 'n':
 		  case 'N':
-			if (sm_strcasecmp(buf, "noop") == 0)
+			if (SM_STRCASEEQ(buf, "noop"))
 			{
 				printf("250 2.0.0 Ok\r\n");
 				continue;
@@ -685,7 +708,7 @@ dolmtp()
 
 		  case 'q':
 		  case 'Q':
-			if (sm_strcasecmp(buf, "quit") == 0)
+			if (SM_STRCASEEQ(buf, "quit"))
 			{
 				printf("221 2.0.0 Bye\r\n");
 				sm_exit(EX_OK);
@@ -736,7 +759,7 @@ dolmtp()
 				printf("250 2.1.5 Ok\r\n");
 				continue;
 			}
-			else if (sm_strcasecmp(buf, "rset") == 0)
+			else if (SM_STRCASEEQ(buf, "rset"))
 			{
 				printf("250 2.0.0 Ok\r\n");
 
@@ -971,7 +994,7 @@ store(from, inbody)
 
 	if (fp == NULL || fflush(fp) == EOF || ferror(fp) != 0)
 	{
-		mailerr("451 4.3.0", "Temporary file write error");
+		mailerr("451 4.3.0", "Temporary file flush error");
 		if (fp != NULL)
 			(void) fclose(fp);
 		return -1;
@@ -1058,7 +1081,7 @@ deliver(fd, name)
 
 	if (HomeMailFile == NULL)
 	{
-		if (sm_strlcpyn(path, sizeof(path), 
+		if (sm_strlcpyn(path, sizeof(path),
 #if HASHSPOOL
 				4,
 #else
@@ -1162,7 +1185,7 @@ deliver(fd, name)
 				goto tryagain;
 
 			/* open failed, don't try again */
-			mailerr("450 4.2.0", "%s: %s", path,
+			mailerr("450 4.2.0", "Create %s: %s", path,
 				sm_errstring(save_errno));
 			goto err0;
 		}
@@ -1205,7 +1228,11 @@ deliver(fd, name)
 	}
 
 	/* change UID for quota checks */
-	if (setreuid(0, user.mbdb_uid) < 0)
+	if (
+#if MAIL_LOCAL_TEST
+	    (HomeMailFile == NULL || user.mbdb_uid != getuid()) &&
+#endif
+	    setreuid(0, user.mbdb_uid) < 0)
 	{
 		mailerr("450 4.2.0", "setreuid(0, %d): %s (r=%d, e=%d)",
 			(int) user.mbdb_uid, sm_errstring(errno),
@@ -1218,7 +1245,7 @@ deliver(fd, name)
 	mbfd = open(path, O_APPEND|O_WRONLY, 0);
 	if (mbfd < 0)
 	{
-		mailerr("450 4.2.0", "%s: %s", path, sm_errstring(errno));
+		mailerr("450 4.2.0", "Append %s: %s", path, sm_errstring(errno));
 		goto err0;
 	}
 	else if (fstat(mbfd, &fsb) < 0 ||
@@ -1227,9 +1254,9 @@ deliver(fd, name)
 		 !S_ISREG(fsb.st_mode) ||
 		 sb.st_dev != fsb.st_dev ||
 		 sb.st_ino != fsb.st_ino ||
-# if HAS_ST_GEN && 0		/* AFS returns random values for st_gen */
+#if HAS_ST_GEN && 0		/* AFS returns random values for st_gen */
 		 sb.st_gen != fsb.st_gen ||
-# endif
+#endif
 		 sb.st_uid != fsb.st_uid)
 	{
 		ExitVal = EX_TEMPFAIL;
@@ -1279,7 +1306,7 @@ deliver(fd, name)
 	/* Wait until we can get a lock on the file. */
 	if (flock(mbfd, LOCK_EX) < 0)
 	{
-		mailerr("450 4.2.0", "%s: %s", path, sm_errstring(errno));
+		mailerr("450 4.2.0", "Lock %s: %s", path, sm_errstring(errno));
 		goto err1;
 	}
 
@@ -1295,7 +1322,7 @@ deliver(fd, name)
 	/* Copy the message into the file. */
 	if (lseek(fd, (off_t) 0, SEEK_SET) == (off_t) -1)
 	{
-		mailerr("450 4.2.0", "Temporary file: %s",
+		mailerr("450 4.2.0", "Temporary file seek error: %s",
 			sm_errstring(errno));
 		goto err1;
 	}
@@ -1337,7 +1364,7 @@ deliver(fd, name)
 				if (errno == EDQUOT && BounceQuota)
 					errcode = "552 5.2.2";
 #endif
-				mailerr(errcode, "%s: %s",
+				mailerr(errcode, "Write %s: %s",
 					path, sm_errstring(errno));
 				goto err3;
 			}
@@ -1345,7 +1372,7 @@ deliver(fd, name)
 	}
 	if (nr < 0)
 	{
-		mailerr("450 4.2.0", "Temporary file: %s",
+		mailerr("450 4.2.0", "Temporary file read error: %s",
 			sm_errstring(errno));
 		goto err3;
 	}
@@ -1353,7 +1380,7 @@ deliver(fd, name)
 	/* Flush to disk, don't wait for update. */
 	if (!nofsync && fsync(mbfd) < 0)
 	{
-		mailerr("450 4.2.0", "%s: %s", path, sm_errstring(errno));
+		mailerr("450 4.2.0", "Sync %s: %s", path, sm_errstring(errno));
 err3:
 #ifdef DEBUG
 		fprintf(stderr, "reset euid = %d\n", (int) geteuid());
@@ -1362,7 +1389,11 @@ deliver(fd, name)
 			(void) ftruncate(mbfd, curoff);
 err1:		if (mbfd >= 0)
 			(void) close(mbfd);
-err0:		(void) setreuid(0, 0);
+err0:
+#if MAIL_LOCAL_TEST
+		if (HomeMailFile == NULL || user.mbdb_uid != getuid())
+#endif
+		(void) setreuid(0, 0);
 		unlockmbox();
 		return;
 	}
@@ -1390,7 +1421,7 @@ err0:		(void) setreuid(0, 0);
 		if (errno == EDQUOT && BounceQuota)
 			errcode = "552 5.2.2";
 #endif
-		mailerr(errcode, "%s: %s", path, sm_errstring(errno));
+		mailerr(errcode, "Close %s: %s", path, sm_errstring(errno));
 		mbfd = open(path, O_WRONLY, 0);
 		if (mbfd < 0 ||
 		    cursize == 0
@@ -1401,9 +1432,9 @@ err0:		(void) setreuid(0, 0);
 		    !S_ISREG(sb.st_mode) ||
 		    sb.st_dev != fsb.st_dev ||
 		    sb.st_ino != fsb.st_ino ||
-# if HAS_ST_GEN && 0		/* AFS returns random values for st_gen */
+#if HAS_ST_GEN && 0		/* AFS returns random values for st_gen */
 		    sb.st_gen != fsb.st_gen ||
-# endif
+#endif
 		    sb.st_uid != fsb.st_uid
 		   )
 		{
@@ -1421,7 +1452,11 @@ err0:		(void) setreuid(0, 0);
 	else if (!nobiff)
 		notifybiff(biffmsg);
 
-	if (setreuid(0, 0) < 0)
+	if (
+#if MAIL_LOCAL_TEST
+	    (HomeMailFile == NULL || user.mbdb_uid != getuid()) &&
+#endif
+	    setreuid(0, 0) < 0)
 	{
 		mailerr("450 4.2.0", "setreuid(0, 0): %s",
 			sm_errstring(errno));
@@ -1597,11 +1632,12 @@ void
 usage()
 {
 	ExitVal = EX_USAGE;
-# if _FFR_SPOOL_PATH
+	/* XXX add U to options for USE_EAI */
+#if _FFR_SPOOL_PATH
 	mailerr(NULL, "usage: mail.local [-7] [-B] [-b] [-d] [-l] [-s] [-f from|-r from] [-h filename] [-p path] user ...");
-# else
+#else
 	mailerr(NULL, "usage: mail.local [-7] [-B] [-b] [-d] [-l] [-s] [-f from|-r from] [-h filename] user ...");
-# endif
+#endif
 	sm_exit(ExitVal);
 }
 
@@ -1666,7 +1702,7 @@ hashname(name)
 	MD5_CTX ctx;
 	unsigned char md5[18];
 #  if MAXPATHLEN <= 24
-    ERROR _MAXPATHLEN <= 24
+#    ERROR "MAXPATHLEN <= 24"
 #  endif
 	char b64[24];
 	MD5_LONG bits;
diff --git a/contrib/sendmail/mailstats/mailstats.8 b/contrib/sendmail/mailstats/mailstats.8
index 1cbc1aedae91..2bedb3f7f46f 100644
--- a/contrib/sendmail/mailstats/mailstats.8
+++ b/contrib/sendmail/mailstats/mailstats.8
@@ -15,7 +15,7 @@ mailstats
 .SH SYNOPSIS
 .B mailstats
 .RB [ \-c "] [" \-o "] [" \-p "] [" \-P ]
-.RB [ \-C 
+.RB [ \-C
 .IR cffile ]
 .RB [ \-f
 .IR stfile ]
@@ -25,10 +25,10 @@ The
 utility displays the current mail statistics.
 .PP
 First, the time at which statistics started being kept is displayed,
-in the format specified by 
-ctime(3).  
-Then, 
-the statistics for each mailer are displayed on a single line, 
+in the format specified by
+ctime(3).
+Then,
+the statistics for each mailer are displayed on a single line,
 each with the following white space separated fields:
 .sp
 .RS
@@ -36,7 +36,7 @@ each with the following white space separated fields:
 .TP 1.2i
 .B M
 The mailer number.
-.TP 
+.TP
 .B msgsfr
 Number of messages from the mailer.
 .TP
@@ -63,15 +63,15 @@ The name of the mailer.
 .PD
 .RE
 .PP
-After this display, a line totaling the values for all of the mailers 
+After this display, a line totaling the values for all of the mailers
 is displayed (preceded with a ``T''),
-separated from the previous information by a line containing only equals 
-(``='') 
+separated from the previous information by a line containing only equals
+(``='')
 characters.
 Another line preceded with a ``C'' lists the number of TCP connections.
 .PP
 The options are as follows:
-.TP 
+.TP
 .B \-C
 Read the specified file instead of the default
 .B sendmail
@@ -85,9 +85,9 @@ configuration file.
 .B \-f
 Read the specified statistics file instead of the statistics file
 specified in the
-.B sendmail 
+.B sendmail
 configuration file.
-.TP 
+.TP
 .B \-P
 Output information in program-readable mode without clearing statistics.
 .TP
@@ -114,5 +114,5 @@ The default
 statistics file.
 .PD
 .SH SEE ALSO
-mailq(1), 
+mailq(1),
 sendmail(8)
diff --git a/contrib/sendmail/makemap/makemap.c b/contrib/sendmail/makemap/makemap.c
index 4aa8d6ddab38..f2ed0e39f990 100644
--- a/contrib/sendmail/makemap/makemap.c
+++ b/contrib/sendmail/makemap/makemap.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1998-2002, 2004, 2008 Proofpoint, Inc. and its suppliers.
+ * Copyright (c) 1998-2002, 2004, 2008, 2020 Proofpoint, Inc. and its suppliers.
  *	All rights reserved.
  * Copyright (c) 1992 Eric P. Allman.  All rights reserved.
  * Copyright (c) 1992, 1993
@@ -38,6 +38,9 @@ SM_IDSTR(id, "@(#)$Id: makemap.c,v 8.183 2013-11-22 20:51:52 ca Exp $")
 #include 
 #include 
 #include 
+#if USE_EAI
+# include 
+#endif
 
 uid_t	RealUid;
 gid_t	RealGid;
@@ -67,6 +70,11 @@ usage(progname)
 	sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
 		      "       %*s [-d] [-e] [-f] [-l] [-o] [-r] [-s] [-t delimiter]\n",
 		      (int) strlen(progname), "");
+#if _FFR_TESTS
+	sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
+		      "       %*s [-S n]\n",
+		      (int) strlen(progname), "");
+#endif
 	sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
 		      "       %*s [-u] [-v] type mapname\n",
 		      (int) strlen(progname), "");
@@ -286,6 +294,12 @@ main(argc, argv)
 	static char rnamebuf[MAXNAME];	/* holds RealUserName */
 	extern char *optarg;
 	extern int optind;
+#if USE_EAI
+	bool ascii = true;
+#endif
+#if _FFR_TESTS
+	int slp = 0;
+#endif
 
 	memset(¶ms, '\0', sizeof params);
 	params.smdbp_cache_size = 1024 * 1024;
@@ -313,7 +327,12 @@ main(argc, argv)
 		       SMDB_MAX_USER_NAME_LEN);
 
 #define OPTIONS		"C:D:Nc:defi:Llorst:uvx"
-	while ((opt = getopt(argc, argv, OPTIONS)) != -1)
+#if _FFR_TESTS
+# define X_OPTIONS		"S:"
+#else
+# define X_OPTIONS
+#endif
+	while ((opt = getopt(argc, argv, OPTIONS X_OPTIONS)) != -1)
 	{
 		switch (opt)
 		{
@@ -369,6 +388,12 @@ main(argc, argv)
 			allowreplace = true;
 			break;
 
+#if _FFR_TESTS
+		  case 'S':
+			slp = atoi(optarg);
+			break;
+#endif
+
 		  case 's':
 			setbitn(DBS_MAPINUNSAFEDIRPATH, DontBlameSendmail);
 			setbitn(DBS_WRITEMAPTOHARDLINK, DontBlameSendmail);
@@ -393,7 +418,7 @@ main(argc, argv)
 		  case 'v':
 			verbose = true;
 			break;
- 
+
 		  case 'x':
 			smdb_print_available_types(true);
 			exit(EX_OK);
@@ -602,12 +627,41 @@ main(argc, argv)
 			memset(&db_val, '\0', sizeof db_val);
 			db_key.data = ibuf;
 
-			for (p = ibuf; *p != '\0' && !(ISSEP(*p)); p++)
+#if USE_EAI
+			db_key.size = 0;
+			if (foldcase)
+			{
+				for (p = ibuf; *p != '\0' && !ISSEP(*p); p++)
+				{
+					if (!ISASCII(*p))
+						ascii = false;
+				}
+				if (!ascii)
+				{
+					char sep;
+					char *lkey;
+
+					sep = *p;
+					*p = '\0';
+
+					lkey = sm_lowercase(ibuf);
+					db_key.data = lkey;
+					db_key.size = strlen(lkey);
+					*p = sep;
+				}
+			}
+			if (ascii)
+#endif /* USE_EAI */
+			/* NOTE: see if () above! */
+			for (p = ibuf; *p != '\0' && !ISSEP(*p); p++)
 			{
 				if (foldcase && ISASCII(*p) && isupper(*p))
 					*p = tolower(*p);
 			}
-			db_key.size = p - ibuf;
+#if USE_EAI
+			if (0 == db_key.size)
+#endif
+				db_key.size = p - ibuf;
 			if (inclnull)
 				db_key.size++;
 
@@ -680,6 +734,11 @@ main(argc, argv)
 		}
 	}
 
+#if _FFR_TESTS
+	if (slp > 0)
+		sleep(slp);
+#endif
+
 	/*
 	**  Now close the database.
 	*/
diff --git a/contrib/sendmail/praliases/praliases.8 b/contrib/sendmail/praliases/praliases.8
index 8a8f66d9055f..74d11100756e 100644
--- a/contrib/sendmail/praliases/praliases.8
+++ b/contrib/sendmail/praliases/praliases.8
@@ -24,7 +24,7 @@ praliases
 .SH DESCRIPTION
 The
 .B praliases
-utility displays the current system aliases, 
+utility displays the current system aliases,
 one per line, in no particular order.
 The special internal @:@ alias will be displayed if present.
 .PP
@@ -34,7 +34,7 @@ The options are as follows:
 Read the specified sendmail configuration file instead of the default
 .B sendmail
 configuration file.
-.TP 
+.TP
 .BI "\-f " file
 Read the specified file instead of the configured
 .B sendmail
@@ -53,5 +53,5 @@ The default
 .B sendmail
 configuration file.
 .SH SEE ALSO
-mailq(1), 
+mailq(1),
 sendmail(8)
diff --git a/contrib/sendmail/praliases/praliases.c b/contrib/sendmail/praliases/praliases.c
index 682d4e14d2ea..2239bd1b9b39 100644
--- a/contrib/sendmail/praliases/praliases.c
+++ b/contrib/sendmail/praliases/praliases.c
@@ -87,7 +87,7 @@ main(argc, argv)
 	RunAsUserName = RealUserName = rnamebuf;
 
 	cfile = getcfname(0, 0, SM_GET_SENDMAIL_CF, NULL);
-	while ((ch = getopt(argc, argv, "C:f:")) != -1)
+	while ((ch = getopt(argc, argv, "C:f:l")) != -1)
 	{
 		switch ((char)ch) {
 		case 'C':
@@ -96,6 +96,11 @@ main(argc, argv)
 		case 'f':
 			filename = optarg;
 			break;
+
+		case 'l':
+			smdb_print_available_types(false);
+			exit(EX_OK);
+			break;
 		case '?':
 		default:
 			(void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
diff --git a/contrib/sendmail/rmail/rmail.8 b/contrib/sendmail/rmail/rmail.8
index 3d2a8c5ec6bd..e93f3e1c6f53 100644
--- a/contrib/sendmail/rmail/rmail.8
+++ b/contrib/sendmail/rmail/rmail.8
@@ -19,23 +19,23 @@ rmail
 .RB [ \-D
 .IR domain ]
 .RB [ \-T ]
-.I 
+.I
 user ...
 .SH DESCRIPTION
 .B Rmail
-interprets incoming mail received via 
-uucp(1), 
-collapsing ``From'' lines in the form generated 
-by 
-mail.local(8) 
-into a single line of the form ``return-path!sender'', 
-and passing the processed mail on to 
+interprets incoming mail received via
+uucp(1),
+collapsing ``From'' lines in the form generated
+by
+mail.local(8)
+into a single line of the form ``return-path!sender'',
+and passing the processed mail on to
 sendmail(8).
 .PP
 .B Rmail
-is explicitly designed for use with 
-uucp 
-and 
+is explicitly designed for use with
+uucp
+and
 sendmail.
 .SS Flags
 .TP
@@ -47,15 +47,15 @@ instead of the default domain of ``UUCP''.
 .B \-T
 Turn on debugging.
 .SH SEE ALSO
-uucp(1), 
-mail.local(8), 
+uucp(1),
+mail.local(8),
 sendmail(8)
 .SH HISTORY
 The
 .B rmail
-program appeared in 
+program appeared in
 4.2BSD.
 .SH BUGS
 .B Rmail
-should not reside in 
+should not reside in
 /bin.
diff --git a/contrib/sendmail/smrsh/smrsh.c b/contrib/sendmail/smrsh/smrsh.c
index 8c4e47ca200b..1e6cd28c4f59 100644
--- a/contrib/sendmail/smrsh/smrsh.c
+++ b/contrib/sendmail/smrsh/smrsh.c
@@ -377,7 +377,7 @@ main(argc, argv)
 #if ALLOWSEMI
 		if (*r == ';')
 		{
-			addcmd(p, false,  r - p + 1);
+			addcmd(p, false, r - p + 1);
 			q = r + 1;
 			continue;
 		}
@@ -385,7 +385,7 @@ main(argc, argv)
 		if ((*r == '&' && *(r + 1) == '&') ||
 		    (*r == '|' && *(r + 1) == '|'))
 		{
-			addcmd(p, false,  r - p + 2);
+			addcmd(p, false, r - p + 2);
 			q = r + 2;
 			continue;
 		}
diff --git a/contrib/sendmail/src/README b/contrib/sendmail/src/README
index 06d727de4baf..81f5bfbce147 100644
--- a/contrib/sendmail/src/README
+++ b/contrib/sendmail/src/README
@@ -8,9 +8,6 @@
 # forth in the LICENSE file which can be found at the top level of
 # the sendmail distribution.
 #
-#
-#	$Id: README,v 8.393 2013-11-22 20:51:54 ca Exp $
-#
 
 This directory contains the source files for sendmail(TM).
 
@@ -133,8 +130,7 @@ MAP_REGEX	Regular Expression support.  You will need to use an
 		routines or install a regexp library such as libregex from
 		the Free Software Foundation.
 DNSMAP		DNS map support.  Requires NAMED_BIND.
-PH_MAP		PH map support.  You will need the libphclient library from
-		the nph package (http://www-dev.cites.uiuc.edu/ph/nph/).
+PH_MAP		PH map support.
 MAP_NSD		nsd map support (IRIX 6.5 and later).
 SOCKETMAP	Support for a trivial query protocol over UNIX domain or TCP
 		sockets.
@@ -337,6 +333,8 @@ NEEDPUTENV	Define this if your system needs am emulation of the
 NOFTRUNCATE	Define this if you don't have the ftruncate(2) syscall.
 		If you don't have this system call, there is an unavoidable
 		race condition that occurs when creating alias databases.
+NO_EOH_FIELDS	Define this to disable the special handling of the headers
+		Message: and Text: to denote the end of the message header.
 GIDSET_T	The type of entries in a gidset passed as the second
 		argument to getgroups(2).  Historically this has been an
 		int, so this is the default, but some systems (such as
@@ -767,25 +765,23 @@ From: Garrett Wollman 
     certificate authentication -- even some of those which already support
     SSL/TLS for confidentiality.
 
-Further information can be found via:
-http://www.sendmail.org/tips/
-
 
 +------------------------------------+
 | SASL COMPILATION AND CONFIGURATION |
 +------------------------------------+
 
 Please read the documentation accompanying the Cyrus SASL library
-(INSTALL and README).  If you use Berkeley DB for Cyrus SASL then
-you must compile sendmail with the same version of Berkeley DB.
-See devtools/README for how to set the correct compile time parameters;
-you should at least set the following variables:
+(INSTALL and README, especially about Sendmail.conf).  If you use
+Berkeley DB for Cyrus SASL then you must compile sendmail with the
+same version of Berkeley DB.  See devtools/README for how to set
+the correct compile time parameters; you should at least set the
+following variables:
 
-APPENDDEF(`conf_sendmail_ENVDEF', `-DSASL')
-APPENDDEF(`conf_sendmail_LIBS', `-lsasl')
+APPENDDEF(`conf_sendmail_ENVDEF', `-DSASL=2')
+APPENDDEF(`conf_sendmail_LIBS', `-lsasl2')
 
 If you have installed the Cyrus SASL library and include files in
-a location that your C compiler doesn't use by default you should
+a location which your C compiler doesn't use by default you should
 set confINCDIRS and confLIBDIRS as explained in the first section:
 BUILDING SENDMAIL.
 
@@ -794,7 +790,8 @@ sendmail where to find the sasl library and the include files (see
 devtools/README for the parameters to set).  Set up the required
 users and passwords as explained in the SASL documentation.  See
 also cf/README for authentication related options (especially
-DefaultAuthInfo if you want authentication between MTAs).
+"Providing SMTP AUTH Data when sendmail acts as Client"
+if you want authentication between MTAs).
 
 To perform an initial test, connect to your sendmail daemon
 (telnet localhost 25) and issue a EHLO localhost and see whether
@@ -804,9 +801,6 @@ is in the response.  If it isn't, run the daemon with
 and try again.  Then take a look at the logfile and see whether
 there are any security related problems listed (unsafe files).
 
-Further information can be found via:
-http://www.sendmail.org/tips/
-
 
 +-------------------------------------+
 | OPERATING SYSTEM AND COMPILE QUIRKS |
@@ -1180,10 +1174,6 @@ IRIX
 
 	These are unavoidable and innocuous -- just ignore them.
 
-	According to Dave Sill , there is a version of the
-	Berkeley DB library patched to run on Irix 6.2 available from
-	http://reality.sgi.com/ariel/freeware/#db .
-
 IRIX 6.x
 	If you are using XFS filesystem, avoid using the -32 ABI switch to
 	the cc compiler if possible.
@@ -1580,7 +1570,6 @@ Darwin/Mac OS X (10.X.X)
 
 	From scratch here is what Darwin users need to do to the standard
 	10.0.0, 10.0.1 install to get sendmail working.
-	From http://www.macosx.com/forums/showthread.php?s=6dac0e9e1f3fd118a4870a8a9b559491&threadid=2242:
 	1. chmod g-w / /private /private/etc
 	2. Properly set HOSTNAME in /etc/hostconfig to your FQDN:
 	   HOSTNAME=-my.domain.com-
@@ -1695,8 +1684,7 @@ Listproc 6.0c
 	as well. :)
 
 PH
-	PH support is provided by Mark Roth .  The map is
-	described at http://www-dev.cites.uiuc.edu/sendmail/ .
+	PH support is provided by Mark Roth .
 
 	NOTE: The "spacedname" pseudo-field which was used by earlier
 	versions of the PH map code is no longer supported!  See the URL
@@ -1866,4 +1854,30 @@ util.c		Some general purpose routines used by sendmail.
 version.c	The version number and information about this
 		version of sendmail.
 
-(Version $Revision: 8.393 $, last update $Date: 2013-11-22 20:51:54 $ )
+
++---------------------------+
+| SOME NOTES ABOUT THE CODE |
++---------------------------+
+
+Some things are not easy to understand by just reading the source
+code, so this section has some notes which might be interesting for
+those who want to enhance sendmail.  These notes are not exhaustive
+but just cover some things which might be interesting.
+
+Address format: sendmail uses a range of 8 bit characters for its
+internal purposes as noted in sendmail.h:
+
+**  Special characters in rewriting rules.
+**	These are used internally only.
+
+To handle all 8 bit characters, sendmail uses two address formats:
+internal and external -- for details see the comments in cataddr()
+as well as the functions quote_internal_chars() and
+dequote_internal_chars() in libsm/util.c.
+
+These formats are marked in many places with [i] and [x] respectively.
+Some functions only work on one kind of those formats, so it is
+important to mark the strings accordingly. In some cases the marker
+[A] is used to denote that the string format does not matter (which
+is the default) -- this is only used in cases where there might be
+some confusion about any format requirements.
diff --git a/contrib/sendmail/src/TRACEFLAGS b/contrib/sendmail/src/TRACEFLAGS
index df5df6bded58..0b9ccd989320 100644
--- a/contrib/sendmail/src/TRACEFLAGS
+++ b/contrib/sendmail/src/TRACEFLAGS
@@ -78,13 +78,16 @@
 64	multiple	Milter
 65	main.c		permission checks
 #if DANE
+66	domain.c	force port=25 for TLSA RR lookups
 67	domain.c	TLSA RR lookups
 #endif
+68	unused
 #if _FFR_QUEUE_SCHED_DBG
 69	queue.c		scheduling
 #endif
 70	queue.c		quarantining
 71,>99	milter.c	quarantine on errors
+72	unused
 73	queue.c		shared memory updates
 74,>99	map.c		LDAP map defer
 #if _FFR_XCNCT
@@ -93,16 +96,21 @@
 #if _FFR_TESTS
 76,>99	queue.c		run_work_group: sleep
 77,>99	daemon.c	change delivery host/port
+78,>99	queue.c		generate 15 char queue ids
+79,>99	alias.c		rebuild aliases: sleep
 #endif
 80			content length
 81			sun remote mode
+82,>99	parseaddr.c	disable clearing bit 8 on addresses
 83	collect.c	timeout
 84	deliver.c	timeout
 85	map.c		dprintf map
 #if _FFR_PROXY
 87	srvrsmtp.c	proxy mode
 #endif
+88,>99	tls.c		disable the effect of _FFR_VRFY_TRUSTED_FIRST
 89	conf.c		>=8 use sm_dprintf() instead of syslog()
+90	unused
 91	mci.c		syslogging of MCI cache information
 92	EF_LOGSENDER
 93,>99	*		Prevent daemon connection fork for profiling/debugging
diff --git a/contrib/sendmail/src/alias.c b/contrib/sendmail/src/alias.c
index 70d8452eb08c..e3ee68f21852 100644
--- a/contrib/sendmail/src/alias.c
+++ b/contrib/sendmail/src/alias.c
@@ -15,6 +15,8 @@
 
 SM_RCSID("@(#)$Id: alias.c,v 8.221 2013-11-22 20:51:54 ca Exp $")
 
+#include 
+
 #define SEPARATOR ':'
 # define ALIAS_SPEC_SEPARATORS	" ,/:"
 
@@ -58,7 +60,7 @@ alias(a, sendq, aliaslevel, e)
 	register char *p;
 	char *owner;
 	auto int status = EX_OK;
-	char obuf[MAXNAME + 7];
+	char obuf[MAXNAME_I + 7];
 
 	if (tTd(27, 1))
 		sm_dprintf("alias(%s)\n", a->q_user);
@@ -186,11 +188,12 @@ alias(a, sendq, aliaslevel, e)
 	e->e_flags |= EF_SENDRECEIPT;
 	a->q_flags |= QDELIVERED|QEXPANDED;
 }
+
 /*
 **  ALIASLOOKUP -- look up a name in the alias file.
 **
 **	Parameters:
-**		name -- the name to look up.
+**		name -- the name to look up [i]
 **		pstat -- a pointer to a place to put the status.
 **		av -- argument for %1 expansion.
 **
@@ -198,9 +201,6 @@ alias(a, sendq, aliaslevel, e)
 **		the value of name.
 **		NULL if unknown.
 **
-**	Side Effects:
-**		none.
-**
 **	Warnings:
 **		The return value will be trashed across calls.
 */
@@ -212,9 +212,15 @@ aliaslookup(name, pstat, av)
 	char *av;
 {
 	static MAP *map = NULL;
+	char *res;
 #if _FFR_ALIAS_DETAIL
 	int i;
 	char *argv[4];
+#else
+# define argv NULL
+#endif
+#if _FFR_8BITENVADDR
+	char buf[MAXNAME];	/* EAI:ok */
 #endif
 
 	if (map == NULL)
@@ -228,8 +234,13 @@ aliaslookup(name, pstat, av)
 	DYNOPENMAP(map);
 
 	/* special case POstMastER -- always use lower case */
-	if (sm_strcasecmp(name, "postmaster") == 0)
+	if (SM_STRCASEEQ(name, "postmaster"))
 		name = "postmaster";
+#if _FFR_8BITENVADDR
+	(void) dequote_internal_chars(name, buf, sizeof(buf));
+	/* check length? */
+	name = buf;
+#endif /* _FFR_8BITENVADDR */
 
 #if _FFR_ALIAS_DETAIL
 	i = 0;
@@ -240,11 +251,14 @@ aliaslookup(name, pstat, av)
 	if (av != NULL && *av == '+')
 		argv[i++] = av + 1;
 	argv[i++] = NULL;
-	return (*map->map_class->map_lookup)(map, name, argv, pstat);
-#else /* _FFR_ALIAS_DETAIL */
-	return (*map->map_class->map_lookup)(map, name, NULL, pstat);
 #endif /* _FFR_ALIAS_DETAIL */
+	res = (*map->map_class->map_lookup)(map, name, argv, pstat);
+#if _FFR_8BITENVADDR
+	/* map_lookup() does a map_rewrite(), so no quoting here */
+#endif
+	return res;
 }
+
 /*
 **  SETALIAS -- set up an alias map
 **
@@ -601,6 +615,16 @@ rebuildaliases(map, automatic)
 	/* add distinguished entries and close the database */
 	if (bitset(MF_OPEN, map->map_mflags))
 	{
+#if _FFR_TESTS
+		if (tTd(78, 101))
+		{
+			int sl;
+
+			sl = tTdlevel(78) - 100;
+			sm_dprintf("rebuildaliases: sleep=%d\n", sl);
+			sleep(sl);
+		}
+#endif
 		map->map_mflags |= MF_CLOSING;
 		map->map_class->map_close(map);
 		map->map_mflags &= ~(MF_OPEN|MF_WRITABLE|MF_CLOSING);
@@ -647,7 +671,13 @@ readaliases(map, af, announcestats, logstats)
 	bool skipping;
 	long naliases, bytes, longest;
 	ADDRESS al, bl;
-	char line[BUFSIZ];
+	char lbuf[BUFSIZ];
+	char *line;
+#if _FFR_8BITENVADDR
+	char lhsbuf[MAXNAME];	/* EAI:ok */
+	char rhsbuf[BUFSIZ];
+	int len;
+#endif
 
 	/*
 	**  Read and interpret lines
@@ -657,12 +687,21 @@ readaliases(map, af, announcestats, logstats)
 	LineNumber = 0;
 	naliases = bytes = longest = 0;
 	skipping = false;
-	while (sm_io_fgets(af, SM_TIME_DEFAULT, line, sizeof(line)) >= 0)
+	line = NULL;
+	while (sm_io_fgets(af, SM_TIME_DEFAULT, lbuf, sizeof(lbuf)) >= 0)
 	{
 		int lhssize, rhssize;
 		int c;
 
 		LineNumber++;
+#if _FFR_8BITENVADDR
+		if (line != lbuf)
+			SM_FREE(line);
+		len = 0;
+		line = quote_internal_chars(lbuf, NULL, &len, NULL);
+#else
+		line = lbuf;
+#endif
 		p = strchr(line, '\n');
 
 		/* XXX what if line="a\\" ? */
@@ -723,6 +762,7 @@ readaliases(map, af, announcestats, logstats)
 			syserr("554 5.3.5 missing colon");
 			continue;
 		}
+/* XXX line must be [i] */
 		if (parseaddr(line, &al, RF_COPYALL, ':', NULL, CurEnv, true)
 		    == NULL)
 		{
@@ -758,6 +798,7 @@ readaliases(map, af, announcestats, logstats)
 						p++;
 					if (*p == '\0')
 						break;
+/* XXX p must be [i] */
 					if (parseaddr(p, &bl, RF_COPYNONE, ',',
 						      &delimptr, CurEnv, true)
 					    == NULL)
@@ -811,8 +852,8 @@ readaliases(map, af, announcestats, logstats)
 		**	Special case pOStmaStER -- always make it lower case.
 		*/
 
-		if (sm_strcasecmp(al.q_user, "postmaster") == 0)
-			makelower(al.q_user);
+		if (SM_STRCASEEQ(al.q_user, "postmaster"))
+			makelower_a(&al.q_user, CurEnv->e_rpool);
 
 		lhssize = strlen(al.q_user);
 		rhssize = strlen(rhs);
@@ -831,7 +872,13 @@ readaliases(map, af, announcestats, logstats)
 		}
 		else
 		{
+#if _FFR_8BITENVADDR
+			dequote_internal_chars(al.q_user, lhsbuf, sizeof(lhsbuf));
+			dequote_internal_chars(rhs, rhsbuf, sizeof(rhsbuf));
+			map->map_class->map_store(map, lhsbuf, rhsbuf);
+#else
 			map->map_class->map_store(map, al.q_user, rhs);
+#endif
 
 			/* statistics */
 			naliases++;
@@ -859,8 +906,7 @@ readaliases(map, af, announcestats, logstats)
 **	Parameters:
 **		user -- the name of the user who's mail we would like
 **			to forward to.  It must have been verified --
-**			i.e., the q_home field must have been filled
-**			in.
+**			i.e., the q_home field must have been filled in.
 **		sendq -- a pointer to the head of the send queue to
 **			put this user's aliases in.
 **		aliaslevel -- the current alias nesting depth.
diff --git a/contrib/sendmail/src/aliases.5 b/contrib/sendmail/src/aliases.5
index f09b49cf5e0c..cb6750876830 100644
--- a/contrib/sendmail/src/aliases.5
+++ b/contrib/sendmail/src/aliases.5
@@ -18,13 +18,13 @@ aliases
 .SH SYNOPSIS
 .B aliases
 .SH DESCRIPTION
-This file describes user 
-ID 
-aliases used by 
+This file describes user
+ID
+aliases used by
 sendmail.
-The file resides in 
-/etc/mail 
-and 
+The file resides in
+/etc/mail
+and
 is formatted as a series of lines of the form
 .IP
 name: addr_1, addr_2, addr_3, . . .
@@ -33,7 +33,7 @@ The
 .I name
 is the name to alias, and the
 .I addr_n
-are the aliases for that name.  
+are the aliases for that name.
 .I addr_n
 can be another alias, a local username, a local filename,
 a command,
@@ -68,14 +68,14 @@ user@domain
 .IP
 An e-mail address in RFC 822 format.
 .PP
-Lines beginning with white space are continuation lines.  
-Another way to continue lines is by placing a backslash 
-directly before a newline.  
-Lines beginning with 
-# 
+Lines beginning with white space are continuation lines.
+Another way to continue lines is by placing a backslash
+directly before a newline.
+Lines beginning with
+#
 are comments.
 .PP
-Aliasing occurs only on local names.  
+Aliasing occurs only on local names.
 Loops can not occur, since no message will be sent to any person more than once.
 .PP
 If an alias is found for
@@ -89,43 +89,43 @@ If it is found and the result expands to more than one address, the
 envelope sender address is changed to
 .IR owner-name .
 .PP
-After aliasing has been done, local and valid recipients who have a 
-``.forward'' 
-file in their home directory have messages forwarded to the 
+After aliasing has been done, local and valid recipients who have a
+``.forward''
+file in their home directory have messages forwarded to the
 list of users defined in that file.
 .PP
 This is only the raw data file; the actual aliasing information is
-placed into a binary format in the file 
+placed into a binary format in the file
 /etc/mail/aliases.db
-using the program 
-newaliases(1).  
-A 
+using the program
+newaliases(1).
+A
 newaliases
-command should be executed each time the aliases file is changed for the 
+command should be executed each time the aliases file is changed for the
 change to take effect.
 .SH SEE  ALSO
-newaliases(1), 
-dbm(3), 
-dbopen(3), 
-db_open(3), 
+newaliases(1),
+dbm(3),
+dbopen(3),
+db_open(3),
 sendmail(8)
 .PP
 .I
 SENDMAIL Installation and Operation Guide.
 .PP
-.I 
+.I
 SENDMAIL An Internetwork Mail Router.
 .SH BUGS
-If you have compiled 
-sendmail 
+If you have compiled
+sendmail
 with DBM support instead of NEWDB,
-you may have encountered problems in 
-dbm(3) 
-restricting a single alias to about 1000 bytes of information.  
-You can get longer aliases by ``chaining''; that is, make the last name in 
+you may have encountered problems in
+dbm(3)
+restricting a single alias to about 1000 bytes of information.
+You can get longer aliases by ``chaining''; that is, make the last name in
 the alias be a dummy name which is a continuation alias.
 .SH HISTORY
 The
 .B aliases
-file format appeared in 
+file format appeared in
 4.0BSD.
diff --git a/contrib/sendmail/src/collect.c b/contrib/sendmail/src/collect.c
index 526519050ca6..762c60155f9b 100644
--- a/contrib/sendmail/src/collect.c
+++ b/contrib/sendmail/src/collect.c
@@ -15,6 +15,8 @@
 
 SM_RCSID("@(#)$Id: collect.c,v 8.287 2013-11-22 20:51:55 ca Exp $")
 
+#include 
+
 static void	eatfrom __P((char *volatile, ENVELOPE *));
 static void	collect_doheader __P((ENVELOPE *));
 static SM_FILE_T *collect_dfopen __P((ENVELOPE *));
@@ -846,8 +848,7 @@ collect(fp, smtpmode, hdrp, e, rsetsize)
 			q->q_state = QS_FATALERR;
 		}
 
-		(void) sm_io_close(df, SM_TIME_DEFAULT);
-		df = NULL;
+		SM_CLOSE_FP(df);
 		finis(true, true, ExitStat);
 		/* NOTREACHED */
 	}
@@ -895,7 +896,7 @@ collect(fp, smtpmode, hdrp, e, rsetsize)
 	{
 		/* if it claimed to be 8 bits, well, it lied.... */
 		if (e->e_bodytype != NULL &&
-		    sm_strcasecmp(e->e_bodytype, "8BITMIME") == 0)
+		    SM_STRCASEEQ(e->e_bodytype, "8bitmime"))
 			e->e_bodytype = "7BIT";
 	}
 
@@ -939,6 +940,8 @@ collect(fp, smtpmode, hdrp, e, rsetsize)
 
   end:
 	(void) set_tls_rd_tmo(old_rd_tmo);
+	if (buf != bufbuf)
+		SM_FREE(buf);
 }
 
 /*
diff --git a/contrib/sendmail/src/conf.c b/contrib/sendmail/src/conf.c
index 63c545cb27e1..e7a9615c23ab 100644
--- a/contrib/sendmail/src/conf.c
+++ b/contrib/sendmail/src/conf.c
@@ -25,16 +25,14 @@ SM_RCSID("@(#)$Id: conf.c,v 8.1192 2014-01-27 18:23:21 ca Exp $")
 #include "map.h"
 #include 
 
-#ifdef DEC
-# if NETINET6
+#if defined(DEC) && NETINET6
 /* for the IPv6 device lookup */
-#  define _SOCKADDR_LEN
-#  include 
-# endif /* NETINET6 */
-#endif /* DEC */
+# define _SOCKADDR_LEN
+# include 
+#endif
 
-# include 
-# include 
+#include 
+#include 
 
 #include 
 #if NETINET || NETINET6
@@ -94,6 +92,12 @@ static struct hostent *sm_getipnodebyaddr __P((const void *, size_t, int, int *)
 **	something with them.
 */
 
+#if _FFR_MTA_MODE
+# define Xflags	H_ASIS
+#else
+# define Xflags 0
+#endif
+
 struct hdrinfo	HdrInfo[] =
 {
 		/* originator fields, most to least significant */
@@ -101,8 +105,8 @@ struct hdrinfo	HdrInfo[] =
 	{ "resent-from",		H_FROM|H_RESENT,	NULL	},
 	{ "resent-reply-to",		H_FROM|H_RESENT,	NULL	},
 	{ "sender",			H_FROM,			NULL	},
-	{ "from",			H_FROM,			NULL	},
-	{ "reply-to",			H_FROM,			NULL	},
+	{ "from",			H_FROM | Xflags,	NULL	},
+	{ "reply-to",			H_FROM | Xflags,	NULL	},
 	{ "errors-to",			H_FROM|H_ERRORSTO,	NULL	},
 	{ "full-name",			H_ACHECK,		NULL	},
 	{ "return-receipt-to",		H_RECEIPTTO,		NULL	},
@@ -110,7 +114,7 @@ struct hdrinfo	HdrInfo[] =
 	{ "disposition-notification-to",	H_FROM,		NULL	},
 
 		/* destination fields */
-	{ "to",				H_RCPT,			NULL	},
+	{ "to",				H_RCPT | Xflags,	NULL	},
 	{ "resent-to",			H_RCPT|H_RESENT,	NULL	},
 	{ "cc",				H_RCPT,			NULL	},
 	{ "resent-cc",			H_RCPT|H_RESENT,	NULL	},
@@ -148,8 +152,6 @@ struct hdrinfo	HdrInfo[] =
 	{ NULL,				0,			NULL	}
 };
 
-
-
 /*
 **  Privacy values
 */
@@ -172,6 +174,9 @@ struct prival PrivacyValues[] =
 	{ "nobodyreturn",	PRIV_NOBODYRETN		},
 	{ "goaway",		PRIV_GOAWAY		},
 	{ "noactualrecipient",	PRIV_NOACTUALRECIPIENT	},
+#if _FFR_NOREFLECT
+	{ "noreflection",	PRIV_NOREFLECTION	},
+#endif
 	{ NULL,			0			}
 };
 
@@ -320,7 +325,7 @@ setdefaults(e)
 	e->e_xfqgrp = NOQGRP;
 	e->e_xfqdir = NOQDIR;
 	e->e_ctime = curtime();
-#if _FFR_EAI
+#if USE_EAI
 	e->e_smtputf8 = false;
 #endif
 	SevenBitInput = false;			/* option 7 */
@@ -405,9 +410,9 @@ setdefaults(e)
 	}
 	else
 		InetMode = AF_INET;
-#if !IPV6_FULL
+# if !IPV6_FULL
 	UseCompressedIPv6Addresses = true;
-#endif
+# endif
 #else /* NETINET6 */
 	InetMode = AF_INET;
 #endif /* NETINET6 */
@@ -1038,9 +1043,9 @@ switch_map_find(service, maptype, mapreturn)
 				p = strpbrk(buf, "#\n");
 				if (p != NULL)
 					*p = '\0';
-#ifndef SM_NSSWITCH_DELIMS
-# define SM_NSSWITCH_DELIMS	" \t"
-#endif
+# ifndef SM_NSSWITCH_DELIMS
+#  define SM_NSSWITCH_DELIMS	" \t"
+# endif
 				p = strpbrk(buf, SM_NSSWITCH_DELIMS);
 				if (p != NULL)
 					*p++ = '\0';
@@ -1185,14 +1190,14 @@ char *
 username()
 {
 	static char *myname = NULL;
-	extern char *getlogin();
+	extern char *getlogin __P((void));
 	register struct passwd *pw;
 
 	/* cache the result */
 	if (myname == NULL)
 	{
 		myname = getlogin();
-		if (myname == NULL || myname[0] == '\0')
+		if (SM_IS_EMPTY(myname))
 		{
 			pw = sm_getpwuid(RealUid);
 			if (pw != NULL)
@@ -1210,7 +1215,7 @@ username()
 					myname = pw->pw_name;
 			}
 		}
-		if (myname == NULL || myname[0] == '\0')
+		if (SM_IS_EMPTY(myname))
 		{
 			syserr("554 5.3.0 Who are you?");
 			myname = "postmaster";
@@ -1251,8 +1256,8 @@ ttypath()
 {
 	struct stat stbuf;
 	register char *pathn;
-	extern char *ttyname();
-	extern char *getlogin();
+	extern char *ttyname __P((int));
+	extern char *getlogin __P((void));
 
 	/* compute the pathname of the controlling tty */
 	if ((pathn = ttyname(2)) == NULL && (pathn = ttyname(1)) == NULL &&
@@ -1543,7 +1548,7 @@ getla()
 #   endif
 #  endif /* LA_TYPE == LA_SHORT */
 # endif /* LA_TYPE == LA_INT */
-	extern off_t lseek();
+	extern off_t lseek __P((int, off_t, int));
 
 	if (kmem < 0)
 	{
@@ -2546,13 +2551,13 @@ initsetproctitle(argc, argv, envp)
 	**  Use all contiguous argv and envp pointers starting at argv[0]
 	*/
 
-# if _FFR_SPT_ALIGN
+#if _FFR_SPT_ALIGN
 	align = -1;
-#  ifdef SPT_ALIGN_SIZE
+# ifdef SPT_ALIGN_SIZE
 	for (i = SPT_ALIGN_SIZE; i > 0; i >>= 1)
 		align++;
-#  endif
-# endif /* _FFR_SPT_ALIGN */
+# endif
+#endif /* _FFR_SPT_ALIGN */
 
 	for (i = 0; i < argc; i++)
 	{
@@ -2760,29 +2765,29 @@ pid_t
 sm_wait(status)
 	int *status;
 {
-# ifdef WAITUNION
+#ifdef WAITUNION
 	union wait st;
-# else
+#else
 	auto int st;
-# endif
+#endif
 	pid_t i;
-# if defined(ISC_UNIX) || defined(_SCO_unix_)
+#if defined(ISC_UNIX) || defined(_SCO_unix_)
 	int savesig;
-# endif
+#endif
 
-# if defined(ISC_UNIX) || defined(_SCO_unix_)
+#if defined(ISC_UNIX) || defined(_SCO_unix_)
 	savesig = sm_releasesignal(SIGCHLD);
-# endif
+#endif
 	i = wait(&st);
-# if defined(ISC_UNIX) || defined(_SCO_unix_)
+#if defined(ISC_UNIX) || defined(_SCO_unix_)
 	if (savesig > 0)
 		sm_blocksignal(SIGCHLD);
-# endif
-# ifdef WAITUNION
+#endif
+#ifdef WAITUNION
 	*status = st.w_status;
-# else
+#else
 	*status = st;
-# endif
+#endif
 	return i;
 }
 /*
@@ -2811,7 +2816,7 @@ reapchild(sig)
 	int save_errno = errno;
 	int st;
 	pid_t pid;
-# if HASWAITPID
+#if HASWAITPID
 	auto int status;
 	int count;
 
@@ -2821,14 +2826,14 @@ reapchild(sig)
 		st = status;
 		if (count++ > 1000)
 			break;
-# else /* HASWAITPID */
-#  ifdef WNOHANG
+#else /* HASWAITPID */
+# ifdef WNOHANG
 	union wait status;
 
 	while ((pid = wait3(&status, WNOHANG, (struct rusage *) NULL)) > 0)
 	{
 		st = status.w_status;
-#  else /* WNOHANG */
+# else /* WNOHANG */
 	auto int status;
 
 	/*
@@ -2841,8 +2846,8 @@ reapchild(sig)
 	if ((pid = wait(&status)) > 0)
 	{
 		st = status;
-#  endif /* WNOHANG */
-# endif /* HASWAITPID */
+# endif /* WNOHANG */
+#endif /* HASWAITPID */
 		/* Drop PID and check if it was a control socket child */
 		proc_list_drop(pid, st, NULL);
 	}
@@ -2872,22 +2877,22 @@ reapchild(sig)
 int
 getdtsize()
 {
-# ifdef RLIMIT_NOFILE
+#ifdef RLIMIT_NOFILE
 	struct rlimit rl;
 
 	if (getrlimit(RLIMIT_NOFILE, &rl) >= 0)
 		return rl.rlim_cur;
-# endif /* RLIMIT_NOFILE */
+#endif /* RLIMIT_NOFILE */
 
-# if HASGETDTABLESIZE
+#if HASGETDTABLESIZE
 	return getdtablesize();
-# else /* HASGETDTABLESIZE */
-#  ifdef _SC_OPEN_MAX
+#else /* HASGETDTABLESIZE */
+# ifdef _SC_OPEN_MAX
 	return sysconf(_SC_OPEN_MAX);
-#  else
+# else
 	return NOFILE;
-#  endif
-# endif /* HASGETDTABLESIZE */
+# endif
+#endif /* HASGETDTABLESIZE */
 }
 /*
 **  UNAME -- get the UUCP name of this system.
@@ -2947,6 +2952,7 @@ uname(name)
 */
 
 #if !HASINITGROUPS
+int
 initgroups(name, basegid)
 	char *name;
 	int basegid;
@@ -2980,7 +2986,7 @@ setgroups(ngroups, grouplist)
 pid_t
 setsid __P ((void))
 {
-#  ifdef TIOCNOTTY
+# ifdef TIOCNOTTY
 	int fd;
 
 	fd = open("/dev/tty", O_RDWR, 0);
@@ -2989,12 +2995,12 @@ setsid __P ((void))
 		(void) ioctl(fd, TIOCNOTTY, (char *) 0);
 		(void) close(fd);
 	}
-#  endif /* TIOCNOTTY */
-#  ifdef SYS5SETPGRP
+# endif /* TIOCNOTTY */
+# ifdef SYS5SETPGRP
 	return setpgrp();
-#  else
+# else
 	return setpgid(0, CurrentPid);
-#  endif
+# endif
 }
 
 #endif /* !HASSETSID */
@@ -3003,7 +3009,7 @@ setsid __P ((void))
 */
 
 #if NEEDFSYNC
-
+int
 fsync(fd)
 	int fd;
 {
@@ -3227,9 +3233,9 @@ usershellok(user, shell)
 	char *user;
 	char *shell;
 {
-# if HASGETUSERSHELL
+#if HASGETUSERSHELL
 	register char *p;
-	extern char *getusershell();
+	extern char *getusershell __P((void));
 
 	if (shell == NULL || shell[0] == '\0' || wordinclass(user, 't') ||
 	    ConfigLevel <= 1)
@@ -3241,10 +3247,10 @@ usershellok(user, shell)
 			break;
 	endusershell();
 	return p != NULL;
-# else /* HASGETUSERSHELL */
-#  if USEGETCONFATTR
+#else /* HASGETUSERSHELL */
+# if USEGETCONFATTR
 	auto char *v;
-#  endif
+# endif
 	register SM_FILE_T *shellf;
 	char buf[MAXLINE];
 
@@ -3252,7 +3258,7 @@ usershellok(user, shell)
 	    ConfigLevel <= 1)
 		return true;
 
-#  if USEGETCONFATTR
+# if USEGETCONFATTR
 	/*
 	**  Naturally IBM has a "better" idea.....
 	**
@@ -3276,7 +3282,7 @@ usershellok(user, shell)
 		}
 		return false;
 	}
-#  endif /* USEGETCONFATTR */
+# endif /* USEGETCONFATTR */
 
 	shellf = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, _PATH_SHELLS,
 			    SM_IO_RDONLY, NULL);
@@ -3319,7 +3325,7 @@ usershellok(user, shell)
 	}
 	(void) sm_io_close(shellf, SM_TIME_DEFAULT);
 	return false;
-# endif /* HASGETUSERSHELL */
+#endif /* HASGETUSERSHELL */
 }
 /*
 **  FREEDISKSPACE -- see how much free space is on the queue filesystem
@@ -3340,87 +3346,87 @@ usershellok(user, shell)
 */
 
 /* statfs types */
-# define SFS_NONE	0	/* no statfs implementation */
-# define SFS_USTAT	1	/* use ustat */
-# define SFS_4ARGS	2	/* use four-argument statfs call */
-# define SFS_VFS	3	/* use  implementation */
-# define SFS_MOUNT	4	/* use  implementation */
-# define SFS_STATFS	5	/* use  implementation */
-# define SFS_STATVFS	6	/* use  implementation */
+#define SFS_NONE	0	/* no statfs implementation */
+#define SFS_USTAT	1	/* use ustat */
+#define SFS_4ARGS	2	/* use four-argument statfs call */
+#define SFS_VFS	3	/* use  implementation */
+#define SFS_MOUNT	4	/* use  implementation */
+#define SFS_STATFS	5	/* use  implementation */
+#define SFS_STATVFS	6	/* use  implementation */
 
-# ifndef SFS_TYPE
-#  define SFS_TYPE	SFS_NONE
-# endif
+#ifndef SFS_TYPE
+# define SFS_TYPE	SFS_NONE
+#endif
 
-# if SFS_TYPE == SFS_USTAT
-#  include 
-# endif
-# if SFS_TYPE == SFS_4ARGS || SFS_TYPE == SFS_STATFS
-#  include 
-# endif
-# if SFS_TYPE == SFS_VFS
-#  include 
-# endif
-# if SFS_TYPE == SFS_MOUNT
-#  include 
-# endif
-# if SFS_TYPE == SFS_STATVFS
-#  include 
-# endif
+#if SFS_TYPE == SFS_USTAT
+# include 
+#endif
+#if SFS_TYPE == SFS_4ARGS || SFS_TYPE == SFS_STATFS
+# include 
+#endif
+#if SFS_TYPE == SFS_VFS
+# include 
+#endif
+#if SFS_TYPE == SFS_MOUNT
+# include 
+#endif
+#if SFS_TYPE == SFS_STATVFS
+# include 
+#endif
 
 long
 freediskspace(dir, bsize)
 	const char *dir;
 	long *bsize;
 {
-# if SFS_TYPE == SFS_NONE
+#if SFS_TYPE == SFS_NONE
 	if (bsize != NULL)
 		*bsize = 4096L;
 
 	/* assume free space is plentiful */
 	return (long) LONG_MAX;
-# else /* SFS_TYPE == SFS_NONE */
-#  if SFS_TYPE == SFS_USTAT
+#else /* SFS_TYPE == SFS_NONE */
+# if SFS_TYPE == SFS_USTAT
 	struct ustat fs;
 	struct stat statbuf;
-#   define FSBLOCKSIZE	DEV_BSIZE
-#   define SFS_BAVAIL	f_tfree
-#  else /* SFS_TYPE == SFS_USTAT */
-#   if defined(ultrix)
+#  define FSBLOCKSIZE	DEV_BSIZE
+#  define SFS_BAVAIL	f_tfree
+# else /* SFS_TYPE == SFS_USTAT */
+#  if defined(ultrix)
 	struct fs_data fs;
-#    define SFS_BAVAIL	fd_bfreen
-#    define FSBLOCKSIZE	1024L
-#   else /* defined(ultrix) */
-#    if SFS_TYPE == SFS_STATVFS
+#   define SFS_BAVAIL	fd_bfreen
+#   define FSBLOCKSIZE	1024L
+#  else /* defined(ultrix) */
+#   if SFS_TYPE == SFS_STATVFS
 	struct statvfs fs;
-#     define FSBLOCKSIZE	fs.f_frsize
-#    else /* SFS_TYPE == SFS_STATVFS */
+#    define FSBLOCKSIZE	fs.f_frsize
+#   else /* SFS_TYPE == SFS_STATVFS */
 	struct statfs fs;
-#     define FSBLOCKSIZE	fs.f_bsize
-#    endif /* SFS_TYPE == SFS_STATVFS */
-#   endif /* defined(ultrix) */
-#  endif /* SFS_TYPE == SFS_USTAT */
-#  ifndef SFS_BAVAIL
-#   define SFS_BAVAIL f_bavail
-#  endif
+#    define FSBLOCKSIZE	fs.f_bsize
+#   endif /* SFS_TYPE == SFS_STATVFS */
+#  endif /* defined(ultrix) */
+# endif /* SFS_TYPE == SFS_USTAT */
+# ifndef SFS_BAVAIL
+#  define SFS_BAVAIL f_bavail
+# endif
 
-#  if SFS_TYPE == SFS_USTAT
+# if SFS_TYPE == SFS_USTAT
 	if (stat(dir, &statbuf) == 0 && ustat(statbuf.st_dev, &fs) == 0)
-#  else /* SFS_TYPE == SFS_USTAT */
-#   if SFS_TYPE == SFS_4ARGS
+# else /* SFS_TYPE == SFS_USTAT */
+#  if SFS_TYPE == SFS_4ARGS
 	if (statfs(dir, &fs, sizeof(fs), 0) == 0)
-#   else /* SFS_TYPE == SFS_4ARGS */
-#    if SFS_TYPE == SFS_STATVFS
+#  else /* SFS_TYPE == SFS_4ARGS */
+#   if SFS_TYPE == SFS_STATVFS
 	if (statvfs(dir, &fs) == 0)
-#    else /* SFS_TYPE == SFS_STATVFS */
-#     if defined(ultrix)
+#   else /* SFS_TYPE == SFS_STATVFS */
+#    if defined(ultrix)
 	if (statfs(dir, &fs) > 0)
-#     else /* defined(ultrix) */
+#    else /* defined(ultrix) */
 	if (statfs(dir, &fs) == 0)
-#     endif /* defined(ultrix) */
-#    endif /* SFS_TYPE == SFS_STATVFS */
-#   endif /* SFS_TYPE == SFS_4ARGS */
-#  endif /* SFS_TYPE == SFS_USTAT */
+#    endif /* defined(ultrix) */
+#   endif /* SFS_TYPE == SFS_STATVFS */
+#  endif /* SFS_TYPE == SFS_4ARGS */
+# endif /* SFS_TYPE == SFS_USTAT */
 	{
 		if (bsize != NULL)
 			*bsize = FSBLOCKSIZE;
@@ -3432,7 +3438,7 @@ freediskspace(dir, bsize)
 			return (long) fs.SFS_BAVAIL;
 	}
 	return -1;
-# endif /* SFS_TYPE == SFS_NONE */
+#endif /* SFS_TYPE == SFS_NONE */
 }
 /*
 **  ENOUGHDISKSPACE -- is there enough free space on the queue file systems?
@@ -3610,14 +3616,14 @@ lockfile(fd, filename, ext, type)
 {
 	int i;
 	int save_errno;
-# if !HASFLOCK
+#if !HASFLOCK
 	int action;
 	struct flock lfd;
 
 	if (ext == NULL)
 		ext = "";
 
-	memset(&lfd, '\0', sizeof(lfd));
+	(void) memset(&lfd, '\0', sizeof(lfd));
 	if (bitset(LOCK_UN, type))
 		lfd.l_type = F_UNLCK;
 	else if (bitset(LOCK_EX, type))
@@ -3633,7 +3639,6 @@ lockfile(fd, filename, ext, type)
 	if (tTd(55, 60))
 		sm_dprintf("lockfile(%s%s, action=%d, type=%d): ",
 			filename, ext, action, lfd.l_type);
-
 	while ((i = fcntl(fd, action, &lfd)) < 0 && errno == EINTR)
 		continue;
 	if (i >= 0)
@@ -3674,7 +3679,7 @@ lockfile(fd, filename, ext, type)
 		       filename, ext, fd, type, omode, (long) euid);
 		dumpfd(fd, true, true);
 	}
-# else /* !HASFLOCK */
+#else /* !HASFLOCK */
 	if (ext == NULL)
 		ext = "";
 
@@ -3704,7 +3709,7 @@ lockfile(fd, filename, ext, type)
 			filename, ext, fd, type, omode, (long) euid);
 		dumpfd(fd, true, true);
 	}
-# endif /* !HASFLOCK */
+#endif /* !HASFLOCK */
 	if (tTd(55, 60))
 		sm_dprintf("FAIL\n");
 	errno = save_errno;
@@ -3772,7 +3777,7 @@ chownsafe(fd, safedir)
 	int fd;
 	bool safedir;
 {
-# if (!defined(_POSIX_CHOWN_RESTRICTED) || _POSIX_CHOWN_RESTRICTED != -1) && \
+#if (!defined(_POSIX_CHOWN_RESTRICTED) || _POSIX_CHOWN_RESTRICTED != -1) && \
     (defined(_PC_CHOWN_RESTRICTED) || defined(_GNU_TYPES_H))
 	int rval;
 
@@ -3788,14 +3793,14 @@ chownsafe(fd, safedir)
 
 	errno = 0;
 	rval = fpathconf(fd, _PC_CHOWN_RESTRICTED);
-#  if SAFENFSPATHCONF
+# if SAFENFSPATHCONF
 	return errno == 0 && rval IS_SAFE_CHOWN;
-#  else
+# else
 	return safedir && errno == 0 && rval IS_SAFE_CHOWN;
-#  endif
-# else /* (!defined(_POSIX_CHOWN_RESTRICTED) || _POSIX_CHOWN_RESTRICTED != -1) && ... */
+# endif
+#else /* (!defined(_POSIX_CHOWN_RESTRICTED) || _POSIX_CHOWN_RESTRICTED != -1) && ... */
 	return bitnset(DBS_ASSUMESAFECHOWN, DontBlameSendmail);
-# endif /* (!defined(_POSIX_CHOWN_RESTRICTED) || _POSIX_CHOWN_RESTRICTED != -1) && ... */
+#endif /* (!defined(_POSIX_CHOWN_RESTRICTED) || _POSIX_CHOWN_RESTRICTED != -1) && ... */
 }
 /*
 **  RESETLIMITS -- reset system controlled resource limits
@@ -3858,7 +3863,7 @@ bool
 setvendor(vendor)
 	char *vendor;
 {
-	if (sm_strcasecmp(vendor, "Berkeley") == 0)
+	if (SM_STRCASEEQ(vendor, "Berkeley"))
 	{
 		VendorCode = VENDOR_BERKELEY;
 		return true;
@@ -3867,14 +3872,14 @@ setvendor(vendor)
 	/* add vendor extensions here */
 
 #ifdef SUN_EXTENSIONS
-	if (sm_strcasecmp(vendor, "Sun") == 0)
+	if (SM_STRCASEEQ(vendor, "Sun"))
 	{
 		VendorCode = VENDOR_SUN;
 		return true;
 	}
 #endif /* SUN_EXTENSIONS */
 #ifdef DEC
-	if (sm_strcasecmp(vendor, "Digital") == 0)
+	if (SM_STRCASEEQ(vendor, "Digital"))
 	{
 		VendorCode = VENDOR_DEC;
 		return true;
@@ -3882,7 +3887,7 @@ setvendor(vendor)
 #endif /* DEC */
 
 #if defined(VENDOR_NAME) && defined(VENDOR_CODE)
-	if (sm_strcasecmp(vendor, VENDOR_NAME) == 0)
+	if (SM_STRCASEEQ(vendor, VENDOR_NAME))
 	{
 		VendorCode = VENDOR_CODE;
 		return true;
@@ -4404,8 +4409,11 @@ sm_gethostbyname(name, family)
 # endif /* NETINET6 */
 	char *maptype[MAXMAPSTACK];
 	short mapreturn[MAXMAPACTIONS];
-	char hbuf[MAXNAME];
-
+	char hbuf[MAXNAME_I];
+# if _FFR_8BITENVADDR
+	(void) dequote_internal_chars(name, hbuf, sizeof(hbuf));
+	name = hbuf;
+# endif
 	if (tTd(61, 10))
 		sm_dprintf("sm_gethostbyname(%s, %d)... ", name, family);
 
@@ -4423,7 +4431,8 @@ sm_gethostbyname(name, family)
 	if (h == NULL)
 	{
 		if (tTd(61, 10))
-			sm_dprintf("failure\n");
+			sm_dprintf("failure: errno=%d, h_errno=%d\n",
+				errno, h_errno);
 
 		nmaps = switch_map_find("hosts", maptype, mapreturn);
 		while (--nmaps >= 0)
@@ -4467,9 +4476,9 @@ sm_gethostbyname(name, family)
 	/* the function is supposed to return only the requested family */
 	if (h != NULL && h->h_addrtype != family)
 	{
-# if NETINET6
+#if NETINET6
 		freehostent(h);
-# endif
+#endif
 		h = NULL;
 		SM_SET_H_ERRNO(NO_DATA);
 	}
@@ -4477,7 +4486,8 @@ sm_gethostbyname(name, family)
 	if (tTd(61, 10))
 	{
 		if (h == NULL)
-			sm_dprintf("failure\n");
+			sm_dprintf("failure: errno=%d, h_errno=%d\n",
+				errno, h_errno);
 		else
 		{
 			sm_dprintf("%s\n", h->h_name);
@@ -4821,24 +4831,24 @@ struct mbuf;
 void
 load_if_names()
 {
-# if NETINET6 && defined(SIOCGLIFCONF)
-#  ifdef __hpux
+#if NETINET6 && defined(SIOCGLIFCONF)
+# ifdef __hpux
 
     /*
     **  Unfortunately, HP has changed all of the structures,
     **  making life difficult for implementors.
     */
 
-#   define lifconf	if_laddrconf
-#   define lifc_len	iflc_len
-#   define lifc_buf	iflc_buf
-#   define lifreq	if_laddrreq
-#   define lifr_addr	iflr_addr
-#   define lifr_name	iflr_name
-#   define lifr_flags	iflr_flags
-#   define ss_family	sa_family
-#   undef SIOCGLIFNUM
-#  endif /* __hpux */
+#  define lifconf	if_laddrconf
+#  define lifc_len	iflc_len
+#  define lifc_buf	iflc_buf
+#  define lifreq	if_laddrreq
+#  define lifr_addr	iflr_addr
+#  define lifr_name	iflr_name
+#  define lifr_flags	iflr_flags
+#  define ss_family	sa_family
+#  undef SIOCGLIFNUM
+# endif /* __hpux */
 
 	int s;
 	int i;
@@ -4846,26 +4856,26 @@ load_if_names()
 	int numifs;
 	char *buf;
 	struct lifconf lifc;
-#  ifdef SIOCGLIFNUM
+# ifdef SIOCGLIFNUM
 	struct lifnum lifn;
-#  endif
+# endif
 
 	s = socket(InetMode, SOCK_DGRAM, 0);
 	if (s == -1)
 		return;
 
 	/* get the list of known IP address from the kernel */
-#  ifdef __hpux
+# ifdef __hpux
 	i = ioctl(s, SIOCGIFNUM, (char *) &numifs);
-#  endif
-#  ifdef SIOCGLIFNUM
+# endif
+# ifdef SIOCGLIFNUM
 	lifn.lifn_family = AF_UNSPEC;
 	lifn.lifn_flags = 0;
 	i = ioctl(s, SIOCGLIFNUM, (char *)&lifn);
 	numifs = lifn.lifn_count;
-#  endif /* SIOCGLIFNUM */
+# endif /* SIOCGLIFNUM */
 
-#  if defined(__hpux) || defined(SIOCGLIFNUM)
+# if defined(__hpux) || defined(SIOCGLIFNUM)
 	if (i < 0)
 	{
 		/* can't get number of interfaces -- fall back */
@@ -4877,7 +4887,7 @@ load_if_names()
 	else if (tTd(0, 42))
 		sm_dprintf("system has %d interfaces\n", numifs);
 	if (numifs < 0)
-#  endif /* defined(__hpux) || defined(SIOCGLIFNUM) */
+# endif /* defined(__hpux) || defined(SIOCGLIFNUM) */
 		numifs = MAXINTERFACES;
 
 	if (numifs <= 0)
@@ -4888,10 +4898,10 @@ load_if_names()
 
 	len = lifc.lifc_len = numifs * sizeof(struct lifreq);
 	buf = lifc.lifc_buf = xalloc(lifc.lifc_len);
-#  ifndef __hpux
+# ifndef __hpux
 	lifc.lifc_family = AF_UNSPEC;
 	lifc.lifc_flags = 0;
-#  endif
+# endif
 	if (ioctl(s, SIOCGLIFCONF, (char *)&lifc) < 0)
 	{
 		if (tTd(0, 4))
@@ -4917,9 +4927,9 @@ load_if_names()
 		char *name;
 		struct in6_addr ia6;
 		struct in_addr ia;
-#  ifdef SIOCGLIFFLAGS
+# ifdef SIOCGLIFFLAGS
 		struct lifreq ifrf;
-#  endif
+# endif
 		char ip_addr[256];
 		char buf6[INET6_ADDRSTRLEN];
 
@@ -4946,18 +4956,21 @@ load_if_names()
 		if ((len - i) < sizeof(*ifr))
 			break;
 
-#  ifdef BSD4_4_SOCKADDR
+# ifdef BSD4_4_SOCKADDR
 		if (sa->sa.sa_len > sizeof(ifr->lifr_addr))
 			i += sizeof(ifr->lifr_name) + sa->sa.sa_len;
 		else
-#  endif /* BSD4_4_SOCKADDR */
-#  ifdef DEC
+# endif /* BSD4_4_SOCKADDR */
+		/* "else" in #if code above */
+		{
+# ifdef DEC
 			/* fix for IPv6  size differences */
 			i += sizeof(ifr->ifr_name) +
 			     max(sizeof(ifr->ifr_addr), ifr->ifr_addr.sa_len);
-#   else /* DEC */
+# else /* DEC */
 			i += sizeof(*ifr);
-#   endif /* DEC */
+# endif /* DEC */
+		}
 
 		if (tTd(0, 20))
 			sm_dprintf("%s\n", anynet_ntoa(sa));
@@ -4965,7 +4978,7 @@ load_if_names()
 		if (af != AF_INET && af != AF_INET6)
 			continue;
 
-#  ifdef SIOCGLIFFLAGS
+# ifdef SIOCGLIFFLAGS
 		memset(&ifrf, '\0', sizeof(struct lifreq));
 		(void) sm_strlcpy(ifrf.lifr_name, ifr->lifr_name,
 				  sizeof(ifrf.lifr_name));
@@ -4985,7 +4998,7 @@ load_if_names()
 
 		if (!bitset(IFF_UP, flags))
 			continue;
-#  endif /* SIOCGLIFFLAGS */
+# endif /* SIOCGLIFFLAGS */
 
 		ip_addr[0] = '\0';
 
@@ -4994,7 +5007,7 @@ load_if_names()
 		{
 		  case AF_INET6:
 			SETV6LOOPBACKADDRFOUND(*sa);
-#  ifdef __KAME__
+# ifdef __KAME__
 			/* convert into proper scoped address */
 			if ((IN6_IS_ADDR_LINKLOCAL(&sa->sin6.sin6_addr) ||
 			     IN6_IS_ADDR_SITELOCAL(&sa->sin6.sin6_addr)) &&
@@ -5007,7 +5020,7 @@ load_if_names()
 							       ((unsigned int)ia6p->s6_addr[2] << 8));
 				ia6p->s6_addr[2] = ia6p->s6_addr[3] = 0;
 			}
-#  endif /* __KAME__ */
+# endif /* __KAME__ */
 			ia6 = sa->sin6.sin6_addr;
 			if (IN6_IS_ADDR_UNSPECIFIED(&ia6))
 			{
@@ -5052,18 +5065,18 @@ load_if_names()
 				sm_dprintf("\ta.k.a.: %s\n", ip_addr);
 		}
 
-#  ifdef SIOCGLIFFLAGS
+# ifdef SIOCGLIFFLAGS
 		/* skip "loopback" interface "lo" */
 		if (DontProbeInterfaces == DPI_SKIPLOOPBACK &&
 		    bitset(IFF_LOOPBACK, flags))
 			continue;
-#  endif /* SIOCGLIFFLAGS */
+# endif /* SIOCGLIFFLAGS */
 		(void) add_hostnames(sa);
 	}
 	sm_free(buf); /* XXX */
 	(void) close(s);
-# else /* NETINET6 && defined(SIOCGLIFCONF) */
-#  if defined(SIOCGIFCONF) && !SIOCGIFCONF_IS_BROKEN
+#else /* NETINET6 && defined(SIOCGLIFCONF) */
+# if defined(SIOCGIFCONF) && !SIOCGIFCONF_IS_BROKEN
 	int s;
 	int i;
 	struct ifconf ifc;
@@ -5074,7 +5087,7 @@ load_if_names()
 		return;
 
 	/* get the list of known IP address from the kernel */
-#   if defined(SIOCGIFNUM) && !SIOCGIFNUM_IS_BROKEN
+#  if defined(SIOCGIFNUM) && !SIOCGIFNUM_IS_BROKEN
 	if (ioctl(s, SIOCGIFNUM, (char *) &numifs) < 0)
 	{
 		/* can't get number of interfaces -- fall back */
@@ -5086,7 +5099,7 @@ load_if_names()
 	else if (tTd(0, 42))
 		sm_dprintf("system has %d interfaces\n", numifs);
 	if (numifs < 0)
-#   endif /* defined(SIOCGIFNUM) && !SIOCGIFNUM_IS_BROKEN */
+#  endif /* defined(SIOCGIFNUM) && !SIOCGIFNUM_IS_BROKEN */
 		numifs = MAXINTERFACES;
 
 	if (numifs <= 0)
@@ -5113,20 +5126,24 @@ load_if_names()
 	for (i = 0; i < ifc.ifc_len && i >= 0; )
 	{
 		int af;
+#  if HAVE_IFC_BUF_VOID
+		struct ifreq *ifr = (struct ifreq *) &((char *)ifc.ifc_buf)[i];
+#  else
 		struct ifreq *ifr = (struct ifreq *) &ifc.ifc_buf[i];
+#  endif
 		SOCKADDR *sa = (SOCKADDR *) &ifr->ifr_addr;
-#   if NETINET6
+#  if NETINET6
 		char *addr;
 		struct in6_addr ia6;
-#   endif
+#  endif
 		struct in_addr ia;
-#   ifdef SIOCGIFFLAGS
+#  ifdef SIOCGIFFLAGS
 		struct ifreq ifrf;
-#   endif
+#  endif
 		char ip_addr[256];
-#   if NETINET6
+#  if NETINET6
 		char buf6[INET6_ADDRSTRLEN];
-#   endif
+#  endif
 
 		/*
 		**  If we don't have a complete ifr structure,
@@ -5136,25 +5153,28 @@ load_if_names()
 		if ((ifc.ifc_len - i) < sizeof(*ifr))
 			break;
 
-#   ifdef BSD4_4_SOCKADDR
+#  ifdef BSD4_4_SOCKADDR
 		if (sa->sa.sa_len > sizeof(ifr->ifr_addr))
 			i += sizeof(ifr->ifr_name) + sa->sa.sa_len;
 		else
-#   endif /* BSD4_4_SOCKADDR */
+#  endif /* BSD4_4_SOCKADDR */
+		/* "else" in #if code above */
+		{
 			i += sizeof(*ifr);
+		}
 
 		if (tTd(0, 20))
 			sm_dprintf("%s\n", anynet_ntoa(sa));
 
 		af = ifr->ifr_addr.sa_family;
 		if (af != AF_INET
-#   if NETINET6
+#  if NETINET6
 		    && af != AF_INET6
-#   endif
+#  endif
 		    )
 			continue;
 
-#   ifdef SIOCGIFFLAGS
+#  ifdef SIOCGIFFLAGS
 		memset(&ifrf, '\0', sizeof(struct ifreq));
 		(void) sm_strlcpy(ifrf.ifr_name, ifr->ifr_name,
 			       sizeof(ifrf.ifr_name));
@@ -5162,10 +5182,10 @@ load_if_names()
 		if (tTd(0, 41))
 			sm_dprintf("\tflags: %lx\n",
 				(unsigned long) ifrf.ifr_flags);
-#    define IFRFREF ifrf
-#   else /* SIOCGIFFLAGS */
-#    define IFRFREF (*ifr)
-#   endif /* SIOCGIFFLAGS */
+#   define IFRFREF ifrf
+#  else /* SIOCGIFFLAGS */
+#   define IFRFREF (*ifr)
+#  endif /* SIOCGIFFLAGS */
 
 		if (!bitset(IFF_UP, IFRFREF.ifr_flags))
 			continue;
@@ -5191,10 +5211,10 @@ load_if_names()
 					inet_ntoa(ia));
 			break;
 
-#   if NETINET6
+#  if NETINET6
 		  case AF_INET6:
 			SETV6LOOPBACKADDRFOUND(*sa);
-#    ifdef __KAME__
+#   ifdef __KAME__
 			/* convert into proper scoped address */
 			if ((IN6_IS_ADDR_LINKLOCAL(&sa->sin6.sin6_addr) ||
 			     IN6_IS_ADDR_SITELOCAL(&sa->sin6.sin6_addr)) &&
@@ -5207,7 +5227,7 @@ load_if_names()
 							       ((unsigned int)ia6p->s6_addr[2] << 8));
 				ia6p->s6_addr[2] = ia6p->s6_addr[3] = 0;
 			}
-#    endif /* __KAME__ */
+#   endif /* __KAME__ */
 			ia6 = sa->sin6.sin6_addr;
 			if (IN6_IS_ADDR_UNSPECIFIED(&ia6))
 			{
@@ -5227,7 +5247,7 @@ load_if_names()
 						   addr);
 			break;
 
-#   endif /* NETINET6 */
+#  endif /* NETINET6 */
 		}
 
 		if (ip_addr[0] == '\0')
@@ -5249,9 +5269,9 @@ load_if_names()
 	}
 	sm_free(ifc.ifc_buf); /* XXX */
 	(void) close(s);
-#   undef IFRFREF
-#  endif /* defined(SIOCGIFCONF) && !SIOCGIFCONF_IS_BROKEN */
-# endif /* NETINET6 && defined(SIOCGLIFCONF) */
+#  undef IFRFREF
+# endif /* defined(SIOCGIFCONF) && !SIOCGIFCONF_IS_BROKEN */
+#endif /* NETINET6 && defined(SIOCGLIFCONF) */
 }
 /*
 **  ISLOOPBACK -- is socket address in the loopback net?
@@ -5503,7 +5523,12 @@ sm_syslog(level, id, fmt, va_alist)
 	}
 
 	/* clean up buf after it has been expanded with args */
+#if _FFR_LOGASIS >= 5
+/* for testing! */
+	newstring = buf;
+#else
 	newstring = str2prt(buf);
+#endif
 	if ((strlen(newstring) + idlen + 1) < SYSLOG_BUFSIZE)
 	{
 #if LOG
@@ -5695,8 +5720,7 @@ local_hostname_length(hostname)
 	len_host = strlen(hostname);
 	len_domain = strlen(_res.defdname);
 	if (len_host > len_domain &&
-	    (sm_strcasecmp(hostname + len_host - len_domain,
-			_res.defdname) == 0) &&
+	    (SM_STRCASEEQ(hostname + len_host - len_domain, _res.defdname)) &&
 	    hostname[len_host - len_domain - 1] == '.')
 		return len_host - len_domain - 1;
 	else
@@ -5820,14 +5844,11 @@ link(source, target)
 char	*CompileOptions[] =
 {
 #if ALLOW_255
+	/* if not enabled (and EightBitAddrOK not set): convert 0xff to 0x7f */
 	"ALLOW_255",
 #endif
 #if DANE
-# if STARTTLS
 	"DANE",
-# else
-#  error "DANE set but STARTTLS not defined"
-# endif
 #endif
 #if NAMED_BIND
 # if DNSMAP
@@ -5854,15 +5875,19 @@ char	*CompileOptions[] =
 	"LDAPMAP",
 #endif
 #if LDAP_NETWORK_TIMEOUT
-# if LDAPMAP && defined(LDAP_OPT_NETWORK_TIMEOUT)
+# if LDAPMAP
 	/* set LDAP_OPT_NETWORK_TIMEOUT if available (-c) */
 	"LDAP_NETWORK_TIMEOUT",
 # else
-#  ERROR: _LDAP_NETWORK_TIMEOUT requires _LDAPMAP
+#  ERROR "LDAP_NETWORK_TIMEOUT requires LDAPMAP"
 # endif
 #endif
 #if LDAP_REFERRALS
+# if LDAPMAP
 	"LDAP_REFERRALS",
+# else
+#  ERROR "LDAP_REFERRALS requires LDAPMAP"
+# endif
 #endif
 #if LOG
 	"LOG",
@@ -5926,6 +5951,9 @@ char	*CompileOptions[] =
 #endif
 #if NEWDB
 # if defined(DB_VERSION_MAJOR) && defined(DB_VERSION_MINOR)
+#  if DB_VERSION_MAJOR >= 5 && !HASFLOCK
+#    ERROR "Berkeley DB file locking needs flock() for version 5.x (and greater?)"
+#  endif
 	"NEWDB=" SM_XSTR(DB_VERSION_MAJOR) "." SM_XSTR(DB_VERSION_MINOR),
 # else
 	"NEWDB",
@@ -5996,6 +6024,22 @@ char	*CompileOptions[] =
 #if USERDB
 	"USERDB",
 #endif
+#if USE_EAI
+
+	/*
+	**  Initial/Partial/Experimental EAI (SMTPUTF8) support.
+	**  Requires ICU include files and library depending on the OS.
+	**  Initial patch from Arnt Gulbrandsen.
+	*/
+
+# if !ALLOW_255
+#  ERROR "USE_EAI requires ALLOW_255"
+# endif
+# if _FFR_EIGHT_BIT_ADDR_OK
+#  ERROR "Cannot enable both USE_EAI and _FFR_EIGHT_BIT_ADDR_OK"
+# endif
+	"USE_EAI",
+#endif
 #if USE_LDAP_INIT
 	"USE_LDAP_INIT",
 #endif
@@ -6258,7 +6302,7 @@ char	*OsCompileOptions[] =
 	"USE_OPENSSL_ENGINE=" SM_XSTR(USE_OPENSSL_ENGINE),
 # else
 	"USE_OPENSSL_ENGINE",
-#endif
+# endif
 #endif
 #if USING_NETSCAPE_LDAP
 	"USING_NETSCAPE_LDAP",
@@ -6339,15 +6383,23 @@ char	*FFRCompileOptions[] =
 	/* Don't try to send mail if its size exceeds SIZE= of server. */
 	"_FFR_CLIENT_SIZE",
 #endif
-#if _FFR_DM_ONE
-	/* deliver first TA in background, then queue */
-	"_FFR_DM_ONE",
-#endif
 #if _FFR_DIGUNIX_SAFECHOWN
 	/* Properly set SAFECHOWN (include/sm/conf.h) for Digital UNIX */
 /* Problem noted by Anne Bennett of Concordia University */
 	"_FFR_DIGUNIX_SAFECHOWN",
 #endif
+#if _FFR_DM_ONE
+	/* deliver first TA in background, then queue */
+	"_FFR_DM_ONE",
+#endif
+#if _FFR_DMTRIGGER
+	/*
+	**  WIP: DeliveryMode=Trigger: queue message and notify
+	**  some kind of queue manager about it.
+	*/
+
+	"_FFR_DMTRIGGER",
+#endif
 #if _FFR_DNSMAP_ALIASABLE
 	/* Allow dns map type to be used for aliases. */
 /* Don Lewis of TDK */
@@ -6479,11 +6531,6 @@ char	*FFRCompileOptions[] =
 	/* log some TLS info in to= too */
 	"_FFR_LOG_MORE2=" SM_XSTR(_FFR_LOG_MORE2),
 #endif
-#if _FFR_LOG_MORE1 > 1 || _FFR_LOG_MORE2 > 1
-# if _FFR_LOG_MORE1 != _FFR_LOG_MORE2
-   ERROR: FFR_LOG_MORE1 != FFR_LOG_MORE2
-# endif
-#endif
 #if _FFR_MAIL_MACRO
 	/* make the "real" sender address available in {mail_from} */
 	"_FFR_MAIL_MACRO",
@@ -6522,7 +6569,7 @@ char	*FFRCompileOptions[] =
 #if _FFR_MILTER_CONNECT_REPLYCODE
 	/* milter: propagate replycode returned by connect commands */
 	/* John Gardiner Myers of Proofpoint */
-	"_FFR_MILTER_CONNECT_REPLYCODE ",
+	"_FFR_MILTER_CONNECT_REPLYCODE",
 #endif
 #if _FFR_MILTER_CONVERT_ALL_LF_TO_CRLF
 	/*
@@ -6566,6 +6613,23 @@ char	*FFRCompileOptions[] =
 	/* allow to override "Message accepted for delivery" */
 	"_FFR_MSG_ACCEPT",
 #endif
+#if _FFR_MTA_MODE
+	/* do not modify headers -- does NOT (yet) work */
+	"_FFR_MTA_MODE",
+#endif
+#if _FFR_MTA_STS
+# if !MAP_REGEX
+#  ERROR "_FFR_MTA_STS requires MAP_REGEX"
+# endif
+# if !STARTTLS
+#  ERROR "_FFR_MTA_STS requires STARTTLS"
+# endif
+# if !_FFR_TLS_ALTNAMES
+#  ERROR "_FFR_MTA_STS requires _FFR_TLS_ALTNAMES"
+# endif
+	/* MTA STS support */
+	"_FFR_MTA_STS",
+#endif /* _FFR_MTA_STS */
 #if _FFR_NODELAYDSN_ON_HOLD
 	/* Do not issue a DELAY DSN for mailers that use the hold flag. */
 /* Steven Pitzl */
@@ -6589,7 +6653,7 @@ char	*FFRCompileOptions[] =
 	**  better option) which returns the single dn/multiple
 	**  attributes.
 	**
-	** Jeffrey T. Eaton, Carnegie-Mellon University
+	**  Jeffrey T. Eaton, Carnegie-Mellon University
 	*/
 
 	"_FFR_LDAP_SINGLEDN",
@@ -6603,7 +6667,7 @@ char	*FFRCompileOptions[] =
 	/* outgoing connection control (not yet working) */
 	"_FFR_OCC",
 # else
-#  ERROR: FFR_OCC requires _SM_CONF_SHM
+#  ERROR "_FFR_OCC requires SM_CONF_SHM"
 # endif
 #endif
 #if _FFR_PROXY
@@ -6652,6 +6716,10 @@ char	*FFRCompileOptions[] =
 	/* reject NUL bytes in body */
 	"_FFR_REJECT_NUL_BYTE",
 #endif
+#if _FFR_REPLY_MULTILINE
+	/* try to gather multi-line replies for reply= logging */
+	"_FFR_REPLY_MULTILINE=" SM_XSTR(_FFR_REPLY_MULTILINE),
+#endif
 #if _FFR_RESET_MACRO_GLOBALS
 	/* Allow macro 'j' to be set dynamically via rulesets. */
 	"_FFR_RESET_MACRO_GLOBALS",
@@ -6691,14 +6759,14 @@ char	*FFRCompileOptions[] =
 #endif
 #if _FFR_SLEEP_USE_SELECT
 	/* Use select(2) in libsm/clock.c to emulate sleep(2) */
-	"_FFR_SLEEP_USE_SELECT ",
+	"_FFR_SLEEP_USE_SELECT",
 #endif
 #if _FFR_SM_LDAP_DBG
 # if LDAPMAP && defined(LBER_OPT_LOG_PRINT_FN)
 	/* LDAP debugging */
 	"_FFR_SM_LDAP_DBG",
 # else
-#  ERROR: FFR_SM_LDAP_DBG requires _LDAPMAP and LBER_OPT_LOG_PRINT_FN
+#  ERROR "_FFR_SM_LDAP_DBG requires LDAPMAP and LBER_OPT_LOG_PRINT_FN"
 # endif
 #endif
 #if _FFR_SPT_ALIGN
@@ -6727,19 +6795,11 @@ char	*FFRCompileOptions[] =
 #endif
 #if _FFR_TLS_ALTNAMES
 	/* store subjectAltNames in class {cert_altnames} */
-# if STARTTLS
 	"_FFR_TLS_ALTNAMES",
-# else
-#  error "_FFR_TLS_ALTNAMES set but STARTTLS not defined"
-# endif
 #endif
 #if _FFR_TLSFB2CLEAR
 	/* set default for TLSFallbacktoClear to true */
-# if STARTTLS
 	"_FFR_TLSFB2CLEAR",
-# else
-#  error "_FFR_TLSFB2CLEAR set but STARTTLS not defined"
-# endif
 #endif
 #if _FFR_TLS_USE_CERTIFICATE_CHAIN_FILE
 	/*
@@ -6747,11 +6807,7 @@ char	*FFRCompileOptions[] =
 	**  instead of SSL_CTX_use_certificate_file()
 	*/
 
-# if STARTTLS
 	"_FFR_TLS_USE_CERTIFICATE_CHAIN_FILE",
-# else
-#  error "_FFR_TLS_USE_CERTIFICATE_CHAIN_FILE set but STARTTLS not defined"
-# endif
 #endif
 #if _FFR_TRUSTED_QF
 	/*
@@ -6790,7 +6846,7 @@ char	*FFRCompileOptions[] =
 # if defined(X509_V_FLAG_TRUSTED_FIRST)
 	"_FFR_VRFY_TRUSTED_FIRST",
 # else
-#  error "FFR_VRFY_TRUSTED_FIRST set but X509_V_FLAG_TRUSTED_FIRST not defined"
+#  ERROR "_FFR_VRFY_TRUSTED_FIRST set but X509_V_FLAG_TRUSTED_FIRST not defined"
 # endif
 #endif
 
@@ -6807,24 +6863,44 @@ char	*FFRCompileOptions[] =
 	/* X-Connect support */
 	"_FFR_XCNCT",
 #endif
-#if _FFR_EAI
-
+#if _FFR_LOGASIS
+	"_FFR_LOGASIS=" SM_XSTR(_FFR_LOGASIS),
+#endif
+#if _FFR_NOREFLECT
+	/* Don't forget to update docs for "goaway" to include this */
+	"_FFR_NOREFLECT",
+#endif
+#if _FFR_AUTH_PASSING
+	/* Set the default AUTH= if the sender didn't */
+	"_FFR_AUTH_PASSING",
+#endif
+#if _FFR_HOST_SORT_REVERSE
+	/* Reverse sort for host for recipient sorting pre-envelope-split */
+	"_FFR_HOST_SORT_REVERSE",
+#endif
+#if _FFR_MSP_PARANOIA
 	/*
-	**  Initial/Partial/Experimental EAI (SMTPUTF8) support.
-	**  NOTE: This is currently BROKEN as the handling of
-	**  envelope addresses in sendmail is NOT 8-bit clean
-	**  (in contrast to header addresses/values).
-	**  Requires ICU include files and library depending on the OS.
-	**  Patch from Arnt Gulbrandsen.
+	**  Forbid queue groups, multiple queues, and dangerous queue permissions
+	**  when operating as an MSP
 	*/
 
-# if !ALLOW_255
-#  ERROR FFR_EAI requires _ALLOW_255
-# endif
-# if _FFR_EIGHT_BIT_ADDR_OK
-#  error "Cannot enable both of these FFRs: FFR_EAI FFR_EIGHT_BIT_ADDR_OK"
-# endif
-	"_FFR_EAI",
+	"_FFR_MSP_PARANOIA",
+#endif
+#if _FFR_ANY_FREE_FS
+	/*
+	**  Check whether there is at least one fs with enough space
+	**  (may not work, needs review)
+	*/
+
+	"_FFR_ANY_FREE_FS",
+#endif
+#if _FFR_MIME_CR_OK
+	/*
+	**  Strip trailing CR in MIME boundaries
+	**  (may not work, needs review)
+	*/
+
+	"_FFR_MIME_CR_OK",
 #endif
 	NULL
 };
diff --git a/contrib/sendmail/src/conf.h b/contrib/sendmail/src/conf.h
index b5b368f48d68..122b7614516f 100644
--- a/contrib/sendmail/src/conf.h
+++ b/contrib/sendmail/src/conf.h
@@ -109,7 +109,7 @@ struct rusage;	/* forward declaration to get gcc to shut up in wait.h */
 #define MAXSHORTSTR	203		/* max short string length */
 #define MAXMACNAMELEN	25		/* max macro name length */
 #define MAXMACROID	0377		/* max macro id number */
-					/* Must match (BITMAPBITS - 1) */
+	/* Must match (BITMAPBITS - 1), checked in macro.c */
 #ifndef MAXHDRSLEN
 # define MAXHDRSLEN	(32 * 1024)	/* max size of message headers */
 #endif
@@ -132,7 +132,7 @@ struct rusage;	/* forward declaration to get gcc to shut up in wait.h */
 	/* must be less than BITMAPBITS for DoQueueRun */
 #endif
 #if MAXQUEUEGROUPS >= BITMAPBITS
-  ERROR _MAXQUEUEGROUPS must be less than _BITMAPBITS
+# ERROR "MAXQUEUEGROUPS must be less than BITMAPBITS"
 #endif
 
 #ifndef MAXWORKGROUPS
diff --git a/contrib/sendmail/src/control.c b/contrib/sendmail/src/control.c
index a6d8ad631335..1af2cf7cfd95 100644
--- a/contrib/sendmail/src/control.c
+++ b/contrib/sendmail/src/control.c
@@ -12,6 +12,7 @@
 
 SM_RCSID("@(#)$Id: control.c,v 8.130 2013-11-22 20:51:55 ca Exp $")
 
+#include 
 #include 
 
 /* values for cmd_code */
@@ -61,13 +62,13 @@ int ControlSocket = -1;
 int
 opencontrolsocket()
 {
-# if NETUNIX
+#if NETUNIX
 	int save_errno;
 	int rval;
 	long sff = SFF_SAFEDIRPATH|SFF_OPENASROOT|SFF_NOLINK|SFF_CREAT|SFF_MUSTOWN;
 	struct sockaddr_un controladdr;
 
-	if (ControlSocketName == NULL || *ControlSocketName == '\0')
+	if (SM_IS_EMPTY(ControlSocketName))
 		return 0;
 
 	if (strlen(ControlSocketName) >= sizeof(controladdr.sun_path))
@@ -152,7 +153,7 @@ opencontrolsocket()
 		errno = save_errno;
 		return -1;
 	}
-# endif /* NETUNIX */
+#endif /* NETUNIX */
 	return 0;
 }
 /*
@@ -172,7 +173,7 @@ void
 closecontrolsocket(fullclose)
 	bool fullclose;
 {
-# if NETUNIX
+#if NETUNIX
 	long sff = SFF_SAFEDIRPATH|SFF_OPENASROOT|SFF_NOLINK|SFF_CREAT|SFF_MUSTOWN;
 
 	if (ControlSocket >= 0)
@@ -200,7 +201,7 @@ closecontrolsocket(fullclose)
 			return;
 		}
 	}
-# endif /* NETUNIX */
+#endif /* NETUNIX */
 	return;
 }
 /*
@@ -219,11 +220,11 @@ closecontrolsocket(fullclose)
 void
 clrcontrol()
 {
-# if NETUNIX
+#if NETUNIX
 	if (ControlSocket >= 0)
 		(void) close(ControlSocket);
 	ControlSocket = -1;
-# endif /* NETUNIX */
+#endif /* NETUNIX */
 }
 /*
 **  CONTROL_COMMAND -- read and process command from named socket
@@ -329,7 +330,7 @@ control_command(sock, e)
 	/* decode command */
 	for (c = CmdTab; c->cmd_name != NULL; c++)
 	{
-		if (sm_strcasecmp(c->cmd_name, cmdbuf) == 0)
+		if (SM_STRCASEEQ(c->cmd_name, cmdbuf))
 			break;
 	}
 
@@ -397,7 +398,7 @@ control_command(sock, e)
 		break;
 
 	  case CMDMEMDUMP:	/* daemon memory dump, to find memory leaks */
-# if SM_HEAP_CHECK
+#if SM_HEAP_CHECK
 		/* dump the heap, if we are checking for memory leaks */
 		if (sm_debug_active(&SmHeapCheck, 2))
 		{
@@ -410,12 +411,12 @@ control_command(sock, e)
 			(void) sm_io_fprintf(s, SM_TIME_DEFAULT,
 					     "To fix, run sendmail with -dsm_check_heap.4\r\n");
 		}
-# else /* SM_HEAP_CHECK */
+#else /* SM_HEAP_CHECK */
 		(void) sm_io_fprintf(s, SM_TIME_DEFAULT,
 				     "Memory dump unavailable.\r\n");
 		(void) sm_io_fprintf(s, SM_TIME_DEFAULT,
 				     "To fix, rebuild with -DSM_HEAP_CHECK\r\n");
-# endif /* SM_HEAP_CHECK */
+#endif /* SM_HEAP_CHECK */
 		break;
 
 	  case CMDERROR:	/* unknown command */
diff --git a/contrib/sendmail/src/convtime.c b/contrib/sendmail/src/convtime.c
index 4d51d97ad6da..703602552e89 100644
--- a/contrib/sendmail/src/convtime.c
+++ b/contrib/sendmail/src/convtime.c
@@ -14,6 +14,7 @@
 #include 
 
 SM_RCSID("@(#)$Id: convtime.c,v 8.40 2013-11-22 20:51:55 ca Exp $")
+#include 
 
 /*
 **  CONVTIME -- convert time
@@ -48,7 +49,7 @@ convtime(p, units)
 	bool pos = true;
 
 	r = 0;
-	if (sm_strcasecmp(p, "now") == 0)
+	if (SM_STRCASEEQ(p, "now"))
 		return NOW;
 	if (*p == '-')
 	{
diff --git a/contrib/sendmail/src/daemon.c b/contrib/sendmail/src/daemon.c
index 19a937815cbd..5b42e323b1cc 100644
--- a/contrib/sendmail/src/daemon.c
+++ b/contrib/sendmail/src/daemon.c
@@ -15,6 +15,7 @@
 #include "map.h"
 
 SM_RCSID("@(#)$Id: daemon.c,v 8.698 2013-11-22 20:51:55 ca Exp $")
+#include 
 
 #if defined(SOCK_STREAM) || defined(__GNU_LIBRARY__)
 # define USE_SOCK_STREAM	1
@@ -39,6 +40,10 @@ SM_RCSID("@(#)$Id: daemon.c,v 8.698 2013-11-22 20:51:55 ca Exp $")
 # endif
 #endif
 
+#if _FFR_DMTRIGGER
+# include 
+#endif
+
 #if NETINET6
 # define FREEHOSTENT(h, s)			\
 	do					\
@@ -695,6 +700,15 @@ getrequests(e)
 			/* turn on profiling */
 			/* SM_PROF(0); */
 
+#if _FFR_DMTRIGGER
+			if (SM_TRIGGER == e->e_sendmode)
+			{
+				i = sm_notify_start(false, 0);
+				if (i != 0)
+					syserr("sm_notify_start(false) failed=%d", i);
+			}
+#endif
+
 			/*
 			**  Initialize exception stack and default exception
 			**  handler for child process.
@@ -957,7 +971,7 @@ getrequests(e)
 		}
 	}
 	if (tTd(15, 2))
-		sm_dprintf("getreq: returning\n");
+		sm_dprintf("getrequests: returning\n");
 
 #if MILTER
 	/* set the filters for this daemon */
@@ -1541,28 +1555,28 @@ setsockaddroptions(p, d)
 			if (isascii(*v) && isdigit(*v))
 				d->d_addr.sa.sa_family = atoi(v);
 #ifdef NETUNIX
-			else if (sm_strcasecmp(v, "unix") == 0 ||
-				 sm_strcasecmp(v, "local") == 0)
+			else if (SM_STRCASEEQ(v, "unix") ||
+				 SM_STRCASEEQ(v, "local"))
 				d->d_addr.sa.sa_family = AF_UNIX;
 #endif
 #if NETINET
-			else if (sm_strcasecmp(v, "inet") == 0)
+			else if (SM_STRCASEEQ(v, "inet"))
 				d->d_addr.sa.sa_family = AF_INET;
 #endif
 #if NETINET6
-			else if (sm_strcasecmp(v, "inet6") == 0)
+			else if (SM_STRCASEEQ(v, "inet6"))
 				d->d_addr.sa.sa_family = AF_INET6;
 #endif
 #if NETISO
-			else if (sm_strcasecmp(v, "iso") == 0)
+			else if (SM_STRCASEEQ(v, "iso"))
 				d->d_addr.sa.sa_family = AF_ISO;
 #endif
 #if NETNS
-			else if (sm_strcasecmp(v, "ns") == 0)
+			else if (SM_STRCASEEQ(v, "ns"))
 				d->d_addr.sa.sa_family = AF_NS;
 #endif
 #if NETX25
-			else if (sm_strcasecmp(v, "x.25") == 0)
+			else if (SM_STRCASEEQ(v, "x.25"))
 				d->d_addr.sa.sa_family = AF_CCITT;
 #endif
 			else
@@ -2388,7 +2402,7 @@ makeconnection(host, port, mci, e, enough
 				/*
 				**  Check for errors!
 				**  If no ad: turn off TLSA.
-				**  permail: use "normal" method?
+				**  permfail: use "normal" method?
 				**  tempfail: delay or use "normal" method?
 				*/
 
@@ -2402,9 +2416,9 @@ makeconnection(host, port, mci, e, enough
 						return EX_TEMPFAIL;
 					}
 					hp = dns2he(rr, family);
-#if NETINET6
+# if NETINET6
 					hs = hp;
-#endif
+# endif
 				}
 
 				/* other possible "tempfails"? */
@@ -2453,6 +2467,7 @@ makeconnection(host, port, mci, e, enough
 			}
 			else
 # endif /* NETINET6 */
+			/* "else" in #if code above */
 			{
 				if (errno == ETIMEDOUT ||
 # if _FFR_GETHBN_ExFILE
@@ -2536,7 +2551,7 @@ makeconnection(host, port, mci, e, enough
 		**  8754: see common.sh XREF SNKPORT2
 		*/
 
-		if (tTd(77, 101) && hp->h_addrtype == AF_INET &&
+		if (tTd(77, 101) && hp != NULL && hp->h_addrtype == AF_INET &&
 		    addr.sin.sin_addr.s_addr == inet_addr("10.1.1.12"))
 		{
 			addr.sin.sin_addr.s_addr = inet_addr("127.0.0.1");
@@ -2671,6 +2686,7 @@ makeconnection(host, port, mci, e, enough
 		}
 		else
 #endif /* HASRRESVPORT */
+		/* "else" in #if code above */
 		{
 			s = socket(addr.sa.sa_family, SOCK_STREAM, 0);
 		}
@@ -2812,6 +2828,25 @@ makeconnection(host, port, mci, e, enough
 				sm_dprintf("Connecting to [%s].%d...\n",
 					anynet_ntoa(&addr), ntohs(port));
 
+#if _FFR_TESTS
+			if (tTd(77, 101)
+			    /* && AF_INET == addr.sin.sin_family */
+			    && addr.sin.sin_addr.s_addr >=
+				inet_addr("255.255.255.1")
+			    && addr.sin.sin_addr.s_addr <=
+				inet_addr("255.255.255.255")
+			   )
+			{
+				i = -1;
+				save_errno = ntohl(addr.sin.sin_addr.s_addr) -
+					ntohl(inet_addr("255.255.255.0"));
+				sm_dprintf("hack: fail connection=%d\n",
+					save_errno);
+				errno = save_errno;
+			}
+			else
+				/* Watch out of changes below! */
+#endif /* _FFR_TESTS */
 			i = connect(s, (struct sockaddr *) &addr, addrlen);
 			save_errno = errno;
 			if (ev != NULL)
@@ -2928,8 +2963,7 @@ makeconnection(host, port, mci, e, enough
 		save_errno = errno;
 		syserr("cannot open SMTP client channel, fd=%d", s);
 		mci_setstat(mci, EX_TEMPFAIL, "4.4.5", NULL);
-		if (mci->mci_out != NULL)
-			(void) sm_io_close(mci->mci_out, SM_TIME_DEFAULT);
+		SM_CLOSE_FP(mci->mci_out);
 		(void) close(s);
 		errno = save_errno;
 		OCC_CLOSE;
@@ -2940,13 +2974,26 @@ makeconnection(host, port, mci, e, enough
 	/* set {client_flags} */
 	if (ClientSettings[addr.sa.sa_family].d_mflags != NULL)
 	{
-		macdefine(&mci->mci_macro, A_PERM,
-			  macid("{client_flags}"),
-			  ClientSettings[addr.sa.sa_family].d_mflags);
+		char flags[64];	/* XXX */
+
+		/*
+		**  For now just concatenate the flags as there is no
+		**  overlap yet.
+		*/
+
+		p = macvalue(macid("{client_flags}"), e);
+		flags[0] = '\0';
+		if (!SM_IS_EMPTY(p))
+		{
+			(void) sm_strlcpy(flags, p, sizeof(flags));
+			(void) sm_strlcat(flags, " ", sizeof(flags));
+		}
+		(void) sm_strlcat(flags,
+			ClientSettings[addr.sa.sa_family].d_mflags,
+			sizeof(flags));
+		macdefine(&mci->mci_macro, A_PERM, macid("{client_flags}"),
+			  flags);
 	}
-	else
-		macdefine(&mci->mci_macro, A_PERM,
-			  macid("{client_flags}"), "");
 
 	/* "add" {client_flags} to bitmap */
 	if (bitnset(D_IFNHELO, ClientSettings[addr.sa.sa_family].d_flags))
@@ -3123,8 +3170,7 @@ makeconnection_ds(mux_path, mci)
 		save_errno = errno;
 		syserr("cannot open SMTP client channel, fd=%d", sock);
 		mci_setstat(mci, EX_TEMPFAIL, "4.4.5", NULL);
-		if (mci->mci_out != NULL)
-			(void) sm_io_close(mci->mci_out, SM_TIME_DEFAULT);
+		SM_CLOSE_FP(mci->mci_out);
 		(void) close(sock);
 		errno = save_errno;
 		return EX_TEMPFAIL;
@@ -3539,8 +3585,8 @@ getauthinfo(fd, may_be_forged)
 	struct hostent *hp;
 	char *ostype = NULL;
 	char **ha;
-	char ibuf[MAXNAME + 1];
-	static char hbuf[MAXNAME + MAXAUTHINFO + 11];
+	char ibuf[MAXNAME + 1];	/* EAI:ok? it's a hostname from OS */
+	static char hbuf[MAXNAME + MAXAUTHINFO + 11]; /* EAI:ok? (as above)*/
 
 	*may_be_forged = true;
 	falen = sizeof(RealHostAddr);
@@ -4058,7 +4104,7 @@ host_map_lookup(map, name, av, statp)
 	time_t SM_NONVOLATILE retrans = 0;
 	int SM_NONVOLATILE retry = 0;
 #endif
-	char hbuf[MAXNAME + 1];
+	char hbuf[MAXNAME + 1]; /* is (host)name in 'x' format? */
 
 	/*
 	**  See if we have already looked up this name.  If so, just
@@ -4151,12 +4197,37 @@ host_map_lookup(map, name, av, statp)
 	if (*name != '[')
 	{
 		int ttl, r;
+#if USE_EAI
+		bool utf8;
 
-		(void) sm_strlcpy(hbuf, name, sizeof(hbuf));
+		utf8 = !addr_is_ascii(name);
+		if (utf8)
+		{
+			(void) sm_strlcpy(hbuf, hn2alabel(name), sizeof(hbuf));
+
+			/* if this is not a FQHN then do not restore it */
+			utf8 = strchr(hbuf, '.') != NULL;
+		}
+		else
+#endif /* USE_EAI */
+		/* "else" in #if code above */
+		{
+			(void) sm_strlcpy(hbuf, name, sizeof(hbuf));
+		}
 
 		r = getcanonname(hbuf, sizeof(hbuf) - 1, !HasWildcardMX, &ttl);
 		if (r != HOST_NOTFOUND)
 		{
+#if USE_EAI
+			/*
+			**  Restore original. XXX Check if modified?
+			**  If so, convert it via hn2ulabel()
+			**  (not available yet)?
+			*/
+
+			if (utf8)
+				(void) sm_strlcpy(hbuf, name, sizeof(hbuf));
+#endif
 			ans = hbuf;
 			if (ttl > 0)
 				s->s_namecanon.nc_exp = now + SM_MIN(ttl,
@@ -4208,7 +4279,7 @@ host_map_lookup(map, name, av, statp)
 #if NETINET6
 			if (ans == hp->h_name)
 			{
-				static char n[MAXNAME + 1];
+				static char n[MAXNAME + 1];	/* EAI:ok */
 
 				/* hp->h_name is about to disappear */
 				(void) sm_strlcpy(n, ans, sizeof(n));
@@ -4515,12 +4586,14 @@ anynet_ntoa(sap)
 	(void) sm_snprintf(buf, sizeof(buf), "Family %d: ", sap->sa.sa_family);
 	bp = &buf[strlen(buf)];
 	ap = sap->sa.sa_data;
-	for (l = sizeof(sap->sa.sa_data); --l >= 0; )
+	for (l = sizeof(sap->sa.sa_data); --l >= 0 && SPACELEFT(buf, bp) > 3; )
 	{
 		(void) sm_snprintf(bp, SPACELEFT(buf, bp), "%02x:",
 				   *ap++ & 0377);
 		bp += 3;
 	}
+	SM_ASSERT(bp > buf);
+	SM_ASSERT(bp <= buf + sizeof(buf));
 	*--bp = '\0';
 	return buf;
 }
@@ -4613,7 +4686,7 @@ hostnamebyanyaddr(sap)
 #  if NETINET6
 		if (name == hp->h_name)
 		{
-			static char n[MAXNAME + 1];
+			static char n[MAXNAME + 1];	/* EAI:ok */
 
 			/* Copy the string, hp->h_name is about to disappear */
 			(void) sm_strlcpy(n, name, sizeof(n));
diff --git a/contrib/sendmail/src/deliver.c b/contrib/sendmail/src/deliver.c
index ff4b21487700..7d8008d80ac8 100644
--- a/contrib/sendmail/src/deliver.c
+++ b/contrib/sendmail/src/deliver.c
@@ -16,6 +16,8 @@
 
 SM_RCSID("@(#)$Id: deliver.c,v 8.1030 2013-11-22 20:51:55 ca Exp $")
 
+#include 
+
 #if HASSETUSERCONTEXT
 # include 
 #endif
@@ -54,6 +56,9 @@ static bool	iscltflgset __P((ENVELOPE *, int));
 # include 
 #endif
 
+#define ESCNULLMXRCPT "5.1.10"
+#define ERRNULLMX "556 Host does not accept mail: MX 0 ."
+
 /*
 **  SENDALL -- actually send all the messages.
 **
@@ -160,7 +165,7 @@ sendall(e, mode)
 			recip = "(nobody)";
 
 		errno = 0;
-		queueup(e, WILL_BE_QUEUED(mode), false);
+		queueup(e, WILL_BE_QUEUED(mode) ? QUP_FL_ANNOUNCE : QUP_FL_NONE);
 		e->e_flags |= EF_FATALERRS|EF_PM_NOTIFY|EF_CLRQUEUE;
 		ExitStat = EX_UNAVAILABLE;
 		syserr("554 5.4.6 Too many hops %d (%d max): from %s via %s, to %s",
@@ -527,7 +532,7 @@ sendall(e, mode)
 	       SuperSafe == SAFE_REALLY_POSTMILTER))) &&
 	    (!bitset(EF_INQUEUE, e->e_flags) || splitenv != NULL))
 	{
-		bool msync;
+		unsigned int qup_flags;
 
 		/*
 		**  Be sure everything is instantiated in the queue.
@@ -536,15 +541,18 @@ sendall(e, mode)
 		**  recipients.
 		*/
 
-#if !HASFLOCK
-		msync = false;
-#else
-		msync = mode == SM_FORK;
+		if (WILL_BE_QUEUED(mode))
+			qup_flags = QUP_FL_ANNOUNCE;
+		else
+			qup_flags = QUP_FL_NONE;
+#if HASFLOCK
+		if (mode == SM_FORK)
+			qup_flags |= QUP_FL_MSYNC;
 #endif
 
 		for (ee = splitenv; ee != NULL; ee = ee->e_sibling)
-			queueup(ee, WILL_BE_QUEUED(mode), msync);
-		queueup(e, WILL_BE_QUEUED(mode), msync);
+			queueup(ee, qup_flags);
+		queueup(e, qup_flags);
 	}
 
 	if (tTd(62, 10))
@@ -662,9 +670,7 @@ sendall(e, mode)
 			/* be sure we leave the temp files to our child */
 			/* close any random open files in the envelope */
 			closexscript(e);
-			if (e->e_dfp != NULL)
-				(void) sm_io_close(e->e_dfp, SM_TIME_DEFAULT);
-			e->e_dfp = NULL;
+			SM_CLOSE_FP(e->e_dfp);
 			e->e_flags &= ~EF_HAS_DF;
 
 			/* can't call unlockqueue to avoid unlink of xfp */
@@ -866,7 +872,7 @@ sendenvelope(e, mode)
 			return;
 		}
 		for (ee = e->e_sibling; ee != NULL; ee = ee->e_sibling)
-			queueup(ee, false, true);
+			queueup(ee, QUP_FL_MSYNC);
 
 		/* clean up */
 		for (ee = e->e_sibling; ee != NULL; ee = ee->e_sibling)
@@ -876,11 +882,7 @@ sendenvelope(e, mode)
 			unlockqueue(ee);
 
 			/* this envelope is marked unused */
-			if (ee->e_dfp != NULL)
-			{
-				(void) sm_io_close(ee->e_dfp, SM_TIME_DEFAULT);
-				ee->e_dfp = NULL;
-			}
+			SM_CLOSE_FP(ee->e_dfp);
 			ee->e_id = NULL;
 			ee->e_flags &= ~EF_HAS_DF;
 		}
@@ -891,10 +893,10 @@ sendenvelope(e, mode)
 	for (q = e->e_sendqueue; q != NULL; q = q->q_next)
 	{
 #if XDEBUG
-		char wbuf[MAXNAME + 20];
+		char wbuf[MAXNAME + 20]; /* EAI: might be too short, but that's ok for debugging */
 
 		(void) sm_snprintf(wbuf, sizeof(wbuf), "sendall(%.*s)",
-				   MAXNAME, q->q_paddr);
+				   MAXNAME, q->q_paddr); /* EAI: see above */
 		checkfd012(wbuf);
 #endif /* XDEBUG */
 		if (mode == SM_VERIFY)
@@ -922,7 +924,7 @@ sendenvelope(e, mode)
 			if (CheckpointInterval > 0 &&
 			    e->e_nsent >= CheckpointInterval)
 			{
-				queueup(e, false, false);
+				queueup(e, QUP_FL_NONE);
 				e->e_nsent = 0;
 			}
 			(void) deliver(e, q);
@@ -1233,6 +1235,63 @@ should_try_fbsh(e, tried_fallbacksmarthost, hostbuf, hbsz, status)
 	return false;
 }
 
+/*
+**  CLTFEATURES -- Get features for SMTP client
+**
+**	Parameters:
+**		e -- envelope
+**		clientname -- name of client.
+**
+**	Returns:
+**		EX_OK or EX_TEMPFAIL
+*/
+
+static int cltfeatures __P((ENVELOPE *, char *));
+static int
+cltfeatures(e, clientname)
+	ENVELOPE *e;
+	char *clientname;
+{
+	int r, i, idx;
+	char **pvp, c;
+	char pvpbuf[PSBUFSIZE];
+	char flags[64];	/* XXX */
+
+	SM_ASSERT(e != NULL);
+	SM_ASSERT(e->e_mci != NULL);
+	macdefine(&e->e_mci->mci_macro, A_PERM, macid("{client_flags}"), "");
+	pvp = NULL;
+	r = rscap("clt_features", clientname, "", e, &pvp, pvpbuf,
+		  sizeof(pvpbuf));
+	if (r != EX_OK)
+		return EX_OK;
+	if (pvp == NULL || pvp[0] == NULL || (pvp[0][0] & 0377) != CANONNET)
+		return EX_OK;
+	if (pvp[1] != NULL && sm_strncasecmp(pvp[1], "temp", 4) == 0)
+		return EX_TEMPFAIL;
+
+	/* XXX Note: this does not inherit defaults! */
+	for (idx = 0, i = 1; pvp[i] != NULL; i++)
+	{
+		c = pvp[i][0];
+		if (!(isascii(c) && !isspace(c) && isprint(c)))
+			continue;
+		if (idx >= sizeof(flags) - 4)
+			break;
+		flags[idx++] = c;
+		if (isupper(c))
+			flags[idx++] = c;
+		flags[idx++] = ' ';
+	}
+	flags[idx] = '\0';
+
+	macdefine(&e->e_mci->mci_macro, A_TEMP, macid("{client_flags}"), flags);
+	if (tTd(10, 30))
+		sm_dprintf("cltfeatures: mci=%p, flags=%s, {client_flags}=%s\n",
+			e->e_mci, flags, macvalue(macid("{client_flags}"), e));
+	return EX_OK;
+}
+
 /*
 **  DELIVER -- Deliver a message to a list of addresses.
 **
@@ -1370,7 +1429,7 @@ deliver(e, firstto)
 	int rpvect[2];
 	char *mxhosts[MAXMXHOSTS + 1];
 	char *pv[MAXPV + 1];
-	char buf[MAXNAME + 1];
+	char buf[MAXNAME + 1];	/* EAI:ok */
 	char cbuf[MAXPATHLEN];
 
 	errno = 0;
@@ -1440,7 +1499,9 @@ deliver(e, firstto)
 	rpath = remotename(p, m, RF_SENDERADDR|RF_CANONICAL, &rcode, e);
 	if (rcode != EX_OK && bitnset(M_xSMTP, m->m_flags))
 		goto cleanup;
-	if (strlen(rpath) > MAXNAME)
+
+	/* need to check external format, not internal! */
+	if (strlen(rpath) > MAXNAME_I)
 	{
 		rpath = shortenstring(rpath, MAXSHORTSTR);
 
@@ -1597,6 +1658,13 @@ deliver(e, firstto)
 		if (++rcptcount > to->q_mailer->m_maxrcpt)
 			break;
 
+		/*
+		**  prepare envelope for new session to avoid leakage
+		**  between delivery attempts.
+		*/
+
+		smtpclrse(e);
+
 		if (tTd(10, 1))
 		{
 			sm_dprintf("\nsend to ");
@@ -2020,6 +2088,7 @@ deliver(e, firstto)
 		}
 		else
 #endif /* NETUNIX */
+		/* "else" in #if code above */
 		{
 			CurHostName = pv[1];
 							/* XXX ??? */
@@ -2069,7 +2138,7 @@ deliver(e, firstto)
 		{
 			char sep = ':';
 			char *endp;
-			static char hostbuf[MAXNAME + 1];
+			static char hostbuf[MAXNAME_I + 1];
 			bool tried_fallbacksmarthost = false;
 #if DANE
 			unsigned long tlsa_flags;
@@ -2207,6 +2276,7 @@ deliver(e, firstto)
 			}
 			else
 #endif /* NETUNIX */
+			/* "else" in #if code above */
 			{
 				if (port == 0)
 					message("Connecting to %s via %s...",
@@ -2215,22 +2285,48 @@ deliver(e, firstto)
 					message("Connecting to %s port %d via %s...",
 						hostbuf, ntohs(port),
 						m->m_name);
+
+				/*
+				**  XXX OK to do this here already?
+				**  set the current connection information
+				**  required to set {client_flags} in e->e_mci
+				*/
+
+				e->e_mci = mci;
+				if ((i = cltfeatures(e, hostbuf)) != EX_OK)
+				{
+					if (LogLevel > 8)
+					sm_syslog(LOG_WARNING, e->e_id,
+						  "clt_features=TEMPFAIL, host=%s, status=skipped"
+						  , hostbuf);
+					/* XXX handle error! */
+					(void) sm_strlcpy(SmtpError,
+						"clt_features=TEMPFAIL",
+						sizeof(SmtpError));
 #if DANE
-				tlsa_flags |= (ste != NULL) ? Dane : DANE_NEVER;
+					tlsa_flags &= ~TLSAFLTEMP;
+#endif
+				}
+#if DANE
+				/* hack: disable DANE if requested */
+				if (iscltflgset(e, D_NODANE))
+					ste = NULL;
+				tlsa_flags |= ste != NULL ? Dane : DANE_NEVER;
 				dane_vrfy_ctx.dane_vrfy_chk = tlsa_flags;
 				dane_vrfy_ctx.dane_vrfy_port = m->m_port;
 				if (tTd(11, 11))
-					sm_dprintf("makeconnection: before: chk=%d, mode=%lX\n", dane_vrfy_ctx.dane_vrfy_chk, tlsa_flags);
+					sm_dprintf("makeconnection: before: chk=%d, tlsa_flags=%lX, {client_flags}=%s\n", dane_vrfy_ctx.dane_vrfy_chk, tlsa_flags, macvalue(macid("{client_flags}"), e));
 #endif
-				i = makeconnection(hostbuf, port, mci, e,
-						enough
+				if (EX_OK == i)
+					i = makeconnection(hostbuf, port, mci,
+						e, enough
 #if DANE
 						, &tlsa_flags
 #endif
 						);
 #if DANE
 				if (tTd(11, 11))
-					sm_dprintf("makeconnection: after: chk=%d, mode=%lX\n", dane_vrfy_ctx.dane_vrfy_chk, tlsa_flags);
+					sm_dprintf("makeconnection: after: chk=%d, tlsa_flags=%lX\n", dane_vrfy_ctx.dane_vrfy_chk, tlsa_flags);
 				if (dane_vrfy_ctx.dane_vrfy_chk != DANE_ALWAYS)
 					dane_vrfy_ctx.dane_vrfy_chk = DANEMODE(tlsa_flags);
 				if (EX_TEMPFAIL == i &&
@@ -2546,7 +2642,7 @@ deliver(e, firstto)
 			/* tweak niceness */
 			if (m->m_nice != 0)
 				(void) nice(m->m_nice);
-#endif /* HASNICE */
+#endif
 
 			/* reset group id */
 			if (bitnset(M_SPECIFIC_UID, m->m_flags))
@@ -2920,8 +3016,7 @@ deliver(e, firstto)
 			syserr("deliver: cannot create mailer input channel, fd=%d",
 			       mpvect[1]);
 			(void) close(rpvect[0]);
-			(void) sm_io_close(mci->mci_out, SM_TIME_DEFAULT);
-			mci->mci_out = NULL;
+			SM_CLOSE_FP(mci->mci_out);
 			rcode = EX_OSERR;
 			goto give_up;
 		}
@@ -2939,7 +3034,7 @@ deliver(e, firstto)
 #if STARTTLS || SASL
 		char *srvname;
 		extern SOCKADDR CurHostAddr;
-#endif /* STARTTLS || SASL */
+#endif
 
 #if SASL
 # define DONE_AUTH(f)		bitset(MCIF_AUTHACT, f)
@@ -3017,7 +3112,7 @@ deliver(e, firstto)
 			dane_vrfy_ctx.dane_vrfy_fp[0] = '\0';
 			dane_vrfy_ctx.dane_vrfy_res = 0;
 		}
-# endif
+# endif /* DANE */
 
 #endif /* STARTTLS || SASL */
 
@@ -3026,6 +3121,75 @@ deliver(e, firstto)
 #if SASL
 		mci->mci_saslcap = NULL;
 #endif
+#if _FFR_MTA_STS
+# define USEMTASTS (MTASTS && !SM_TLSI_IS(&(mci->mci_tlsi), TLSI_FL_NOSTS) && !iscltflgset(e, D_NOSTS))
+# if DANE
+#  define CHKMTASTS (USEMTASTS && (ste == NULL || ste->s_tlsa == NULL || SM_TLSI_IS(&(mci->mci_tlsi), TLSI_FL_NODANE)))
+# else
+#  define CHKMTASTS USEMTASTS
+# endif
+#endif /* _FFR_MTA_STS */
+#if _FFR_MTA_STS
+		if (!DONE_STARTTLS(mci->mci_flags))
+		{
+		/*
+		**  HACK: use the domain of the first valid RCPT for STS.
+		**  It seems whoever wrote the specs did not consider
+		**  SMTP sessions versus transactions.
+		**  (but what would you expect from people who try
+		**  to use https for "security" after relying on DNS?)
+		*/
+
+		macdefine(&e->e_macro, A_PERM, macid("{rcpt_addr}"), "");
+# if DANE
+		if (MTASTS && (ste != NULL && ste->s_tlsa != NULL))
+			macdefine(&e->e_macro, A_PERM, macid("{sts_sni}"), "DANE");
+		else
+# endif
+			macdefine(&e->e_macro, A_PERM, macid("{sts_sni}"), "");
+		if (USEMTASTS && firstto->q_user != NULL)
+		{
+			if (tTd(10, 64))
+			{
+				sm_dprintf("firstto ");
+				printaddr(sm_debug_file(), firstto, false);
+			}
+			macdefine(&e->e_macro, A_TEMP,
+				  macid("{rcpt_addr}"), firstto->q_user);
+		}
+		else if (USEMTASTS)
+		{
+			if (tTd(10, 64))
+			{
+				sm_dprintf("tochain ");
+				printaddr(sm_debug_file(), tochain, false);
+			}
+			for (to = tochain; to != NULL; to = to->q_tchain)
+			{
+				if (!QS_IS_UNMARKED(to->q_state))
+					continue;
+				if (to->q_user == NULL)
+					continue;
+				macdefine(&e->e_macro, A_TEMP,
+					  macid("{rcpt_addr}"), to->q_user);
+				break;
+			}
+		}
+		}
+#endif /* _FFR_MTA_STS */
+#if USE_EAI
+		if (!addr_is_ascii(e->e_from.q_paddr) && !e->e_smtputf8)
+			e->e_smtputf8 = true;
+		for (to = tochain; to != NULL && !e->e_smtputf8; to = to->q_tchain)
+		{
+			if (!QS_IS_UNMARKED(to->q_state))
+				continue;
+			if (!addr_is_ascii(to->q_user))
+				e->e_smtputf8 = true;
+		}
+		/* XXX reset e_smtputf8 to original state at the end? */
+#endif /* USE_EAI */
+
 		smtpinit(m, mci, e, ONLY_HELO(mci->mci_flags));
 		CLR_HELO(mci->mci_flags);
 
@@ -3104,6 +3268,13 @@ deliver(e, firstto)
 				{
 					/* start again without STARTTLS */
 					mci->mci_flags |= MCIF_TLSACT;
+# if DANE && _FFR_MTA_STS
+/* if DANE is used (and STS should be used): disable STS */
+/* also check MTASTS and NOSTS flag? */
+					if (ste != NULL && ste->s_tlsa != NULL &&
+					    !SM_TLSI_IS(&(mci->mci_tlsi), TLSI_FL_NODANE))
+						macdefine(&e->e_macro, A_PERM, macid("{rcpt_addr}"), "");
+# endif
 				}
 				else
 				{
@@ -3134,6 +3305,9 @@ deliver(e, firstto)
 					  case EX_UNAVAILABLE:
 						s = "NONE";
 						break;
+					  case EX_CONFIG:
+						s = "CONFIG";
+						break;
 
 					  /* everything else is a failure */
 					  default:
@@ -3155,6 +3329,7 @@ deliver(e, firstto)
 				*/
 
 				if (!bitset(MCIF_TLS, mci->mci_flags) &&
+				    !iscltflgset(e, D_NODANE) &&
 				    ste != NULL &&
 				    ste->s_tlsa != NULL &&
 				    ste->s_tlsa->dane_tlsa_n > 0)
@@ -3178,7 +3353,7 @@ deliver(e, firstto)
 			/*
 			**  rcode == EX_SOFTWARE is special:
 			**  the TLS negotiation failed
-			**  we have to drop the connection no matter what
+			**  we have to drop the connection no matter what.
 			**  However, we call tls_server to give it the chance
 			**  to log the problem and return an appropriate
 			**  error code.
@@ -3213,12 +3388,7 @@ deliver(e, firstto)
 				{
 					/* drop the connection */
 					mci->mci_state = MCIS_QUITING;
-					if (mci->mci_in != NULL)
-					{
-						(void) sm_io_close(mci->mci_in,
-								   SM_TIME_DEFAULT);
-						mci->mci_in = NULL;
-					}
+					SM_CLOSE_FP(mci->mci_out);
 					mci->mci_flags &= ~MCIF_TLSACT;
 					(void) endmailer(mci, e, pv);
 
@@ -3230,6 +3400,10 @@ deliver(e, firstto)
 # if DANE
 					     && dane_vrfy_ctx.dane_vrfy_chk !=
 						DANE_SECURE
+# endif
+# if _FFR_MTA_STS
+					     && !SM_TLSI_IS(&(mci->mci_tlsi),
+							TLSI_FL_STS_NOFB2CLR)
 # endif
 					    )
 					{
@@ -3399,8 +3573,8 @@ deliver(e, firstto)
 	if (bitnset(M_MAKE8BIT, m->m_flags) &&
 	    !bitset(MCIF_7BIT, mci->mci_flags) &&
 	    (p = hvalue("Content-Transfer-Encoding", e->e_header)) != NULL &&
-	     (sm_strcasecmp(p, "quoted-printable") == 0 ||
-	      sm_strcasecmp(p, "base64") == 0) &&
+	     (SM_STRCASEEQ(p, "quoted-printable") ||
+	      SM_STRCASEEQ(p, "base64")) &&
 	    (p = hvalue("Content-Type", e->e_header)) != NULL)
 	{
 		/* may want to convert 7 -> 8 */
@@ -3520,14 +3694,17 @@ deliver(e, firstto)
 
 		/* XXX this isn't pipelined... */
 		rcode = smtpmailfrom(m, mci, e);
+		mci->mci_okrcpts = 0;
+		mci->mci_retryrcpt = rcode == EX_TEMPFAIL;
 		if (rcode == EX_OK)
 		{
-			register int i;
+			register int rc;
 #if PIPELINING
 			ADDRESS *volatile pchain;
 #endif
 
 			/* send the recipient list */
+			rc = EX_OK;
 			tobuf[0] = '\0';
 			mci->mci_retryrcpt = false;
 			mci->mci_tolist = tobuf;
@@ -3544,16 +3721,25 @@ deliver(e, firstto)
 				/* mark recipient state as "ok so far" */
 				to->q_state = QS_OK;
 				e->e_to = to->q_paddr;
+#if _FFR_MTA_STS
+				if (CHKMTASTS && to->q_user != NULL)
+					macdefine(&e->e_macro, A_TEMP,
+						macid("{rcpt_addr}"), to->q_user);
+				else
+					macdefine(&e->e_macro, A_PERM,
+						macid("{rcpt_addr}"), "");
+#endif /* _FFR_MTA_STS */
 #if STARTTLS
-				i = rscheck("tls_rcpt", to->q_user, NULL, e,
+				rc = rscheck("tls_rcpt", to->q_user, NULL, e,
 					    RSF_RMCOMM|RSF_COUNT, 3,
 					    mci->mci_host, e->e_id, NULL, NULL);
-				if (i != EX_OK)
+				if (rc != EX_OK)
 				{
-					markfailure(e, to, mci, i, false);
-					giveresponse(i, to->q_status,  m, mci,
+					to->q_flags |= QINTREPLY;
+					markfailure(e, to, mci, rc, false);
+					giveresponse(rc, to->q_status, m, mci,
 						     ctladdr, xstart, e, to);
-					if (i == EX_TEMPFAIL)
+					if (rc == EX_TEMPFAIL)
 					{
 						mci->mci_retryrcpt = true;
 						to->q_state = QS_RETRY;
@@ -3562,9 +3748,9 @@ deliver(e, firstto)
 				}
 #endif /* STARTTLS */
 
-				i = smtprcpt(to, m, mci, e, ctladdr, xstart);
+				rc = smtprcpt(to, m, mci, e, ctladdr, xstart);
 #if PIPELINING
-				if (i == EX_OK &&
+				if (rc == EX_OK &&
 				    bitset(MCIF_PIPELINED, mci->mci_flags))
 				{
 					/*
@@ -3584,12 +3770,12 @@ deliver(e, firstto)
 					}
 				}
 #endif /* PIPELINING */
-				if (i != EX_OK)
+				if (rc != EX_OK)
 				{
-					markfailure(e, to, mci, i, false);
-					giveresponse(i, to->q_status, m, mci,
+					markfailure(e, to, mci, rc, false);
+					giveresponse(rc, to->q_status, m, mci,
 						     ctladdr, xstart, e, to);
-					if (i == EX_TEMPFAIL)
+					if (rc == EX_TEMPFAIL)
 						to->q_state = QS_RETRY;
 				}
 			}
@@ -3597,12 +3783,12 @@ deliver(e, firstto)
 			/* No recipients in list and no missing responses? */
 			if (tobuf[0] == '\0'
 #if PIPELINING
-			    && bitset(MCIF_PIPELINED, mci->mci_flags)
+			    /* && bitset(MCIF_PIPELINED, mci->mci_flags) */
 			    && mci->mci_nextaddr == NULL
 #endif
 			   )
 			{
-				rcode = EX_OK;
+				rcode = rc;
 				e->e_to = NULL;
 				if (bitset(MCIF_CACHED, mci->mci_flags))
 					smtprset(m, mci, e);
@@ -3613,7 +3799,10 @@ deliver(e, firstto)
 				rcode = smtpdata(m, mci, e, ctladdr, xstart);
 			}
 		}
-		if (rcode == EX_TEMPFAIL && nummxhosts > hostnum)
+
+		if (rcode == EX_TEMPFAIL && nummxhosts > hostnum
+		    && (mci->mci_retryrcpt || mci->mci_okrcpts > 0)
+		   )
 		{
 			/* try next MX site */
 			goto tryhost;
@@ -3697,7 +3886,7 @@ deliver(e, firstto)
 
 		if (CheckpointInterval > 0 && e->e_nsent >= CheckpointInterval)
 		{
-			queueup(e, false, false);
+			queueup(e, QUP_FL_NONE);
 			e->e_nsent = 0;
 		}
 
@@ -3773,7 +3962,13 @@ deliver(e, firstto)
 
 	if (tobuf[0] != '\0')
 	{
-		giveresponse(rcode, NULL, m, mci, ctladdr, xstart, e, NULL);
+		giveresponse(rcode,
+#if _FFR_NULLMX_STATUS
+			(NULL == mci || SM_IS_EMPTY(mci->mci_status))
+				? NULL :
+#endif
+				mci->mci_status,
+			m, mci, ctladdr, xstart, e, NULL);
 #if 0
 		/*
 		**  This code is disabled for now because I am not
@@ -3977,7 +4172,7 @@ markfailure(e, q, mci, rcode, ovr)
 	}
 	if (rcode != EX_OK && q->q_rstatus == NULL &&
 	    q->q_mailer != NULL && q->q_mailer->m_diagtype != NULL &&
-	    sm_strcasecmp(q->q_mailer->m_diagtype, "X-UNIX") == 0)
+	    SM_STRCASEEQ(q->q_mailer->m_diagtype, "X-UNIX"))
 	{
 		char buf[16];
 
@@ -4045,11 +4240,7 @@ endmailer(mci, e, pv)
 	mci_unlock_host(mci);
 
 	/* close output to mailer */
-	if (mci->mci_out != NULL)
-	{
-		(void) sm_io_close(mci->mci_out, SM_TIME_DEFAULT);
-		mci->mci_out = NULL;
-	}
+	SM_CLOSE_FP(mci->mci_out);
 
 	/* copy any remaining input to transcript */
 	if (mci->mci_in != NULL && mci->mci_state != MCIS_ERROR &&
@@ -4075,11 +4266,7 @@ endmailer(mci, e, pv)
 #endif
 
 	/* now close the input */
-	if (mci->mci_in != NULL)
-	{
-		(void) sm_io_close(mci->mci_in, SM_TIME_DEFAULT);
-		mci->mci_in = NULL;
-	}
+	SM_CLOSE_FP(mci->mci_in);
 	mci->mci_state = MCIS_CLOSED;
 
 	errno = save_errno;
@@ -4195,6 +4382,10 @@ giveresponse(status, dsn, m, mci, ctladdr, xstart, e, to)
 		SM_ASSERT(0);
 	}
 
+	if (tTd(11, 4))
+		sm_dprintf("giveresponse: status=%d, e->e_message=%s, SmtpError=%s\n",
+			status, e->e_message, SmtpError);
+
 	/*
 	**  Compute status message from code.
 	*/
@@ -4230,7 +4421,7 @@ giveresponse(status, dsn, m, mci, ctladdr, xstart, e, to)
 		if (h_errno == TRY_AGAIN)
 			statmsg = sm_errstring(h_errno + E_DNSBASE);
 		else
-#endif /* NAMED_BIND */
+#endif
 		{
 			if (errnum != 0)
 				statmsg = sm_errstring(errnum);
@@ -4295,6 +4486,25 @@ giveresponse(status, dsn, m, mci, ctladdr, xstart, e, to)
 		usestat = true;
 	}
 #endif /* NAMED_BIND */
+#if USE_EAI
+	else if (errnum == 0 && status == EX_DATAERR
+		&& e->e_message != NULL && e->e_message[0] != '\0')
+	{
+		int m;
+
+		/* XREF: 2nd arg must be coordinated with smtpmailfrom() */
+		m = skipaddrhost(e->e_message, false);
+
+		/*
+		**  XXX Why is the SMTP reply code needed here?
+		**  How to avoid a hard-coded value?
+		*/
+
+		(void) sm_snprintf(buf, sizeof(buf), "550 %s", e->e_message + m);
+		statmsg = buf;
+		usestat = true;
+	}
+#endif /* USE_EAI */
 	else
 	{
 		statmsg = exmsg;
@@ -4382,11 +4592,11 @@ giveresponse(status, dsn, m, mci, ctladdr, xstart, e, to)
 		logdelivery(m, mci, dsn, statmsg + off, ctladdr, xstart, e, to, status);
 
 	if (tTd(11, 2))
-		sm_dprintf("giveresponse: status=%d, dsn=%s, e->e_message=%s, errnum=%d\n",
+		sm_dprintf("giveresponse: status=%d, dsn=%s, e->e_message=%s, errnum=%d, statmsg=%s\n",
 			   status,
 			   dsn == NULL ? "" : dsn,
 			   e->e_message == NULL ? "" : e->e_message,
-			   errnum);
+			   errnum, statmsg);
 
 	if (status != EX_TEMPFAIL)
 		setstat(status);
@@ -4448,6 +4658,10 @@ logdelivery(m, mci, dsn, status, ctladdr, xstart, e, to, rcode)
 	int l;
 	time_t now = curtime();
 	char buf[1024];
+#if _FFR_8BITENVADDR
+	char xbuf[SM_MAX(SYSLOG_BUFSIZE, MAXNAME)];	/* EAI:ok */
+#endif
+	char *xstr;
 
 #if (SYSLOG_BUFSIZE) >= 256
 	/* ctladdr: max 106 bytes */
@@ -4550,11 +4764,11 @@ logdelivery(m, mci, dsn, status, ctladdr, xstart, e, to, rcode)
 # if (STATLEN) < 63
 #  undef STATLEN
 #  define STATLEN	63
-# endif /* (STATLEN) < 63 */
+# endif
 # if (STATLEN) > 203
 #  undef STATLEN
 #  define STATLEN	203
-# endif /* (STATLEN) > 203 */
+# endif
 
 	/*
 	**  Notes:
@@ -4574,9 +4788,9 @@ logdelivery(m, mci, dsn, status, ctladdr, xstart, e, to, rcode)
 	**  reply - how to avoid that?
 	*/
 
-	/* only show errors */
-	if (rcode != EX_OK && to != NULL && to->q_rstatus != NULL &&
-	    *to->q_rstatus != '\0')
+	/* only show errors from server */
+	if (rcode != EX_OK && to != NULL && !SM_IS_EMPTY(to->q_rstatus)
+	    && !bitset(QINTREPLY, to->q_flags))
 	{
 		(void) sm_snprintf(bp, SPACELEFT(buf, bp),
 			", reply=%s",
@@ -4593,6 +4807,16 @@ logdelivery(m, mci, dsn, status, ctladdr, xstart, e, to, rcode)
 			shortenstring(e->e_text, STATLEN));
 		bp += strlen(bp);
 	}
+#if _FFR_NULLMX_STATUS
+	/* Hack for MX 0 . : how to make this general? */
+	else if (rcode != EX_OK && NULL == to && dsn != NULL &&
+		 strcmp(dsn, ESCNULLMXRCPT) == 0)
+	{
+		(void) sm_snprintf(bp, SPACELEFT(buf, bp),
+			", status=%s", ERRNULLMX);
+		bp += strlen(bp);
+	}
+#endif
 
 	/* stat: max 210 bytes */
 	if ((bp - buf) > (sizeof(buf) - ((STATLEN) + 20)))
@@ -4626,11 +4850,24 @@ logdelivery(m, mci, dsn, status, ctladdr, xstart, e, to, rcode)
 		}
 		if (p == q)
 			break;
+# if _FFR_8BITENVADDR
+		/* XXX is this correct? dequote all of p? */
+		(void) dequote_internal_chars(p, xbuf, sizeof(xbuf));
+		xstr = xbuf;
+# else
+		xstr = p;
+# endif
 		sm_syslog(LOG_INFO, e->e_id, "to=%.*s [more]%s",
-			  (int) (++q - p), p, buf);
+			  (int) (++q - p), xstr, buf);
 		p = q;
 	}
-	sm_syslog(LOG_INFO, e->e_id, "to=%.*s%s", l, p, buf);
+# if _FFR_8BITENVADDR
+	(void) dequote_internal_chars(p, xbuf, sizeof(xbuf));
+	xstr = xbuf;
+# else
+	xstr = p;
+# endif
+	sm_syslog(LOG_INFO, e->e_id, "to=%.*s%s", l, xstr, buf);
 
 #else /* (SYSLOG_BUFSIZE) >= 256 */
 
@@ -4767,7 +5004,7 @@ putfromline(mci, e)
 		if (bang == NULL)
 		{
 			char *at;
-			char hname[MAXNAME];
+			char hname[MAXNAME];	/* EAI:ok */
 
 			/*
 			**  If we can construct a UUCP path, do so
@@ -5844,8 +6081,8 @@ mailfile(filename, mailer, ctladdr, sfflags, e)
 		if (bitnset(M_MAKE8BIT, mailer->m_flags) &&
 		    !bitset(MCIF_7BIT, mcibuf.mci_flags) &&
 		    (p = hvalue("Content-Transfer-Encoding", e->e_header)) != NULL &&
-		    (sm_strcasecmp(p, "quoted-printable") == 0 ||
-		     sm_strcasecmp(p, "base64") == 0) &&
+		    (SM_STRCASEEQ(p, "quoted-printable") ||
+		     SM_STRCASEEQ(p, "base64")) &&
 		    (p = hvalue("Content-Type", e->e_header)) != NULL)
 		{
 			/* may want to convert 7 -> 8 */
@@ -5876,9 +6113,9 @@ mailfile(filename, mailer, ctladdr, sfflags, e)
 #if HASFCHMOD
 		(void) fchmod(sm_io_getinfo(f, SM_IO_WHAT_FD, NULL),
 			      (MODE_T) mode);
-#else /* HASFCHMOD */
+#else
 		(void) chmod(filename, (MODE_T) mode);
-#endif /* HASFCHMOD */
+#endif
 		if (sm_io_close(f, SM_TIME_DEFAULT) < 0)
 			setstat(EX_IOERR);
 		(void) sm_io_flush(smioout, SM_TIME_DEFAULT);
@@ -6017,6 +6254,7 @@ hostsignature(m, host, ad)
 	int hl;
 	char *hp;
 	char *endp;
+	char *lstr;
 	int oldoptions = _res.options;
 	char *mxhosts[MAXMXHOSTS + 1];
 	unsigned short mxprefs[MAXMXHOSTS + 1];
@@ -6120,10 +6358,9 @@ hostsignature(m, host, ad)
 			auto int rcode;
 			int ttl;
 
-			GETMPORT(m);
 			nmx = getmxrr(hp, mxhosts, mxprefs,
 				      DROPLOCALHOST|TRYFALLBACK|(ad ? ISAD :0),
-				      &rcode, &ttl, M_PORT(m));
+				      &rcode, &ttl, GETMPORT(m));
 			if (nmx <= 0)
 			{
 				int save_errno;
@@ -6136,8 +6373,8 @@ hostsignature(m, host, ad)
 				mci->mci_herrno = h_errno;
 				mci->mci_lastuse = now;
 				if (nmx == NULLMX)
-					mci_setstat(mci, rcode, "5.7.27",
-						    "550 Host does not accept mail");
+					mci_setstat(mci, rcode, ESCNULLMXRCPT,
+						    ERRNULLMX);
 				else if (rcode == EX_NOHOST)
 					mci_setstat(mci, rcode, "5.1.2",
 						    "550 Host unknown");
@@ -6218,7 +6455,8 @@ hostsignature(m, host, ad)
 			*endp++ = sep;
 		prevsep = sep;
 	}
-	makelower(s->s_hostsig.hs_sig);
+	lstr = makelower_a(&s->s_hostsig.hs_sig, NULL);
+	ASSIGN_IFDIFF(s->s_hostsig.hs_sig, lstr);
 	if (ConfigLevel < 2)
 		_res.options = oldoptions;
 #else /* NAMED_BIND */
@@ -6438,6 +6676,7 @@ starttls(m, mci, e
 {
 	int smtpresult;
 	int result = 0;
+	int ret = EX_OK;
 	int rfd, wfd;
 	SSL *clt_ssl = NULL;
 	time_t tlsstart;
@@ -6454,30 +6693,7 @@ starttls(m, mci, e
 		return EX_TEMPFAIL;
 	}
 
-	smtpmessage("STARTTLS", m, mci);
-
-	/* get the reply */
-	smtpresult = reply(m, mci, e, TimeOuts.to_starttls, NULL, NULL,
-			XS_STARTTLS);
-
-	/* check return code from server */
-	if (REPLYTYPE(smtpresult) == 4)
-		return EX_TEMPFAIL;
-	if (smtpresult == 501)
-		return EX_USAGE;
-	if (smtpresult == -1)
-		return smtpresult;
-
-	/* not an expected reply but we have to deal with it */
-	if (REPLYTYPE(smtpresult) == 5)
-		return EX_UNAVAILABLE;
-	if (smtpresult != 220)
-		return EX_PROTOCOL;
-
-	if (LogLevel > 13)
-		sm_syslog(LOG_INFO, NOQID, "STARTTLS=client, start=ok");
-
-	/* start connection */
+	/* clt_ssl needed for get_tls_se_features() hence create here */
 	if ((clt_ssl = SSL_new(clt_ctx)) == NULL)
 	{
 		if (LogLevel > 5)
@@ -6488,14 +6704,55 @@ starttls(m, mci, e
 		}
 		return EX_SOFTWARE;
 	}
-	/* SSL_clear(clt_ssl); ? */
 
-	if (get_tls_se_options(e, clt_ssl, &mci->mci_tlsi, false) != 0)
+	ret = get_tls_se_features(e, clt_ssl, &mci->mci_tlsi, false);
+	if (EX_OK != ret)
 	{
 		sm_syslog(LOG_ERR, NOQID,
-			  "STARTTLS=client, get_tls_se_options=fail");
-		return EX_SOFTWARE;
+			  "STARTTLS=client, get_tls_se_features=failed, ret=%d",
+				ret);
+		goto fail;
 	}
+
+	smtpmessage("STARTTLS", m, mci);
+
+	/* get the reply */
+	smtpresult = reply(m, mci, e, TimeOuts.to_starttls, NULL, NULL,
+			XS_STARTTLS);
+
+	/* check return code from server */
+	if (REPLYTYPE(smtpresult) == 4)
+	{
+		ret = EX_TEMPFAIL;
+		goto fail;
+	}
+	if (smtpresult == 501)
+	{
+		ret = EX_USAGE;
+		goto fail;
+	}
+	if (smtpresult == -1)
+	{
+		ret = smtpresult;
+		goto fail;
+	}
+
+	/* not an expected reply but we have to deal with it */
+	if (REPLYTYPE(smtpresult) == 5)
+	{
+		ret = EX_UNAVAILABLE;
+		goto fail;
+	}
+	if (smtpresult != 220)
+	{
+		ret = EX_PROTOCOL;
+		goto fail;
+	}
+
+	if (LogLevel > 13)
+		sm_syslog(LOG_INFO, NOQID, "STARTTLS=client, start=ok");
+
+	/* SSL_clear(clt_ssl); ? */
 	result = SSL_set_ex_data(clt_ssl, TLSsslidx, &mci->mci_tlsi);
 	if (0 == result)
 	{
@@ -6506,7 +6763,7 @@ starttls(m, mci, e
 				  result, TLSsslidx);
 			tlslogerr(LOG_WARNING, 9, "client");
 		}
-		return EX_SOFTWARE;
+		goto fail;
 	}
 # if DANE
 	if (SM_TLSI_IS(&(mci->mci_tlsi), TLSI_FL_NODANE))
@@ -6515,29 +6772,50 @@ starttls(m, mci, e
 	{
 		int r;
 
-#  define SM_IS_EMPTY(s)	(NULL == (s) || '\0' == *(s))
-
 		/* set SNI only if there is a TLSA RR */
 		if (dane_get_tlsa(dane_vrfy_ctx) != NULL &&
 		    !(SM_IS_EMPTY(dane_vrfy_ctx->dane_vrfy_host) &&
-		      SM_IS_EMPTY(dane_vrfy_ctx->dane_vrfy_sni)) &&
-		    (r = SSL_set_tlsext_host_name(clt_ssl,
+		      SM_IS_EMPTY(dane_vrfy_ctx->dane_vrfy_sni)))
+		{
+#  if _FFR_MTA_STS
+			SM_FREE(STS_SNI);
+#  endif
+			if ((r = SSL_set_tlsext_host_name(clt_ssl,
 				(!SM_IS_EMPTY(dane_vrfy_ctx->dane_vrfy_sni)
 				? dane_vrfy_ctx->dane_vrfy_sni
 				: dane_vrfy_ctx->dane_vrfy_host))) <= 0)
+			{
+				if (LogLevel > 5)
+				{
+					sm_syslog(LOG_ERR, NOQID,
+						  "STARTTLS=client, host=%s, SSL_set_tlsext_host_name=%d",
+						  dane_vrfy_ctx->dane_vrfy_host, r);
+				}
+				tlslogerr(LOG_ERR, 5, "client");
+				/* return EX_SOFTWARE; */
+			}
+		}
+	}
+	memcpy(&mci->mci_tlsi.tlsi_dvc, dane_vrfy_ctx, sizeof(*dane_vrfy_ctx));
+# endif /* DANE */
+# if _FFR_MTA_STS
+	if (STS_SNI != NULL)
+	{
+		int r;
+
+		if ((r = SSL_set_tlsext_host_name(clt_ssl, STS_SNI)) <= 0)
 		{
 			if (LogLevel > 5)
 			{
 				sm_syslog(LOG_ERR, NOQID,
 					  "STARTTLS=client, host=%s, SSL_set_tlsext_host_name=%d",
-					  dane_vrfy_ctx->dane_vrfy_host, r);
+					  STS_SNI, r);
 			}
 			tlslogerr(LOG_ERR, 5, "client");
 			/* return EX_SOFTWARE; */
 		}
 	}
-	memcpy(&mci->mci_tlsi.tlsi_dvc, dane_vrfy_ctx, sizeof(*dane_vrfy_ctx));
-# endif /* DANE */
+# endif /* _FFR_MTA_STS */
 
 	rfd = sm_io_getinfo(mci->mci_in, SM_IO_WHAT_FD, NULL);
 	wfd = sm_io_getinfo(mci->mci_out, SM_IO_WHAT_FD, NULL);
@@ -6553,7 +6831,7 @@ starttls(m, mci, e
 				  result);
 			tlslogerr(LOG_WARNING, 9, "client");
 		}
-		return EX_SOFTWARE;
+		goto fail;
 	}
 	SSL_set_connect_state(clt_ssl);
 	tlsstart = curtime();
@@ -6584,8 +6862,7 @@ starttls(m, mci, e
 			tlslogerr(LOG_WARNING, 9, "client");
 		}
 
-		SM_SSL_FREE(clt_ssl);
-		return EX_SOFTWARE;
+		goto fail;
 	}
 	mci->mci_ssl = clt_ssl;
 	result = tls_get_info(mci->mci_ssl, false, mci->mci_host,
@@ -6595,10 +6872,12 @@ starttls(m, mci, e
 	if (sfdctls(&mci->mci_in, &mci->mci_out, mci->mci_ssl) == 0)
 		return EX_OK;
 
+  fail:
 	/* failure */
 	SM_SSL_FREE(clt_ssl);
-	return EX_SOFTWARE;
+	return (EX_OK == ret) ? EX_SOFTWARE : ret;
 }
+
 /*
 **  ENDTLSCLT -- shutdown secure connection (client side)
 **
diff --git a/contrib/sendmail/src/domain.c b/contrib/sendmail/src/domain.c
index c45abf1acc09..027038e283db 100644
--- a/contrib/sendmail/src/domain.c
+++ b/contrib/sendmail/src/domain.c
@@ -13,7 +13,7 @@
 
 #include 
 #include "map.h"
-#if _FFR_EAI
+#if USE_EAI
 #include 
 #endif
 
@@ -23,8 +23,9 @@ SM_RCSID("@(#)$Id: domain.c,v 8.205 2013-11-22 20:51:55 ca Exp $ (with name serv
 SM_RCSID("@(#)$Id: domain.c,v 8.205 2013-11-22 20:51:55 ca Exp $ (without name server)")
 #endif
 
-#if NAMED_BIND
+#include 
 
+#if NAMED_BIND
 # include 
 # include 
 # if DANE
@@ -114,14 +115,13 @@ tlsaadd(name, dr, dane_tlsa, dnsrc, n, pttl, level)
 
 		/* check previous error and keep the "most important" one? */
 		dane_tlsa->dane_tlsa_dnsrc = dnsrc;
-# if DNSSEC_TEST
+#  if DNSSEC_TEST
 		if (tTd(8, 110))
-			*pttl = tTdlevel(8)-110;	/* how to make this an option? */
+			*pttl = tTdlevel(8)-110; /* how to make this an option? */
 		else
-# else
+#  endif
+		/* "else" in #if code above */
 			*pttl = SM_NEG_TTL;
-# endif
-
 		return n;
 	}
 	if (dr == NULL)
@@ -418,23 +418,23 @@ getfallbackmxrr(host)
 	int ttl;
 	static time_t renew = 0;
 
-#if 0
+# if 0
 	/* This is currently done before this function is called. */
-	if (host == NULL || *host == '\0')
+	if (SM_IS_EMPTY(host))
 		return 0;
-#endif /* 0 */
+# endif /* 0 */
 	if (NumFallbackMXHosts > 0 && renew > curtime())
 		return NumFallbackMXHosts;
 
 	/* for DANE we need to invoke getmxrr() to get the TLSA RRs. */
-#if !DANE
+# if !DANE
 	if (host[0] == '[')
 	{
 		fbhosts[0] = host;
 		NumFallbackMXHosts = 1;
 	}
 	else
-#endif
+# endif
 	{
 		/* free old data */
 		for (i = 0; i < NumFallbackMXHosts; i++)
@@ -447,9 +447,9 @@ getfallbackmxrr(host)
 		*/
 
 		NumFallbackMXHosts = getmxrr(host, fbhosts, NULL,
-#if DANE
+# if DANE
 					(DANE_SECURE == Dane) ?  ISAD :
-#endif
+# endif
 					0,
 					&rcode, &ttl, 0);
 		renew = curtime() + ttl;
@@ -496,11 +496,44 @@ fallbackmxrr(nmx, prefs, mxhosts)
 	return nmx;
 }
 
+# if USE_EAI
+
+/*
+**  HN2ALABEL -- convert hostname in U-label format to A-label format
+**
+**	Parameters:
+**		hostname -- hostname in U-label format
+**
+**	Returns:
+**		hostname in A-label format in a local static buffer.
+**		It must be copied before the function is called again.
+*/
+
+const char *
+hn2alabel(hostname)
+	const char *hostname;
+{
+	UErrorCode error = U_ZERO_ERROR;
+	UIDNAInfo info = UIDNA_INFO_INITIALIZER;
+	UIDNA *idna;
+	static char buf[MAXNAME_I];	/* XXX ??? */
+
+	if (addr_is_ascii(hostname))
+		return hostname;
+	idna = uidna_openUTS46(UIDNA_NONTRANSITIONAL_TO_ASCII, &error);
+	(void) uidna_nameToASCII_UTF8(idna, hostname, strlen(hostname),
+				     buf, sizeof(buf) - 1,
+				     &info, &error);
+	uidna_close(idna);
+	return buf;
+}
+# endif /* USE_EAI */
+
 /*
 **  GETMXRR -- get MX resource records for a domain
 **
 **	Parameters:
-**		host -- the name of the host to MX.
+**		host -- the name of the host to MX [must be x]
 **		mxhosts -- a pointer to a return buffer of MX records.
 **		mxprefs -- a pointer to a return buffer of MX preferences.
 **			If NULL, don't try to populate.
@@ -554,10 +587,11 @@ getmxrr(host, mxhosts, mxprefs, flags, rcode, pttl, port)
 	int ttl = 0;
 	bool ad;
 	bool seennullmx = false;
-	extern int res_query(), res_search();
+	extern int res_query __P((const char *, int, int, u_char *, int));
+	extern int res_search __P((const char *, int, int , u_char *, int));
 # if DANE
 	bool cname2mx;
-	char qname[MAXNAME];
+	char qname[MAXNAME];	/* EAI: copy of host: ok? */
 	unsigned long old_options = 0;
 # endif
 
@@ -605,22 +639,13 @@ getmxrr(host, mxhosts, mxprefs, flags, rcode, pttl, port)
 		(void) sm_strlcpy(qname, host, sizeof(qname));
 # endif /* DANE */
 
-# if _FFR_EAI
+# if USE_EAI
 	if (!addr_is_ascii(host))
 	{
-		char buf[1024];
-		UErrorCode error = U_ZERO_ERROR;
-		UIDNAInfo info = UIDNA_INFO_INITIALIZER;
-		UIDNA *idna;
-
-		idna = uidna_openUTS46(UIDNA_NONTRANSITIONAL_TO_ASCII, &error);
-		(void) uidna_nameToASCII_UTF8(idna, host, strlen(host),
-					     buf, sizeof(buf) - 1,
-					     &info, &error);
-		uidna_close(idna);
-		host = sm_rpool_strdup_x(CurEnv->e_rpool, buf);
+		/* XXX memory leak? */
+		host = sm_rpool_strdup_x(CurEnv->e_rpool, hn2alabel(host));
 	}
-# endif /* _FFR_EAI */
+# endif /* USE_EAI */
 
 	/*
 	**  If we don't have MX records in our host switch, don't
@@ -865,7 +890,7 @@ getmxrr(host, mxhosts, mxprefs, flags, rcode, pttl, port)
 	/* delete duplicates from list (yes, some bozos have duplicates) */
 	for (i = 0; i < nmx - 1; )
 	{
-		if (sm_strcasecmp(mxhosts[i], mxhosts[i + 1]) != 0)
+		if (!SM_STRCASEEQ(mxhosts[i], mxhosts[i + 1]))
 			i++;
 		else
 		{
@@ -980,6 +1005,25 @@ getmxrr(host, mxhosts, mxprefs, flags, rcode, pttl, port)
 # endif /* NETINET6 */
 				else
 				{
+# if USE_EAI
+					char *hn;
+
+					hn = MXHostBuf + 1;
+					if (!addr_is_ascii(hn))
+					{
+						const char *ahn;
+
+						ahn = hn2alabel(hn);
+						if (strlen(ahn) >= sizeof(MXHostBuf) - 1)
+						{
+							*rcode = EX_CONFIG;
+							syserr("Encoded host name %s too long",
+							       shortenstring(ahn, MAXSHORTSTR));
+							goto error;
+						}
+						(void) sm_strlcpy(hn, ahn, sizeof(MXHostBuf) - 1);
+					}
+# endif /* USE_EAI */
 					trycanon = true;
 					mxhosts[0]++;
 				}
diff --git a/contrib/sendmail/src/envelope.c b/contrib/sendmail/src/envelope.c
index 08d48b171e6a..b1e61f134e15 100644
--- a/contrib/sendmail/src/envelope.c
+++ b/contrib/sendmail/src/envelope.c
@@ -12,6 +12,7 @@
  */
 
 #include 
+#include 
 
 SM_RCSID("@(#)$Id: envelope.c,v 8.313 2013-11-22 20:51:55 ca Exp $")
 
@@ -370,8 +371,8 @@ dropenvelope(e, fulldrop, split)
 			    strcmp(e->e_from.q_paddr, "<>") != 0 &&
 			    sm_strncasecmp(e->e_from.q_paddr, "owner-", 6) != 0 &&
 			    (strlen(e->e_from.q_paddr) <= 8 ||
-			     sm_strcasecmp(&e->e_from.q_paddr[strlen(e->e_from.q_paddr) - 8],
-					   "-request") != 0))
+			     !SM_STRCASEEQ(&e->e_from.q_paddr[strlen(e->e_from.q_paddr) - 8],
+					   "-request")))
 			{
 				for (q = e->e_sendqueue; q != NULL;
 				     q = q->q_next)
@@ -517,7 +518,7 @@ dropenvelope(e, fulldrop, split)
 	    e->e_class >= 0)
 	{
 		auto ADDRESS *rlist = NULL;
-		char pcopy[MAXNAME];
+		char pcopy[MAXNAME_I];
 
 		if (failure_return)
 		{
@@ -553,11 +554,7 @@ dropenvelope(e, fulldrop, split)
 		}
 		if (!panic)
 		{
-			if (e->e_dfp != NULL)
-			{
-				(void) sm_io_close(e->e_dfp, SM_TIME_DEFAULT);
-				e->e_dfp = NULL;
-			}
+			SM_CLOSE_FP(e->e_dfp);
 			(void) xunlink(queuename(e, DATAFL_LETTER));
 		}
 		if (panic && QueueMode == QM_LOST)
@@ -584,7 +581,7 @@ dropenvelope(e, fulldrop, split)
 	else if (queueit || !bitset(EF_INQUEUE, e->e_flags))
 	{
 		if (!split)
-			queueup(e, false, true);
+			queueup(e, QUP_FL_MSYNC);
 		else
 		{
 			ENVELOPE *oldsib;
@@ -607,8 +604,8 @@ dropenvelope(e, fulldrop, split)
 					(long) geteuid());
 			}
 			for (ee = e->e_sibling; ee != NULL; ee = ee->e_sibling)
-				queueup(ee, false, true);
-			queueup(e, false, true);
+				queueup(ee, QUP_FL_MSYNC);
+			queueup(e, QUP_FL_MSYNC);
 
 			/* clean up */
 			for (ee = e->e_sibling; ee != NULL; ee = ee->e_sibling)
@@ -621,12 +618,7 @@ dropenvelope(e, fulldrop, split)
 				unlockqueue(ee);
 
 				/* this envelope is marked unused */
-				if (ee->e_dfp != NULL)
-				{
-					(void) sm_io_close(ee->e_dfp,
-							   SM_TIME_DEFAULT);
-					ee->e_dfp = NULL;
-				}
+				SM_CLOSE_FP(ee->e_dfp);
 				ee->e_id = NULL;
 				ee->e_flags &= ~EF_HAS_DF;
 			}
@@ -641,11 +633,7 @@ dropenvelope(e, fulldrop, split)
 	unlockqueue(e);
 
 	/* make sure that this envelope is marked unused */
-	if (e->e_dfp != NULL)
-	{
-		(void) sm_io_close(e->e_dfp, SM_TIME_DEFAULT);
-		e->e_dfp = NULL;
-	}
+	SM_CLOSE_FP(e->e_dfp);
 	e->e_id = NULL;
 	e->e_flags &= ~EF_HAS_DF;
 	if (panic)
@@ -690,11 +678,8 @@ clearenvelope(e, fullclear, rpool)
 	if (!fullclear)
 	{
 		/* clear out any file information */
-		if (e->e_xfp != NULL)
-			(void) sm_io_close(e->e_xfp, SM_TIME_DEFAULT);
-		if (e->e_dfp != NULL)
-			(void) sm_io_close(e->e_dfp, SM_TIME_DEFAULT);
-		e->e_xfp = e->e_dfp = NULL;
+		SM_CLOSE_FP(e->e_xfp);
+		SM_CLOSE_FP(e->e_dfp);
 	}
 
 	/*
@@ -950,8 +935,7 @@ closexscript(e)
 	if (e->e_lockfp == NULL)
 		syserr("closexscript: job not locked");
 #endif
-	(void) sm_io_close(e->e_xfp, SM_TIME_DEFAULT);
-	e->e_xfp = NULL;
+	SM_CLOSE_FP(e->e_xfp);
 }
 /*
 **  SETSENDER -- set the person who this message is from
@@ -975,7 +959,7 @@ closexscript(e)
 **	ourselves.
 **
 **	Parameters:
-**		from -- the person we would like to believe this message
+**		from -- the person we would like to believe this message [i]
 **			is from, as specified on the command line.
 **		e -- the envelope in which we would like the sender set.
 **		delimptr -- if non-NULL, set to the location of the
@@ -1003,7 +987,7 @@ setsender(from, e, delimptr, delimchar, internal)
 	register char **pvp;
 	char *realname = NULL;
 	char *bp;
-	char buf[MAXNAME + 2];
+	char buf[MAXNAME_I + 2];
 	char pvpbuf[PSBUFSIZE];
 	extern char *FullName;
 
@@ -1021,7 +1005,7 @@ setsender(from, e, delimptr, delimchar, internal)
 	if (bitset(EF_QUEUERUN, e->e_flags) || OpMode == MD_SMTP ||
 	    OpMode == MD_ARPAFTP || OpMode == MD_DAEMON)
 		realname = from;
-	if (realname == NULL || realname[0] == '\0')
+	if (SM_IS_EMPTY(realname))
 		realname = username();
 
 	if (ConfigLevel < 2)
@@ -1044,7 +1028,7 @@ setsender(from, e, delimptr, delimchar, internal)
 		if (from != NULL && LogLevel > 2)
 		{
 			char *p;
-			char ebuf[MAXNAME * 2 + 2];
+			char ebuf[MAXNAME * 2 + 2]; /* EAI:ok? */
 
 			p = macvalue('_', e);
 			if (p == NULL)
@@ -1054,8 +1038,9 @@ setsender(from, e, delimptr, delimchar, internal)
 				if (host == NULL)
 					host = MyHostName;
 				(void) sm_snprintf(ebuf, sizeof(ebuf),
-						   "%.*s@%.*s", MAXNAME,
-						   realname, MAXNAME, host);
+						   "%.*s@%.*s",
+						   MAXNAME, realname, /* EAI: see above */
+						   MAXNAME, host); /* EAI: see above */
 				p = ebuf;
 			}
 			sm_syslog(LOG_NOTICE, e->e_id,
@@ -1074,6 +1059,7 @@ setsender(from, e, delimptr, delimchar, internal)
 			SuprErrs = true;
 		}
 		if (from == realname ||
+/* XXX realname must be [i] */
 		    parseaddr(from = realname,
 			      &e->e_from, RF_COPYALL|RF_SENDERADDR, ' ',
 			      NULL, e, false) == NULL)
@@ -1083,6 +1069,7 @@ setsender(from, e, delimptr, delimchar, internal)
 			SuprErrs = true;
 			expand("\201n", nbuf, sizeof(nbuf), e);
 			from = sm_rpool_strdup_x(e->e_rpool, nbuf);
+/* XXX from must be [i] */
 			if (parseaddr(from, &e->e_from, RF_COPYALL, ' ',
 				      NULL, e, false) == NULL &&
 			    parseaddr(from = "postmaster", &e->e_from,
diff --git a/contrib/sendmail/src/err.c b/contrib/sendmail/src/err.c
index 8bd02669c5e0..c76fb7714066 100644
--- a/contrib/sendmail/src/err.c
+++ b/contrib/sendmail/src/err.c
@@ -19,6 +19,9 @@ SM_RCSID("@(#)$Id: err.c,v 8.206 2013-11-22 20:51:55 ca Exp $")
 # include 
 # include 			/* for LDAP error codes */
 #endif
+#if _FFR_8BITENVADDR
+# include 
+#endif
 
 static void	putoutmsg __P((char *, bool, bool));
 static void	puterrmsg __P((char *));
@@ -375,7 +378,7 @@ usrerrenh(enhsc, fmt, va_alist)
 	char *errtxt;
 	SM_VA_LOCAL_DECL
 
-	if (enhsc == NULL || *enhsc == '\0')
+	if (SM_IS_EMPTY(enhsc))
 	{
 		if (fmt[0] == '5' || fmt[0] == '6')
 			enhsc = "5.0.0";
@@ -730,8 +733,13 @@ putoutmsg(msg, holdmsg, heldmsg)
 				     (OpMode == MD_SMTP || OpMode == MD_DAEMON)
 					? msg : errtxt);
 #if !PIPELINING
-	/* XXX can't flush here for SMTP pipelining */
-	if (msg[3] == ' ')
+	/*
+	**  Note: in case of an SMTP reply this should check
+	**  that the last line of msg is not a continuation line
+	**  but that's probably not worth the effort.
+	*/
+
+	if (ISSMTPREPLY(msg))
 		(void) sm_io_flush(OutChannel, SM_TIME_DEFAULT);
 	if (!sm_io_error(OutChannel) || DisConnected)
 		return;
@@ -893,6 +901,47 @@ extenhsc(s, delim, e)
 	return l + h;
 }
 
+#if USE_EAI
+/*
+**  SKIPADDRHOST -- skip address and host in a message
+**
+**	Parameters:
+**		s -- string with possible address and host
+**		skiphost -- skip also host?
+**
+**	Returns:
+**		0  -- no address and host
+**		>0 -- position after address (and host)
+*/
+
+int
+skipaddrhost(s, skiphost)
+	const char *s;
+	bool skiphost;
+{
+	char *str;
+	size_t len;
+
+#define SM_ADDR_DELIM "... "
+	if (s == NULL)
+		return 0;
+	str = strstr(s, SM_ADDR_DELIM);
+	if (str == NULL)
+		return 0;
+	str += sizeof(SM_ADDR_DELIM) + 1;
+	len = strlen(s);
+	if (str >= s + len)
+		return 0;
+	if (!skiphost)
+		return str - s + 1;
+
+	str = strchr(str, ' ');
+	if (str >= s + len)
+		return 0;
+	return str - s + 1;
+}
+#endif /* USE_EAI */
+
 /*
 **  FMTMSG -- format a message into buffer.
 **
@@ -921,7 +970,7 @@ fmtmsg(eb, to, num, enhsc, eno, fmt, ap)
 	const char *enhsc;
 	int eno;
 	const char *fmt;
-	SM_VA_LOCAL_DECL
+	va_list ap;
 {
 	char del;
 	int l;
@@ -1004,21 +1053,29 @@ fmtmsg(eb, to, num, enhsc, eno, fmt, ap)
 	     strncmp(num, "550", 3) == 0 ||
 	     strncmp(num, "553", 3) == 0))
 	{
+#if _FFR_8BITENVADDR
+		char xbuf[MAXNAME + 1];	/* EAI:ok */
+		int len;
+
+		len = sizeof(xbuf);
+		(void) sm_strlcpy(xbuf, to, len);
+		(void) dequote_internal_chars(xbuf, xbuf, len);
+		(void) sm_strlcpyn(eb, spaceleft, 2,
+				   shortenstring(xbuf, MAXSHORTSTR), "... ");
+		eb += strlen(eb);
+#else /* _FFR_8BITENVADDR */
 		(void) sm_strlcpyn(eb, spaceleft, 2,
 				   shortenstring(to, MAXSHORTSTR), "... ");
-		spaceleft -= strlen(eb);
-#if _FFR_EAI
-		eb += strlen(eb);
-#else
 		while (*eb != '\0')
 			*eb++ &= 0177;
-#endif
+#endif /* _FFR_8BITENVADDR */
+		spaceleft -= strlen(eb);
 	}
 
 	/* output the message */
 	(void) sm_vsnprintf(eb, spaceleft, fmt, ap);
 	spaceleft -= strlen(eb);
-#if _FFR_EAI
+#if USE_EAI
 	eb += strlen(eb);
 #else
 	while (*eb != '\0')
diff --git a/contrib/sendmail/src/headers.c b/contrib/sendmail/src/headers.c
index da84a8009328..a7896f420d97 100644
--- a/contrib/sendmail/src/headers.c
+++ b/contrib/sendmail/src/headers.c
@@ -13,6 +13,7 @@
 
 #include 
 #include 
+#include 
 
 SM_RCSID("@(#)$Id: headers.c,v 8.320 2013-11-22 20:51:55 ca Exp $")
 
@@ -306,15 +307,20 @@ dochompheader(line, pflag, hdrp, e)
 		if (rs != NULL)
 		{
 			int l, k;
-			char qval[MAXNAME];
+			char qval[MAXNAME_I];
+			XLENDECL
 
 			l = 0;
+			XLEN('"');
 			qval[l++] = '"';
 
 			/* - 3 to avoid problems with " at the end */
-			/* should be sizeof(qval), not MAXNAME */
-			for (k = 0; fvalue[k] != '\0' && l < MAXNAME - 3; k++)
+			for (k = 0;
+			     fvalue[k] != '\0' && l < sizeof(qval) - 3
+			     && xlen < MAXNAME - 3;
+			     k++)
 			{
+				XLEN(fvalue[k]);
 				switch (fvalue[k])
 				{
 				  /* XXX other control chars? */
@@ -326,6 +332,7 @@ dochompheader(line, pflag, hdrp, e)
 					qval[l++] = ' ';
 					break;
 				  case '"':
+					XLEN('\\');
 					qval[l++] = '\\';
 					/* FALLTHROUGH */
 				  default:
@@ -333,15 +340,18 @@ dochompheader(line, pflag, hdrp, e)
 					break;
 				}
 			}
+			/* just for "completeness": xlen not used afterwards */
+			XLEN('"');
 			qval[l++] = '"';
 			qval[l] = '\0';
 			k += strlen(fvalue + k);
-			if (k >= MAXNAME)
+			if (k >= sizeof(qval))
 			{
 				if (LogLevel > 9)
 					sm_syslog(LOG_WARNING, e->e_id,
 						  "Warning: truncated header '%s' before check with '%s' len=%d max=%d",
-						  fname, rs, k, MAXNAME - 1);
+						  fname, rs, k,
+						  (int) (sizeof(qval) - 1));
 			}
 			macdefine(&e->e_macro, A_TEMP,
 				macid("{currHeader}"), qval);
@@ -375,7 +385,7 @@ dochompheader(line, pflag, hdrp, e)
 	if (!bitset(EF_RESENT, e->e_flags))
 		p += 7;
 	if (!bitset(pflag, CHHDR_DEF) && !headeronly &&
-	    !bitset(EF_QUEUERUN, e->e_flags) && sm_strcasecmp(fname, p) == 0)
+	    !bitset(EF_QUEUERUN, e->e_flags) && SM_STRCASEEQ(fname, p))
 	{
 		if (e->e_from.q_paddr != NULL &&
 		    e->e_from.q_mailer != NULL &&
@@ -394,7 +404,7 @@ dochompheader(line, pflag, hdrp, e)
 	/* delete default value for this header */
 	for (hp = hdrp; (h = *hp) != NULL; hp = &h->h_link)
 	{
-		if (sm_strcasecmp(fname, h->h_field) == 0 &&
+		if (SM_STRCASEEQ(fname, h->h_field) &&
 		    !bitset(H_USER, h->h_flags) &&
 		    !bitset(H_FORCE, h->h_flags))
 		{
@@ -434,9 +444,12 @@ dochompheader(line, pflag, hdrp, e)
 	}
 
 	/* create a new node */
-	h = (HDR *) sm_rpool_malloc_x(e->e_rpool, sizeof(*h));
-	h->h_field = sm_rpool_strdup_x(e->e_rpool, fname);
-	h->h_value = sm_rpool_strdup_x(e->e_rpool, fvalue);
+	h = (HDR *) sm_rpool_malloc_tagged_x(e->e_rpool, sizeof(*h), "header",
+			pflag, bitset(pflag, CHHDR_DEF) ? 0 : 1);
+	h->h_field = sm_rpool_strdup_tagged_x(e->e_rpool, fname, "h_field",
+			pflag, bitset(pflag, CHHDR_DEF) ? 0 : 1);
+	h->h_value = sm_rpool_strdup_tagged_x(e->e_rpool, fvalue, "h_value",
+			pflag, bitset(pflag, CHHDR_DEF) ? 0 : 1);
 	h->h_link = NULL;
 	memmove((char *) h->h_mflags, (char *) mopts, sizeof(mopts));
 	h->h_macro = mid;
@@ -505,12 +518,12 @@ chompheader(line, pflag, hdrp, e)
 	/* quote this if user (not config file) input */
 	if (bitset(pflag, CHHDR_USER))
 	{
-		char xbuf[MAXLINE];
+		char xbuf[MAXLINE]; /* EAI:ok; actual buffer might be greater */
 		char *xbp = NULL;
 		int xbufs;
 
 		xbufs = sizeof(xbuf);
-		xbp = quote_internal_chars(line, xbuf, &xbufs);
+		xbp = quote_internal_chars(line, xbuf, &xbufs, NULL);
 		if (tTd(31, 7))
 		{
 			sm_dprintf("chompheader: quoted: ");
@@ -626,7 +639,7 @@ addheader(field, value, flags, e, space)
 	/* find current place in list -- keep back pointer? */
 	for (hp = hdrlist; (h = *hp) != NULL; hp = &h->h_link)
 	{
-		if (sm_strcasecmp(field, h->h_field) == 0)
+		if (SM_STRCASEEQ(field, h->h_field))
 			break;
 	}
 
@@ -728,7 +741,7 @@ hvalue(field, header)
 	for (h = header; h != NULL; h = h->h_link)
 	{
 		if (!bitset(H_DEFAULT, h->h_flags) &&
-		    sm_strcasecmp(h->h_field, field) == 0)
+		    SM_STRCASEEQ(h->h_field, field))
 		{
 			char *s;
 
@@ -921,10 +934,10 @@ eatheader(e, full, log)
 		p = "resent-message-id";
 		if (!bitset(EF_RESENT, e->e_flags))
 			p += 7;
-		if (sm_strcasecmp(h->h_field, p) == 0)
+		if (SM_STRCASEEQ(h->h_field, p))
 		{
 			e->e_msgid = h->h_value;
-			while (isascii(*e->e_msgid) && isspace(*e->e_msgid))
+			while (SM_ISSPACE(*e->e_msgid))
 				e->e_msgid++;
 			macdefine(&e->e_macro, A_PERM, macid("{msg_id}"),
 				  e->e_msgid);
@@ -979,9 +992,9 @@ eatheader(e, full, log)
 		/* Check if multipart/report */
 		if (pvp != NULL && pvp[0] != NULL &&
 		    pvp[1] != NULL && pvp[2] != NULL &&
-		    sm_strcasecmp(*pvp++, "multipart") == 0 &&
+		    SM_STRCASEEQ(*pvp++, "multipart") &&
 		    strcmp(*pvp++, "/") == 0 &&
-		    sm_strcasecmp(*pvp++, "report") == 0)
+		    SM_STRCASEEQ(*pvp++, "report"))
 		{
 			/* Look for report-type=delivery-status */
 			while (*pvp != NULL)
@@ -995,7 +1008,7 @@ eatheader(e, full, log)
 					break;
 
 				/* look for report-type */
-				if (sm_strcasecmp(*pvp++, "report-type") != 0)
+				if (!SM_STRCASEEQ(*pvp++, "report-type"))
 					continue;
 
 				/* skip equal */
@@ -1004,8 +1017,7 @@ eatheader(e, full, log)
 
 				/* check value */
 				if (*++pvp != NULL &&
-				    sm_strcasecmp(*pvp,
-						  "delivery-status") == 0)
+				    SM_STRCASEEQ(*pvp, "delivery-status"))
 					e->e_timeoutclass = TOC_DSN;
 
 				/* found report-type, no need to continue */
@@ -1019,11 +1031,11 @@ eatheader(e, full, log)
 	if (p != NULL)
 	{
 		/* (this should be in the configuration file) */
-		if (sm_strcasecmp(p, "urgent") == 0)
+		if (SM_STRCASEEQ(p, "urgent"))
 			e->e_timeoutclass = TOC_URGENT;
-		else if (sm_strcasecmp(p, "normal") == 0)
+		else if (SM_STRCASEEQ(p, "normal"))
 			e->e_timeoutclass = TOC_NORMAL;
-		else if (sm_strcasecmp(p, "non-urgent") == 0)
+		else if (SM_STRCASEEQ(p, "non-urgent"))
 			e->e_timeoutclass = TOC_NONURGENT;
 		else if (bitset(EF_RESPONSE, e->e_flags))
 			e->e_timeoutclass = TOC_DSN;
@@ -1040,7 +1052,7 @@ eatheader(e, full, log)
 
 	/* check to see if this is a MIME message */
 	if ((e->e_bodytype != NULL &&
-	     sm_strcasecmp(e->e_bodytype, "8BITMIME") == 0) ||
+	     SM_STRCASEEQ(e->e_bodytype, "8bitmime")) ||
 	    hvalue("MIME-Version", e->e_header) != NULL)
 	{
 		e->e_flags |= EF_IS_MIME;
@@ -1110,6 +1122,35 @@ eatheader(e, full, log)
 **		none
 */
 
+
+#define XBUFLEN MAXNAME
+#if (SYSLOG_BUFSIZE) >= 256
+# ifndef MSGIDLOGLEN
+#  define MSGIDLOGLEN 100
+#  define FIRSTLOGLEN 850
+# else
+#  if MSGIDLOGLEN < 100
+#    ERROR "MSGIDLOGLEN too short"
+#  endif
+/* XREF: this is "sizeof(sbuf)", see above */
+#  if MSGIDLOGLEN >= MAXLINE / 2
+#    ERROR "MSGIDLOGLEN too long"
+#  endif
+
+/* 850 - 100 for original MSGIDLOGLEN */
+#  define FIRSTLOGLEN (750 + MSGIDLOGLEN)
+
+/* check that total length is ok */
+#  if FIRSTLOGLEN + 200 >= MAXLINE
+#    ERROR "MSGIDLOGLEN too long"
+#  endif
+#  if MSGIDLOGLEN > MAXNAME
+#    undef XBUFLEN
+#    define XBUFLEN MSGIDLOGLEN
+#  endif
+# endif
+#endif /* (SYSLOG_BUFSIZE) >= 256 */
+
 void
 logsender(e, msgid)
 	register ENVELOPE *e;
@@ -1118,8 +1159,12 @@ logsender(e, msgid)
 	char *name;
 	register char *sbp;
 	register char *p;
-	char hbuf[MAXNAME + 1];
-	char sbuf[MAXLINE + 1]; /* XREF: see below MSGIDLOGLEN */
+	char hbuf[MAXNAME + 1];	/* EAI:ok; restricted to short size */
+	char sbuf[MAXLINE + 1]; /* EAI:ok; XREF: see also MSGIDLOGLEN */
+#if _FFR_8BITENVADDR
+	char xbuf[XBUFLEN + 1];	/* EAI:ok */
+#endif
+	char *xstr;
 
 	if (bitset(EF_RESPONSE, e->e_flags))
 		name = "[RESPONSE]";
@@ -1145,34 +1190,26 @@ logsender(e, msgid)
 
 #if (SYSLOG_BUFSIZE) >= 256
 	sbp = sbuf;
+	if (NULL != e->e_from.q_paddr)
+	{
+		xstr = e->e_from.q_paddr;
+# if _FFR_8BITENVADDR
+		(void) dequote_internal_chars(e->e_from.q_paddr, xbuf, sizeof(xbuf));
+		xstr = xbuf;
+# endif
+	}
+	else
+		xstr = "";
 	(void) sm_snprintf(sbp, SPACELEFT(sbuf, sbp),
-		"from=%.200s, size=%ld, class=%d, nrcpts=%d",
-		e->e_from.q_paddr == NULL ? "" : e->e_from.q_paddr,
+		"from=%.200s, size=%ld, class=%d, nrcpts=%d", xstr,
 		PRT_NONNEGL(e->e_msgsize), e->e_class, e->e_nrcpts);
 	sbp += strlen(sbp);
 	if (msgid != NULL)
 	{
-
-#ifndef MSGIDLOGLEN
-# define MSGIDLOGLEN 100
-# define FIRSTLOGLEN 850
-# else
-#  if MSGIDLOGLEN < 100
-    ERROR MSGIDLOGLEN too short
-#  endif
-/* XREF: this is "sizeof(sbuf)", see above */
-#  if MSGIDLOGLEN >= MAXLINE / 2
-    ERROR MSGIDLOGLEN too long
-#  endif
-
-/* 850 - 100 for original MSGIDLOGLEN */
-#  define FIRSTLOGLEN (750 + MSGIDLOGLEN)
-
-/* check that total length is ok */
-#  if FIRSTLOGLEN + 200 >= MAXLINE
-    ERROR MSGIDLOGLEN too long
-#  endif
-#endif
+# if _FFR_8BITENVADDR
+		(void) dequote_internal_chars(msgid, xbuf, sizeof(xbuf));
+		msgid = xbuf;
+# endif
 		(void) sm_snprintf(sbp, SPACELEFT(sbuf, sbp),
 				", msgid=%.*s", MSGIDLOGLEN, msgid);
 		sbp += strlen(sbp);
@@ -1201,7 +1238,7 @@ logsender(e, msgid)
 	LOG_MORE(sbuf, sbp);
 #  if SASL
 	p = macvalue(macid("{auth_type}"), e);
-	if (p == NULL || *p == '\0')
+	if (SM_IS_EMPTY(p))
 		p = "NONE";
 	(void) sm_snprintf(sbp, SPACELEFT(sbuf, sbp), ", auth=%.20s", p);
 	sbp += strlen(sbp);
@@ -1220,9 +1257,15 @@ logsender(e, msgid)
 		  "size=%ld, class=%ld, nrcpts=%d",
 		  PRT_NONNEGL(e->e_msgsize), e->e_class, e->e_nrcpts);
 	if (msgid != NULL)
+	{
+# if _FFR_8BITENVADDR
+		(void) dequote_internal_chars(msgid, xbuf, sizeof(xbuf));
+		msgid = xbuf;
+# endif
 		sm_syslog(LOG_INFO, e->e_id,
 			  "msgid=%s",
 			  shortenstring(msgid, 83));
+	}
 	sbp = sbuf;
 	*sbp = '\0';
 	if (e->e_bodytype != NULL)
@@ -1264,7 +1307,7 @@ priencode(p)
 
 	for (i = 0; i < NumPriorities; i++)
 	{
-		if (sm_strcasecmp(p, Priorities[i].pri_name) == 0)
+		if (SM_STRCASEEQ(p, Priorities[i].pri_name))
 			return Priorities[i].pri_val;
 	}
 
@@ -1291,7 +1334,7 @@ priencode(p)
 **	the original syntax.
 **
 **	Parameters:
-**		addr -- the address to be cracked.
+**		addr -- the address to be cracked. [A]
 **		e -- the current envelope.
 **
 **	Returns:
@@ -1301,11 +1344,16 @@ priencode(p)
 **		none.
 **
 **	Warning:
-**		The return value is saved in local storage and should
+**		The return value is saved in static storage and should
 **		be copied if it is to be reused.
 */
 
-#define SM_HAVE_ROOM		((bp < buflim) && (buflim <= bufend))
+#define SM_HAVE_ROOMB		((bp < buflim) && (buflim <= bufend))
+#if USE_EAI
+# define SM_HAVE_ROOM		((xlen < MAXNAME) && SM_HAVE_ROOMB)
+#else
+# define SM_HAVE_ROOM		SM_HAVE_ROOMB
+#endif
 
 /*
 **  Append a character to bp if we have room.
@@ -1315,6 +1363,7 @@ priencode(p)
 #define SM_APPEND_CHAR(c)					\
 	do							\
 	{							\
+		XLEN(c);					\
 		if (SM_HAVE_ROOM)				\
 			*bp++ = (c);				\
 		else						\
@@ -1322,7 +1371,7 @@ priencode(p)
 	} while (0)
 
 #if MAXNAME < 10
-ERROR MAXNAME must be at least 10
+# ERROR "MAXNAME must be at least 10"
 #endif
 
 char *
@@ -1349,7 +1398,8 @@ crackaddr(addr, e)
 	char *bufhead;
 	char *addrhead;
 	char *bufend;
-	static char buf[MAXNAME + 1];
+	static char buf[MAXNAME_I + 1];	/* XXX: EAI? */
+	XLENDECL
 
 	if (tTd(33, 1))
 		sm_dprintf("crackaddr(%s)\n", addr);
@@ -1730,8 +1780,11 @@ putheader(mci, hdr, e, flags)
 		}
 
 		/* Skip empty headers */
-		if (h->h_value == NULL)
+		if (p == NULL)
 			continue;
+#if _FFR_8BITENVADDR
+		(void) dequote_internal_chars(p, buf, sizeof(buf));
+#endif
 
 		/* heuristic shortening of MIME fields to avoid MUA overflows */
 		if (MaxMimeFieldLength > 0 &&
@@ -1759,7 +1812,7 @@ putheader(mci, hdr, e, flags)
 		{
 			size_t len;
 
-			len = strlen(h->h_value);
+			len = strlen(p);
 			if (len > (size_t) MaxMimeHeaderLength)
 			{
 				h->h_value[MaxMimeHeaderLength - 1] = '\0';
@@ -2055,6 +2108,16 @@ commaize(h, p, oldstyle, mci, e, putflags)
 	if (bitnset(M_7BITHDRS, mci->mci_mailer->m_flags))
 		putflags |= PXLF_STRIP8BIT;
 
+#if _FFR_MTA_MODE
+	/* activate this per mailer? */
+	if (bitset(H_FROM, h->h_flags) && bitset(H_ASIS, h->h_flags))
+	{
+		(void) sm_snprintf(obuf, sizeof(obuf), "%.200s:%s", h->h_field,
+				h->h_value);
+		return putxline(obuf, strlen(obuf), mci, putflags);
+	}
+#endif
+
 	obp = obuf;
 	(void) sm_snprintf(obp, SPACELEFT(obuf, obp), "%.200s:", h->h_field);
 	/* opos = strlen(obp); instead of the next 3 lines? */
@@ -2299,7 +2362,7 @@ fix_mime_header(h, e)
 	size_t len = 0;
 	size_t retlen = 0;
 
-	if (begin == NULL || *begin == '\0')
+	if (SM_IS_EMPTY(begin))
 		return 0;
 
 	/* Split on each ';' */
diff --git a/contrib/sendmail/src/helpfile b/contrib/sendmail/src/helpfile
index 9529b28a7fc2..d17deb384f16 100644
--- a/contrib/sendmail/src/helpfile
+++ b/contrib/sendmail/src/helpfile
@@ -67,7 +67,7 @@ starttls	STARTTLS
 starttls		Start TLS negotiation.
 verb	VERB
 verb		Go into verbose mode.  This sends 0xy responses that are
-verb		not RFC821 standard (but should be)  They are recognized
+verb		not RFC821 standard (but should be).  They are recognized
 verb		by humans and other sendmail implementations.
 vrfy	VRFY 
 vrfy		Verify an address.  If you want to see what it aliases
@@ -115,8 +115,13 @@ dsn		ORCPT	Original recipient.
 -bt	$$m		 :print the value of macro $$m.
 -bt	$$=c		 :print the contents of class $$=c.
 -bt	/mx host	 :returns the MX records for `host'.
+-bt	/gethostbyname host [family]  :calls gethostbyname() for `host'.
+-bt	/dnslookup host [qtype] [flags]	 :Does a qtype DNS lookup for `host'.
 -bt	/parse address	 :parse address, returning the value of crackaddr, and
 -bt			  the parsed address.
+-bt	/sender address	 :parse sender address, returning the value of
+-bt			  setsender.
+-bt	/expand string	 :expands string, returning the value of expand.
 -bt	/try mailer addr :rewrite address into the form it will have when
 -bt			  presented to the indicated mailer.
 -bt	/tryflags flags	 :set flags used by parsing.  The flags can be `H' for
@@ -125,6 +130,7 @@ dsn		ORCPT	Original recipient.
 -bt			  flags for header recipients.
 -bt	/canon hostname	 :try to canonify hostname.
 -bt	/map mapname key :look up `key' in the indicated `mapname'.
+-bt	/header header	 :parses header, returning header flags
 -bt	/quit		 :quit address test mode.
 -bt	rules addr	 :run the indicated address through the named rules.
 -bt			  Rules can be a comma separated list of rules.
diff --git a/contrib/sendmail/src/macro.c b/contrib/sendmail/src/macro.c
index 83438563151b..23012f4a414f 100644
--- a/contrib/sendmail/src/macro.c
+++ b/contrib/sendmail/src/macro.c
@@ -159,23 +159,17 @@ initmacros(e)
 
 /*
 **  EXPAND/DOEXPAND -- macro expand a string using $x escapes.
-**
-**	After expansion, the expansion will be in external form (that is,
-**	there will be no sendmail metacharacters and METAQUOTEs will have
-**	been stripped out).
+**	(including conditionals, e.g., $?x Y $| N $.)
 **
 **	Parameters:
-**		s -- the string to expand.
-**		buf -- the place to put the expansion.
+**		s -- the string to expand. [i]
+**		buf -- the place to put the expansion. [i]
 **		bufsize -- the size of the buffer.
 **		explevel -- the depth of expansion (doexpand only)
 **		e -- envelope in which to work.
 **
 **	Returns:
 **		none.
-**
-**	Side Effects:
-**		none.
 */
 
 static void doexpand __P(( char *, char *, size_t, int, ENVELOPE *));
@@ -422,9 +416,9 @@ mactabclear(mac)
 void
 #if SM_HEAP_CHECK
 macdefine_tagged(mac, vclass, id, value, file, line, grp)
-#else /* SM_HEAP_CHECK */
+#else
 macdefine(mac, vclass, id, value)
-#endif /* SM_HEAP_CHECK */
+#endif
 	MACROS_T *mac;
 	ARGCLASS_T vclass;
 	int id;
@@ -433,7 +427,7 @@ macdefine(mac, vclass, id, value)
 	char *file;
 	int line;
 	int grp;
-#endif /* SM_HEAP_CHECK */
+#endif
 {
 	char *newvalue;
 
@@ -447,6 +441,10 @@ macdefine(mac, vclass, id, value)
 		xputs(sm_debug_file(), value);
 		sm_dprintf(")\n");
 	}
+#if USE_EAI && 0
+	if (('j' == id || 'm' == id) && !addr_is_ascii(value))
+		return an error/warning to caller and let them handle it.
+#endif
 
 	if (mac->mac_rpool == NULL)
 	{
diff --git a/contrib/sendmail/src/mailq.1 b/contrib/sendmail/src/mailq.1
index 0a12d3775b56..6b5069b836b9 100644
--- a/contrib/sendmail/src/mailq.1
+++ b/contrib/sendmail/src/mailq.1
@@ -24,16 +24,16 @@ mailq
 .B Mailq
 prints a summary of the mail messages queued for future delivery.
 .PP
-The first line printed for each message 
-shows the internal identifier used on this host 
-for the message with a possible status character, 
+The first line printed for each message
+shows the internal identifier used on this host
+for the message with a possible status character,
 the size of the message in bytes,
-the date and time the message was accepted into the queue, 
-and the envelope sender of the message.  
-The second line shows the error message that caused this message 
-to be retained in the queue; 
-it will not be present if the message is being processed 
-for the first time.  
+the date and time the message was accepted into the queue,
+and the envelope sender of the message.
+The second line shows the error message that caused this message
+to be retained in the queue;
+it will not be present if the message is being processed
+for the first time.
 The status characters are either
 .B *
 to indicate the job is being processed;
@@ -41,7 +41,7 @@ to indicate the job is being processed;
 to indicate that the load is too high to process the job; and
 .B -
 to indicate that the job is too young to process.
-The following lines show message recipients, 
+The following lines show message recipients,
 one per line.
 .PP
 .B Mailq
@@ -91,15 +91,15 @@ as a substring of the sender or not when
 is specified.
 .TP
 .B \-v
-Print verbose information.  
-This adds the priority of the message and 
-a single character indicator (``+'' or blank) 
-indicating whether a warning message has been sent 
+Print verbose information.
+This adds the priority of the message and
+a single character indicator (``+'' or blank)
+indicating whether a warning message has been sent
 on the first line of the message.
 Additionally, extra lines may be intermixed with the recipients
-indicating the ``controlling user'' information; 
-this shows who will own any programs that are executed 
-on behalf of this message 
+indicating the ``controlling user'' information;
+this shows who will own any programs that are executed
+on behalf of this message
 and the name of the alias this command expanded from, if any.
 Moreover, status messages for each recipient are printed
 if available.
@@ -131,5 +131,5 @@ sendmail(8)
 .SH HISTORY
 The
 .B mailq
-command appeared in 
+command appeared in
 4.0BSD.
diff --git a/contrib/sendmail/src/main.c b/contrib/sendmail/src/main.c
index a79733448039..d1fd89954d48 100644
--- a/contrib/sendmail/src/main.c
+++ b/contrib/sendmail/src/main.c
@@ -17,6 +17,12 @@
 #include 
 #include 
 #include 
+#if _FFR_8BITENVADDR
+# include 
+#endif
+#if _FFR_DMTRIGGER
+# include 
+#endif
 
 #ifndef lint
 SM_UNUSED(static char copyright[]) =
@@ -159,10 +165,15 @@ main(argc, argv, envp)
 	char **argv;
 	char **envp;
 {
-	register char *p;
+	char *p;
 	char **av;
 	extern char Version[];
-	char *ep, *from;
+	char *ep, *fromaddr;
+#if USE_EAI
+	char *fromaddr_x;
+#else
+# define fromaddr_x fromaddr
+#endif
 	STAB *st;
 	register int i;
 	int j;
@@ -195,7 +206,7 @@ main(argc, argv, envp)
 	struct stat traf_st;		/* for TrafficLog FIFO check */
 	char buf[MAXLINE];
 	char jbuf[MAXHOSTNAMELEN];	/* holds MyHostName */
-	static char rnamebuf[MAXNAME];	/* holds RealUserName */
+	static char rnamebuf[MAXNAME];	/* holds RealUserName */ /* EAI:ok */
 	char *emptyenviron[1];
 #if STARTTLS
 	bool tls_ok;
@@ -380,13 +391,13 @@ main(argc, argv, envp)
 		OpMode = MD_PURGESTAT;
 
 #if defined(__osf__) || defined(_AIX3)
-# define OPTIONS	"A:B:b:C:cD:d:e:F:f:Gh:IiL:M:mN:nO:o:p:Q:q:R:r:sTtV:vX:x"
-#endif /* defined(__osf__) || defined(_AIX3) */
+# define OPTIONS	"A:B:b:C:cD:d:e:F:f:Gh:IiL:M:mN:nO:o:p:Q:q:R:r:sTtUV:vX:x"
+#endif
 #if defined(sony_news)
-# define OPTIONS	"A:B:b:C:cD:d:E:e:F:f:Gh:IiJ:L:M:mN:nO:o:p:Q:q:R:r:sTtV:vX:"
-#endif /* defined(sony_news) */
+# define OPTIONS	"A:B:b:C:cD:d:E:e:F:f:Gh:IiJ:L:M:mN:nO:o:p:Q:q:R:r:sTtUV:vX:"
+#endif
 #ifndef OPTIONS
-# define OPTIONS	"A:B:b:C:cD:d:e:F:f:Gh:IiL:M:mN:nO:o:p:Q:q:R:r:sTtV:vX:"
+# define OPTIONS	"A:B:b:C:cD:d:e:F:f:Gh:IiL:M:mN:nO:o:p:Q:q:R:r:sTtUV:vX:"
 #endif
 
 	/* Set to 0 to allow -b; need to check optarg before using it! */
@@ -415,6 +426,9 @@ main(argc, argv, envp)
 				OpMode = j;
 				break;
 
+			  case MD_SHOWCONFIG:
+				showcfopts();
+				return EX_OK;
 #if _FFR_LOCAL_DAEMON
 			  case MD_LOCAL:
 				OpMode = MD_DAEMON;
@@ -477,7 +491,7 @@ main(argc, argv, envp)
 				return EX_USAGE;
 			}
 			j = SM_MIN(strlen(optarg), 32) + 1;
-			sysloglabel = xalloc(j);
+			sysloglabel = sm_malloc_tagged_x(j, "sysloglabel", 0, 0);
 			(void) sm_strlcpy(sysloglabel, optarg, j);
 			SyslogPrefixLen = PIDLEN + (MAXQFNAME - 3) +
 					  SL_FUDGE + j;
@@ -589,8 +603,9 @@ main(argc, argv, envp)
 	j = 0;
 	for (av = argv; *av != NULL; )
 		j += strlen(*av++) + 1;
-	SaveArgv = (char **) xalloc(sizeof(char *) * (argc + 1));
-	CommandLineArgs = xalloc(j);
+	SaveArgv = (char **) sm_malloc_tagged_x(sizeof(char *) * (argc + 1),
+					"argv", 0, 0);
+	CommandLineArgs = sm_malloc_tagged_x(j, "cliargs", 0, 0);
 	p = CommandLineArgs;
 	for (av = argv, i = 0; *av != NULL; )
 	{
@@ -715,7 +730,7 @@ main(argc, argv, envp)
 #endif /* NAMED_BIND */
 
 	errno = 0;
-	from = NULL;
+	fromaddr = NULL;
 
 	/* initialize some macros, etc. */
 	init_vendor_macros(&BlankEnvelope);
@@ -731,6 +746,14 @@ main(argc, argv, envp)
 
 		if (tTd(0, 4))
 			sm_dprintf("Canonical name: %s\n", jbuf);
+#if USE_EAI
+		if (!addr_is_ascii(jbuf))
+		{
+			usrerr("hostname %s must be ASCII", jbuf);
+			finis(false, true, EX_CONFIG);
+			/* NOTREACHED */
+		}
+#endif
 		macdefine(&BlankEnvelope.e_macro, A_TEMP, 'w', jbuf);
 		macdefine(&BlankEnvelope.e_macro, A_TEMP, 'j', jbuf);
 		setclass('w', jbuf);
@@ -746,14 +769,16 @@ main(argc, argv, envp)
 			if (tTd(0, 22))
 				sm_dprintf("uname failed (%s)\n",
 					   sm_errstring(errno));
-			makelower(jbuf);
 			p = jbuf;
+			p = makelower_a(&p, NULL);
 		}
 		if (tTd(0, 4))
 			sm_dprintf(" UUCP nodename: %s\n", p);
 		macdefine(&BlankEnvelope.e_macro, A_TEMP, 'k', p);
 		setclass('k', p);
 		setclass('w', p);
+		if (p != utsname.nodename && p != jbuf)
+			SM_FREE(p);
 	}
 	if (hp != NULL)
 	{
@@ -865,20 +890,20 @@ main(argc, argv, envp)
 			/* already done */
 			break;
 
-		  case 'f':	/* from address */
+		  case 'f':	/* fromaddr address */
 		  case 'r':	/* obsolete -f flag */
 			CHECK_AGAINST_OPMODE(j);
-			if (from != NULL)
+			if (fromaddr != NULL)
 			{
 				usrerr("More than one \"from\" person");
 				ExitStat = EX_USAGE;
 				break;
 			}
 			if (optarg[0] == '\0')
-				from = newstr("<>");
+				fromaddr = newstr("<>");
 			else
-				from = newstr(denlstring(optarg, true, true));
-			if (strcmp(RealUserName, from) != 0)
+				fromaddr = newstr(denlstring(optarg, true, true));
+			if (strcmp(RealUserName, fromaddr) != 0)
 				warn_f_flag = j;
 			break;
 
@@ -921,18 +946,18 @@ main(argc, argv, envp)
 			DefaultNotify |= QHASNOTIFY;
 			macdefine(&BlankEnvelope.e_macro, A_TEMP,
 				macid("{dsn_notify}"), optarg);
-			if (sm_strcasecmp(optarg, "never") == 0)
+			if (SM_STRCASEEQ(optarg, "never"))
 				break;
 			for (p = optarg; p != NULL; optarg = p)
 			{
 				p = strchr(p, ',');
 				if (p != NULL)
 					*p++ = '\0';
-				if (sm_strcasecmp(optarg, "success") == 0)
+				if (SM_STRCASEEQ(optarg, "success"))
 					DefaultNotify |= QPINGONSUCCESS;
-				else if (sm_strcasecmp(optarg, "failure") == 0)
+				else if (SM_STRCASEEQ(optarg, "failure"))
 					DefaultNotify |= QPINGONFAILURE;
-				else if (sm_strcasecmp(optarg, "delay") == 0)
+				else if (SM_STRCASEEQ(optarg, "delay"))
 					DefaultNotify |= QPINGONDELAY;
 				else
 				{
@@ -1116,9 +1141,9 @@ main(argc, argv, envp)
 				break;
 			}
 			BlankEnvelope.e_flags |= EF_RET_PARAM;
-			if (sm_strcasecmp(optarg, "hdrs") == 0)
+			if (SM_STRCASEEQ(optarg, "hdrs"))
 				BlankEnvelope.e_flags |= EF_NO_BODY_RETN;
-			else if (sm_strcasecmp(optarg, "full") != 0)
+			else if (!SM_STRCASEEQ(optarg, "full"))
 			{
 				usrerr("Invalid -R value");
 				ExitStat = EX_USAGE;
@@ -1132,6 +1157,13 @@ main(argc, argv, envp)
 			GrabTo = true;
 			break;
 
+#if USE_EAI
+		  case 'U':
+			CHECK_AGAINST_OPMODE(j);
+			BlankEnvelope.e_smtputf8 = true;
+			break;
+#endif
+
 		  case 'V':	/* DSN ENVID: set "original" envelope id */
 			CHECK_AGAINST_OPMODE(j);
 			if (!xtextok(optarg))
@@ -1344,6 +1376,14 @@ main(argc, argv, envp)
 	    OpMode == MD_DAEMON || OpMode == MD_FGDAEMON)
 		makeworkgroups();
 
+#if USE_EAI
+	if (!SMTPUTF8 && MainEnvelope.e_smtputf8)
+	{
+		usrerr("-U requires SMTPUTF8");
+		finis(false, true, EX_USAGE);
+	}
+#endif
+
 	/* set up the basic signal handlers */
 	if (sm_signal(SIGINT, SIG_IGN) != SIG_IGN)
 		(void) sm_signal(SIGINT, intsig);
@@ -1859,7 +1899,7 @@ main(argc, argv, envp)
 
 	/* MIME message/xxx subtypes that can be treated as messages */
 	setclass('s', "rfc822");
-#if _FFR_EAI
+#if USE_EAI
 	setclass('s', "global");
 #endif
 
@@ -2314,6 +2354,15 @@ main(argc, argv, envp)
 	}
 #endif /* SASL */
 
+#if _FFR_DMTRIGGER
+	if (OpMode == MD_DAEMON && SM_TRIGGER == BlankEnvelope.e_sendmode)
+	{
+		i = sm_notify_init(0);
+		if (i != 0)
+			syserr("sm_notify_init() failed=%d", i);
+	}
+#endif
+
 	if (OpMode == MD_SMTP)
 	{
 		proc_list_add(CurrentPid, "Sendmail SMTP Agent",
@@ -2325,9 +2374,9 @@ main(argc, argv, envp)
 
 	/*
 	**  If a daemon, wait for a request.
-	**	getrequests will always return in a child.
-	**	If we should also be processing the queue, start
-	**		doing it in background.
+	**	getrequests() will return in a child (unless -d93.100 is used).
+	**	If we should also be processing the queue,
+	**		start doing it in background.
 	**	We check for any errors that might have happened
 	**		during startup.
 	*/
@@ -2405,6 +2454,11 @@ main(argc, argv, envp)
 		(void) sm_releasesignal(SIGHUP);
 		(void) sm_signal(SIGTERM, sigterm);
 
+#if _FFR_DMTRIGGER
+		if (SM_TRIGGER == BlankEnvelope.e_sendmode)
+			qm();
+#endif
+
 		if (QueueIntvl > 0)
 		{
 #if _FFR_RUNPQG
@@ -2758,13 +2812,29 @@ main(argc, argv, envp)
 	initsys(&MainEnvelope);
 	macdefine(&MainEnvelope.e_macro, A_PERM, macid("{ntries}"), "0");
 	macdefine(&MainEnvelope.e_macro, A_PERM, macid("{nrcpts}"), "0");
-	setsender(from, &MainEnvelope, NULL, '\0', false);
+#if USE_EAI
+	fromaddr_x = fromaddr;	/* for logging - see below -- just in case */
+	if (fromaddr != NULL && (MainEnvelope.e_smtputf8 ||
+			(MainEnvelope.e_smtputf8 = !asciistr(fromaddr))))
+	{
+		/* not very efficient: asciistr() may be called above already */
+		if (!SMTPUTF8 && !asciistr(fromaddr))
+		{
+			usrerr("non-ASCII sender address %s requires SMTPUTF8",
+				fromaddr);
+			finis(false, true, EX_USAGE);
+		}
+		j = 0;
+		fromaddr = quote_internal_chars(fromaddr, NULL, &j, NULL);
+	}
+#endif
+	setsender(fromaddr, &MainEnvelope, NULL, '\0', false);
 	if (warn_f_flag != '\0' && !wordinclass(RealUserName, 't') &&
 	    (!bitnset(M_LOCALMAILER, MainEnvelope.e_from.q_mailer->m_flags) ||
 	     strcmp(MainEnvelope.e_from.q_user, RealUserName) != 0))
 	{
 		auth_warning(&MainEnvelope, "%s set sender to %s using -%c",
-			     RealUserName, from, warn_f_flag);
+			     RealUserName, fromaddr_x, warn_f_flag);
 #if SASL
 		auth = false;
 #endif
@@ -3023,6 +3093,10 @@ finis(drop, cleanup, exitstat)
 		if (fp != NULL)
 			dump_ch(fp);
 	}
+#endif /* RATECTL_DEBUG || _FFR_OCC */
+#if _FFR_DMTRIGGER
+	if (OpMode == MD_DAEMON && SM_TRIGGER == BlankEnvelope.e_sendmode)
+		sm_notify_stop(DaemonPid == getpid(), 0);
 #endif
 	if (tTd(2, 1))
 	{
@@ -3124,6 +3198,10 @@ finis(drop, cleanup, exitstat)
 #endif
 		(void) setuid(RealUid);
 #if SM_HEAP_CHECK
+# if SM_HEAP_CHECK > 1
+		/* seems this is not always free()? */
+		sm_rpool_free(CurEnv->e_rpool);
+# endif
 		/* dump the heap, if we are checking for memory leaks */
 		if (sm_debug_active(&SmHeapCheck, 2))
 			sm_heap_report(smioout,
@@ -3358,6 +3436,7 @@ disconnect(droplev, e)
 	int droplev;
 	register ENVELOPE *e;
 {
+#define LOGID(e) (((e) != NULL && (e)->e_id != NULL) ? (e)->e_id : NOQID)
 	int fd;
 
 	if (tTd(52, 1))
@@ -3371,7 +3450,7 @@ disconnect(droplev, e)
 		return;
 	}
 	if (LogLevel > 93)
-		sm_syslog(LOG_DEBUG, e->e_id,
+		sm_syslog(LOG_DEBUG, LOGID(e),
 			  "disconnect level %d",
 			  droplev);
 
@@ -3393,7 +3472,7 @@ disconnect(droplev, e)
 	}
 	if (sm_io_reopen(SmFtStdio, SM_TIME_DEFAULT, SM_PATH_DEVNULL,
 			 SM_IO_RDONLY, NULL, smioin) == NULL)
-		sm_syslog(LOG_ERR, e->e_id,
+		sm_syslog(LOG_ERR, LOGID(e),
 			  "disconnect: sm_io_reopen(\"%s\") failed: %s",
 			  SM_PATH_DEVNULL, sm_errstring(errno));
 
@@ -3421,7 +3500,7 @@ disconnect(droplev, e)
 		if (smioout->sm_magic == NULL &&
 		    sm_io_reopen(SmFtStdio, SM_TIME_DEFAULT, SM_PATH_DEVNULL,
 				 SM_IO_WRONLY, NULL, smioout) == NULL)
-			sm_syslog(LOG_ERR, e->e_id,
+			sm_syslog(LOG_ERR, LOGID(e),
 				  "disconnect: sm_io_reopen(\"%s\") failed: %s",
 				  SM_PATH_DEVNULL, sm_errstring(errno));
 #endif /* 0 */
@@ -3431,7 +3510,7 @@ disconnect(droplev, e)
 		fd = open(SM_PATH_DEVNULL, O_WRONLY, 0666);
 		if (fd == -1)
 		{
-			sm_syslog(LOG_ERR, e->e_id,
+			sm_syslog(LOG_ERR, LOGID(e),
 				  "disconnect: open(\"%s\") failed: %s",
 				  SM_PATH_DEVNULL, sm_errstring(errno));
 		}
@@ -3456,7 +3535,7 @@ disconnect(droplev, e)
 #endif
 
 	if (LogLevel > 71)
-		sm_syslog(LOG_DEBUG, e->e_id, "in background, pid=%d",
+		sm_syslog(LOG_DEBUG, LOGID(e), "in background, pid=%d",
 			  (int) CurrentPid);
 
 	errno = 0;
@@ -3554,37 +3633,35 @@ auth_warning(e, msg, va_alist)
 {
 	char buf[MAXLINE];
 	SM_VA_LOCAL_DECL
+	char *p;
+	static char hostbuf[48];
 
-	if (bitset(PRIV_AUTHWARNINGS, PrivacyFlags))
+	if (!bitset(PRIV_AUTHWARNINGS, PrivacyFlags))
+		return;
+
+	if (hostbuf[0] == '\0')
 	{
-		register char *p;
-		static char hostbuf[48];
+		struct hostent *hp;
 
-		if (hostbuf[0] == '\0')
-		{
-			struct hostent *hp;
-
-			hp = myhostname(hostbuf, sizeof(hostbuf));
+		hp = myhostname(hostbuf, sizeof(hostbuf));
 #if NETINET6
-			if (hp != NULL)
-			{
-				freehostent(hp);
-				hp = NULL;
-			}
-#endif /* NETINET6 */
+		if (hp != NULL)
+		{
+			freehostent(hp);
+			hp = NULL;
 		}
-
-		(void) sm_strlcpyn(buf, sizeof(buf), 2, hostbuf, ": ");
-		p = &buf[strlen(buf)];
-		SM_VA_START(ap, msg);
-		(void) sm_vsnprintf(p, SPACELEFT(buf, p), msg, ap);
-		SM_VA_END(ap);
-		addheader("X-Authentication-Warning", buf, 0, e, true);
-		if (LogLevel > 3)
-			sm_syslog(LOG_INFO, e->e_id,
-				  "Authentication-Warning: %.400s",
-				  buf);
+#endif /* NETINET6 */
 	}
+
+	(void) sm_strlcpyn(buf, sizeof(buf), 2, hostbuf, ": ");
+	p = &buf[strlen(buf)];
+	SM_VA_START(ap, msg);
+	(void) sm_vsnprintf(p, SPACELEFT(buf, p), msg, ap);
+	SM_VA_END(ap);
+	addheader("X-Authentication-Warning", buf, 0, e, true);
+	if (LogLevel > 3)
+		sm_syslog(LOG_INFO, e->e_id,
+			  "Authentication-Warning: %.400s", buf);
 }
 /*
 **  GETEXTENV -- get from external environment
@@ -3644,7 +3721,7 @@ sm_setuserenv(envar, value)
 	/* XXX enforce reasonable size? */
 	i = strlen(envar) + 1;
 	l = strlen(value) + i + 1;
-	p = (char *) xalloc(l);
+	p = (char *) sm_malloc_tagged_x(l, "setuserenv", 0, 0);
 	(void) sm_strlcpyn(p, l, 3, envar, "=", value);
 
 	while (*evp != NULL && strncmp(*evp, p, i) != 0)
@@ -4045,28 +4122,29 @@ sm_printoptions(options)
 **
 **	Parameters:
 **		str -- the input line.
+**		mq -- "quote" meta chars?
 **
 **	Returns:
-**		none.
+**		meta quoting performed?
 **
 **	Side Effects:
-**		replaces \0octal in str with octal value.
+**		replaces \0octal in str with octal value,
+**		optionally (meta) quotes meta chars.
 */
 
-static bool to8bit __P((char *));
+static bool to8bit __P((char *, bool));
 
 static bool
-to8bit(str)
+to8bit(str, mq)
 	char *str;
+	bool mq;
 {
 	int c, len;
 	char *out, *in;
-	bool changed;
 
 	if (str == NULL)
 		return false;
 	in = out = str;
-	changed = false;
 	len = 0;
 	while ((c = (*str++ & 0377)) != '\0')
 	{
@@ -4085,21 +4163,21 @@ to8bit(str)
 				++str;
 				++len;
 			}
-			changed = true;
+			mq = true;
 			c = oct;
 		}
 		*out++ = c;
 	}
 	*out++ = c;
-	if (changed)
+	if (mq)
 	{
 		char *q;
 
-		q = quote_internal_chars(in, in, &len);
+		q = quote_internal_chars(in, in, &len, NULL);
 		if (q != in)
 			sm_strlcpy(in, q, len);
 	}
-	return changed;
+	return mq;
 }
 
 /*
@@ -4138,6 +4216,9 @@ testmodeline(line, e)
 	char lbuf[MAXLINE];
 	extern unsigned char TokTypeNoC[];
 	bool eightbit;
+#if _FFR_8BITENVADDR
+	int len = sizeof(exbuf);
+#endif
 
 	/* skip leading spaces */
 	while (*line == ' ')
@@ -4333,13 +4414,13 @@ testmodeline(line, e)
 					     "Usage: /[canon|map|mx|parse|try|tryflags]\n");
 			return;
 		}
-		if (sm_strcasecmp(&line[1], "quit") == 0)
+		if (SM_STRCASEEQ(&line[1], "quit"))
 		{
 			CurEnv->e_id = NULL;
 			finis(true, true, ExitStat);
 			/* NOTREACHED */
 		}
-		if (sm_strcasecmp(&line[1], "mx") == 0)
+		if (SM_STRCASEEQ(&line[1], "mx"))
 		{
 #if NAMED_BIND
 			/* look up MX records */
@@ -4371,7 +4452,7 @@ testmodeline(line, e)
 					     "No MX code compiled in\n");
 #endif /* NAMED_BIND */
 		}
-		else if (sm_strcasecmp(&line[1], "canon") == 0)
+		else if (SM_STRCASEEQ(&line[1], "canon"))
 		{
 			char host[MAXHOSTNAMELEN];
 
@@ -4393,7 +4474,7 @@ testmodeline(line, e)
 					     "getcanonname(%s) returns %s\n",
 					     p, host);
 		}
-		else if (sm_strcasecmp(&line[1], "map") == 0)
+		else if (SM_STRCASEEQ(&line[1], "map"))
 		{
 			auto int rcode = EX_OK;
 			char *av[2];
@@ -4442,7 +4523,25 @@ testmodeline(line, e)
 						     "returns %s (%d)\n", p,
 						     rcode);
 		}
-		else if (sm_strcasecmp(&line[1], "try") == 0)
+		else if (SM_STRCASEEQ(&line[1], "sender"))
+		{
+			setsender(p, CurEnv, NULL, '\0', false);
+			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+					     "addr=%s\n",
+					     e->e_from.q_user);
+		}
+		else if (SM_STRCASEEQ(&line[1], "expand"))
+		{
+			if (*p == '\0')
+			{
+				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+						     "Usage: /expand string\n");
+				return;
+			}
+			expand(p, exbuf, sizeof(exbuf), CurEnv);
+			xputs(smioout, exbuf);
+		}
+		else if (SM_STRCASEEQ(&line[1], "try"))
 		{
 			MAILER *m;
 			STAB *st;
@@ -4474,13 +4573,20 @@ testmodeline(line, e)
 							: "envelope",
 				     bitset(RF_SENDERADDR, tryflags) ? "sender"
 							: "recipient", q, p);
+#if _FFR_8BITENVADDR
+			q = quote_internal_chars(q, exbuf, &len, NULL);
+#endif
 			p = remotename(q, m, tryflags, &rcode, CurEnv);
+#if _FFR_8BITENVADDR
+			dequote_internal_chars(p, exbuf, sizeof(exbuf));
+			p = exbuf;
+#endif
 			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
 					     "Rcode = %d, addr = %s\n",
 					     rcode, p == NULL ? "" : p);
 			e->e_to = NULL;
 		}
-		else if (sm_strcasecmp(&line[1], "tryflags") == 0)
+		else if (SM_STRCASEEQ(&line[1], "tryflags"))
 		{
 			if (*p == '\0')
 			{
@@ -4520,7 +4626,7 @@ testmodeline(line, e)
 			macdefine(&e->e_macro, A_TEMP,
 				macid("{addr_type}"), exbuf);
 		}
-		else if (sm_strcasecmp(&line[1], "parse") == 0)
+		else if (SM_STRCASEEQ(&line[1], "parse"))
 		{
 			if (*p == '\0')
 			{
@@ -4528,6 +4634,9 @@ testmodeline(line, e)
 						     "Usage: /parse address\n");
 				return;
 			}
+#if _FFR_8BITENVADDR
+			p = quote_internal_chars(p, exbuf, &len, NULL);
+#endif
 			q = crackaddr(p, e);
 			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
 					     "Cracked address = ");
@@ -4538,6 +4647,7 @@ testmodeline(line, e)
 							"header" : "envelope",
 					     bitset(RF_SENDERADDR, tryflags) ?
 							"sender" : "recipient");
+/* XXX p must be [i] */
 			if (parseaddr(p, &a, tryflags, '\0', NULL, e, true)
 			    == NULL)
 				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
@@ -4555,16 +4665,16 @@ testmodeline(line, e)
 						     a.q_user);
 			e->e_to = NULL;
 		}
-		else if (sm_strcasecmp(&line[1], "header") == 0)
+		else if (SM_STRCASEEQ(&line[1], "header"))
 		{
 			unsigned long ul;
 
 			ul = chompheader(p, CHHDR_CHECK|CHHDR_USER, NULL, e);
 			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
-					     "ul = %lu\n", ul);
+					     "ul = %#0.8lx\n", ul);
 		}
 #if NETINET || NETINET6
-		else if (sm_strcasecmp(&line[1], "gethostbyname") == 0)
+		else if (SM_STRCASEEQ(&line[1], "gethostbyname"))
 		{
 			int family = AF_INET;
 
@@ -4583,7 +4693,7 @@ testmodeline(line, e)
 		}
 #endif /* NETINET || NETINET6 */
 #if DANE
-		else if (sm_strcasecmp(&line[1], "dnslookup") == 0)
+		else if (SM_STRCASEEQ(&line[1], "dnslookup"))
 		{
 			DNS_REPLY_T *r;
 			int rr_type, family;
@@ -4643,7 +4753,6 @@ testmodeline(line, e)
 		(void) sm_io_flush(smioout, SM_TIME_DEFAULT);
 		return;
 	}
-
 	for (p = line; SM_ISSPACE(*p); p++)
 		continue;
 	q = p;
@@ -4657,7 +4766,7 @@ testmodeline(line, e)
 	}
 	*p = '\0';
 	if (tTd(23, 101))
-		eightbit = to8bit(p + 1);
+		eightbit = to8bit(p + 1, tTd(23, 102));
 	if (invalidaddr(p + 1, NULL, true))
 		return;
 	do
@@ -4666,6 +4775,7 @@ testmodeline(line, e)
 		char pvpbuf[PSBUFSIZE];
 
 		pvp = prescan(++p, ',', pvpbuf, sizeof(pvpbuf), &delimptr,
+				tTd(23, 103) ? ExtTokenTab :
 			      ConfigLevel >= 9 ? TokTypeNoC : ExtTokenTab, false);
 		if (pvp == NULL)
 			continue;
diff --git a/contrib/sendmail/src/map.c b/contrib/sendmail/src/map.c
index 0931ea69235b..85589a1b4e99 100644
--- a/contrib/sendmail/src/map.c
+++ b/contrib/sendmail/src/map.c
@@ -14,6 +14,7 @@
 #include 
 
 SM_RCSID("@(#)$Id: map.c,v 8.713 2013-11-22 20:51:55 ca Exp $")
+#include 
 
 #if LDAPMAP
 # include 
@@ -22,10 +23,10 @@ SM_RCSID("@(#)$Id: map.c,v 8.713 2013-11-22 20:51:55 ca Exp $")
 #if NDBM
 # include 
 # ifdef R_FIRST
-  ERROR README:	You are running the Berkeley DB version of ndbm.h.  See
-  ERROR README:	the README file about tweaking Berkeley DB so it can
-  ERROR README:	coexist with NDBM, or delete -DNDBM from the Makefile
-  ERROR README: and use -DNEWDB instead.
+#  error "README: You are running the Berkeley DB version of ndbm.h.  See"
+#  error "README: the README file about tweaking Berkeley DB so it can"
+#  error "README: coexist with NDBM, or delete -DNDBM from the Makefile"
+#  error "README: and use -DNEWDB instead."
 # endif /* R_FIRST */
 #endif /* NDBM */
 #if NEWDB
@@ -386,8 +387,7 @@ map_rewrite(map, s, slen, av)
 	{
 		/* need to malloc additional space */
 		buflen = len;
-		if (buf != NULL)
-			sm_free(buf);
+		SM_FREE(buf);
 		buf = sm_pmalloc_x(buflen);
 	}
 
@@ -437,6 +437,24 @@ map_rewrite(map, s, slen, av)
 		(void) sm_strlcpy(bp, map->map_app, len);
 	else
 		*bp = '\0';
+#if _FFR_8BITENVADDR
+	if (!bitset(MF_KEEPXFMT, map->map_mflags))
+	{
+		int newlen;
+		char *quoted;
+
+		newlen = 0;
+		quoted = quote_internal_chars(buf, NULL, &newlen, NULL);
+		if (newlen > buflen)
+		{
+			buflen = newlen;
+			SM_FREE(buf);
+			buf = sm_pmalloc_x(buflen);
+		}
+		(void) sm_strlcpy(buf, quoted, buflen);
+		SM_FREE(quoted);
+	}
+#endif
 	if (tTd(39, 1))
 		sm_dprintf("map_rewrite => %s\n", buf);
 	return buf;
@@ -694,7 +712,7 @@ sun_init_domain()
 	**  something like "sun.com").
 	*/
 
-	char buf[MAXNAME];
+	char buf[MAXNAME];	/* EAI:ok (domainname) */
 	char *period, *autodomain;
 
 	if (getdomainname(buf, sizeof buf) < 0)
@@ -940,7 +958,7 @@ extract_canonname(name, dot, line, cbuf, cbuflen)
 
 	for (i = 1; ; i++)
 	{
-		char nbuf[MAXNAME + 1];
+		char nbuf[MAXNAME + 1];	/* EAI:hostname */
 
 		p = get_column(line, i, '\0', nbuf, sizeof(nbuf));
 		if (p == NULL)
@@ -952,13 +970,13 @@ extract_canonname(name, dot, line, cbuf, cbuflen)
 		{
 			(void) sm_strlcpy(cbuf, p, cbuflen);
 		}
-		if (sm_strcasecmp(name, p) == 0)
+		if (SM_STRCASEEQ(name, p))
 			found = true;
 		else if (dot != NULL)
 		{
 			/* try looking for the FQDN as well */
 			*dot = '.';
-			if (sm_strcasecmp(name, p) == 0)
+			if (SM_STRCASEEQ(name, p))
 				found = true;
 			*dot = '\0';
 		}
@@ -1054,14 +1072,14 @@ dns_map_parseargs(map,args)
 			break;
 		switch (*++p)
 		{
-#if DNSSEC_TEST
+#  if DNSSEC_TEST
 		  case '@':
 			++p;
 			if (nsportip(p) < 0)
 				syserr("dns map %s: nsportip(%s)=failed",
 					map->map_mname, p);
 			break;
-#endif /* DNSSEC_TEST */
+#  endif /* DNSSEC_TEST */
 
 		  case 'A':
 			map->map_mflags |= MF_APPEND;
@@ -1132,9 +1150,9 @@ dns_map_parseargs(map,args)
 			break;
 
 		  case 'S':
-#if defined(RES_USE_EDNS0) && defined(RES_USE_DNSSEC)
+#  if defined(RES_USE_EDNS0) && defined(RES_USE_DNSSEC)
 			map_p->dns_m_options |= SM_RES_DNSSEC;
-#endif
+#  endif
 			break;
 
 		  case 'r':
@@ -1339,12 +1357,12 @@ dns_map_lookup(map, name, av, statp)
 					    sizeof(buf6));
 			break;
 #  endif /* NETINET6 */
-# ifdef T_TLSA
+#  ifdef T_TLSA
 		  case T_TLSA:
 			type = "T_TLSA";
 			value = rr->rr_u.rr_txt;
 			break;
-# endif /* T_TLSA */
+#  endif /* T_TLSA */
 		}
 
 		(void) strreplnonprt(value, 'X');
@@ -1427,8 +1445,7 @@ dns_map_lookup(map, name, av, statp)
 		result = map_rewrite(map, vp, vsize, av);
 
   cleanup:
-	if (vp != NULL)
-		sm_free(vp);
+	SM_FREE(vp);
 	dns_free_data(r);
 	return result;
 }
@@ -1679,7 +1696,7 @@ ndbm_map_open(map, mode)
 		map->map_mflags |= MF_LOCKED;
 		if (geteuid() == 0 && TrustedUid != 0)
 		{
-#  if HASFCHOWN
+# if HASFCHOWN
 			if (fchown(dfd, TrustedUid, -1) < 0 ||
 			    fchown(pfd, TrustedUid, -1) < 0)
 			{
@@ -1691,13 +1708,13 @@ ndbm_map_open(map, mode)
 				message("050 ownership change on %s failed: %s",
 					map->map_file, sm_errstring(err));
 			}
-#  else /* HASFCHOWN */
+# else /* HASFCHOWN */
 			sm_syslog(LOG_ALERT, NOQID,
 				  "no fchown(): cannot change ownership on %s",
 				  map->map_file);
 			message("050 no fchown(): cannot change ownership on %s",
 				map->map_file);
-#  endif /* HASFCHOWN */
+# endif /* HASFCHOWN */
 		}
 	}
 	return true;
@@ -1717,7 +1734,7 @@ ndbm_map_lookup(map, name, av, statp)
 {
 	datum key, val;
 	int dfd, pfd;
-	char keybuf[MAXNAME + 1];
+	char keybuf[MAXNAME + 1];	/* EAI:ok */
 	struct stat stbuf;
 
 	if (tTd(38, 20))
@@ -1732,7 +1749,7 @@ ndbm_map_lookup(map, name, av, statp)
 			key.dsize = sizeof(keybuf) - 1;
 		memmove(keybuf, key.dptr, key.dsize);
 		keybuf[key.dsize] = '\0';
-		makelower(keybuf);
+		makelower_buf(keybuf, keybuf, sizeof(keybuf));
 		key.dptr = keybuf;
 	}
 lockdbm:
@@ -1815,7 +1832,7 @@ ndbm_map_store(map, lhs, rhs)
 	datum key;
 	datum data;
 	int status;
-	char keybuf[MAXNAME + 1];
+	char keybuf[MAXNAME + 1];	/* EAI:ok */
 
 	if (tTd(38, 12))
 		sm_dprintf("ndbm_map_store(%s, %s, %s)\n",
@@ -1829,7 +1846,7 @@ ndbm_map_store(map, lhs, rhs)
 			key.dsize = sizeof(keybuf) - 1;
 		memmove(keybuf, key.dptr, key.dsize);
 		keybuf[key.dsize] = '\0';
-		makelower(keybuf);
+		makelower_buf(keybuf, keybuf, sizeof(keybuf));
 		key.dptr = keybuf;
 	}
 
@@ -1861,8 +1878,7 @@ ndbm_map_store(map, lhs, rhs)
 				old.dsize = strlen(old.dptr);
 				if (data.dsize + old.dsize + 2 > bufsiz)
 				{
-					if (buf != NULL)
-						(void) sm_free(buf);
+					SM_FREE(buf);
 					bufsiz = data.dsize + old.dsize + 2;
 					buf = sm_pmalloc_x(bufsiz);
 				}
@@ -2187,7 +2203,7 @@ db_map_open(map, mode, mapclassname, dbtype, openinfo)
 
 #  if DB_VERSION_MAJOR > 2
 		ret = db_create(&db, NULL, 0);
-#  ifdef DB_CACHE_SIZE
+#   ifdef DB_CACHE_SIZE
 		if (ret == 0 && db != NULL)
 		{
 			ret = db->set_cachesize(db, 0, DB_CACHE_SIZE, 0);
@@ -2197,8 +2213,8 @@ db_map_open(map, mode, mapclassname, dbtype, openinfo)
 				db = NULL;
 			}
 		}
-#  endif /* DB_CACHE_SIZE */
-#  ifdef DB_HASH_NELEM
+#   endif /* DB_CACHE_SIZE */
+#   ifdef DB_HASH_NELEM
 		if (dbtype == DB_HASH && ret == 0 && db != NULL)
 		{
 			ret = db->set_h_nelem(db, DB_HASH_NELEM);
@@ -2208,7 +2224,7 @@ db_map_open(map, mode, mapclassname, dbtype, openinfo)
 				db = NULL;
 			}
 		}
-#  endif /* DB_HASH_NELEM */
+#   endif /* DB_HASH_NELEM */
 		if (ret == 0 && db != NULL)
 		{
 			ret = db->open(db,
@@ -2216,10 +2232,10 @@ db_map_open(map, mode, mapclassname, dbtype, openinfo)
 					buf, NULL, dbtype, flags, DBMMODE);
 			if (ret != 0)
 			{
-#ifdef DB_OLD_VERSION
+#   ifdef DB_OLD_VERSION
 				if (ret == DB_OLD_VERSION)
 					ret = EINVAL;
-#endif /* DB_OLD_VERSION */
+#   endif /* DB_OLD_VERSION */
 				(void) db->close(db, 0);
 				db = NULL;
 			}
@@ -2294,7 +2310,7 @@ db_map_open(map, mode, mapclassname, dbtype, openinfo)
 		(void) db->sync(db, 0);
 		if (geteuid() == 0 && TrustedUid != 0)
 		{
-#  if HASFCHOWN
+# if HASFCHOWN
 			if (fchown(fd, TrustedUid, -1) < 0)
 			{
 				int err = errno;
@@ -2305,13 +2321,13 @@ db_map_open(map, mode, mapclassname, dbtype, openinfo)
 				message("050 ownership change on %s failed: %s",
 					buf, sm_errstring(err));
 			}
-#  else /* HASFCHOWN */
+# else /* HASFCHOWN */
 			sm_syslog(LOG_ALERT, NOQID,
 				  "no fchown(): cannot change ownership on %s",
 				  map->map_file);
 			message("050 no fchown(): cannot change ownership on %s",
 				map->map_file);
-#  endif /* HASFCHOWN */
+# endif /* HASFCHOWN */
 		}
 	}
 
@@ -2351,7 +2367,7 @@ db_map_lookup(map, name, av, statp)
 	int save_errno;
 	int fd;
 	struct stat stbuf;
-	char keybuf[MAXNAME + 1];
+	char keybuf[MAXNAME + 1];	/* EAI:ok */
 	char buf[MAXPATHLEN];
 
 	memset(&key, '\0', sizeof(key));
@@ -2380,7 +2396,7 @@ db_map_lookup(map, name, av, statp)
 	memmove(keybuf, name, key.size);
 	keybuf[key.size] = '\0';
 	if (!bitset(MF_NOFOLDCASE, map->map_mflags))
-		makelower(keybuf);
+		makelower_buf(keybuf, keybuf, sizeof(keybuf));
   lockdb:
 # if DB_VERSION_MAJOR < 2
 	fd = db->fd(db);
@@ -2511,7 +2527,7 @@ db_map_store(map, lhs, rhs)
 	DBT key;
 	DBT data;
 	register DB *db = map->map_db2;
-	char keybuf[MAXNAME + 1];
+	char keybuf[MAXNAME + 1];	/* EAI:ok */
 
 	memset(&key, '\0', sizeof(key));
 	memset(&data, '\0', sizeof(data));
@@ -2528,7 +2544,7 @@ db_map_store(map, lhs, rhs)
 			key.size = sizeof(keybuf) - 1;
 		memmove(keybuf, key.data, key.size);
 		keybuf[key.size] = '\0';
-		makelower(keybuf);
+		makelower_buf(keybuf, keybuf, sizeof(keybuf));
 		key.data = keybuf;
 	}
 
@@ -2579,8 +2595,7 @@ db_map_store(map, lhs, rhs)
 				old.size = strlen(old.data);
 				if (data.size + old.size + 2 > (size_t) bufsiz)
 				{
-					if (buf != NULL)
-						sm_free(buf);
+					SM_FREE(buf);
 					bufsiz = data.size + old.size + 2;
 					buf = sm_pmalloc_x(bufsiz);
 				}
@@ -2729,6 +2744,7 @@ cdb_map_open(map, mode)
 	int fd, status, omode, smode;
 	long sff;
 	struct stat st;
+	struct cdb *cdbp;
 	char buf[MAXPATHLEN];
 
 	if (tTd(38, 2))
@@ -2741,11 +2757,7 @@ cdb_map_open(map, mode)
 	omode = mode;
 
 	/*
-	**  Notes:
-	**  If a temporary file is used, then there must be some check
-	**  that the rename() is "safe" (i.e., does not overwrite some
-	**  "other" file created by an attacker).
-	**
+	**  Note:
 	**  The code to add the extension and to set up safefile()
 	**  and open() should be in a common function
 	**  (it would be nice to re-use libsmdb?)
@@ -2763,20 +2775,13 @@ cdb_map_open(map, mode)
 	sff = SFF_ROOTOK|SFF_REGONLY;
 	if (mode == O_RDWR)
 	{
-		if (sm_strlcat(buf, ".tmp", sizeof buf) >= sizeof buf)
-		{
-			errno = 0;
-			if (!bitset(MF_OPTIONAL, map->map_mflags))
-				syserr("cdb map \"%s\": map file %s name too long",
-					map->map_mname, map->map_file);
-			return false;
-		}
 		sff |= SFF_CREAT;
 		if (!bitnset(DBS_WRITEMAPTOSYMLINK, DontBlameSendmail))
 			sff |= SFF_NOSLINK;
 		if (!bitnset(DBS_WRITEMAPTOHARDLINK, DontBlameSendmail))
 			sff |= SFF_NOHLINK;
 		smode = S_IWRITE;
+		map->map_mflags |= MF_LOCKED;
 	}
 	else
 	{
@@ -2841,6 +2846,11 @@ cdb_map_open(map, mode)
 		syserr("cdb_map_open: cannot lock %s", buf);
 # endif /* !LOCK_ON_OPEN */
 
+	map->map_lockfd = fd;
+
+	if (fd >= 0 && fstat(fd, &st) >= 0)
+		map->map_mtime = st.st_mtime;
+
 	/* only for aliases! */
 	if (mode == O_RDWR)
 	{
@@ -2859,45 +2869,49 @@ cdb_map_open(map, mode)
 		map->map_db2 = (ARBPTR_T)cdbmp;
 		return true;
 	}
-	else
+
+	cdbp = (struct cdb *) xalloc(sizeof(*cdbp));
+	status = cdb_init(cdbp, fd);
+	if (status != 0)
 	{
-		struct cdb *cdbp;
-
-		cdbp = (struct cdb *) xalloc(sizeof(*cdbp));
-		status = cdb_init(cdbp, fd);
-		if (status != 0)
-		{
-			close(fd);
-			if (!bitset(MF_OPTIONAL, map->map_mflags))
-				syserr("initialization of cdb map failed");
-			return false;
-		}
-		map->map_db1 = (ARBPTR_T)cdbp;
-		return true;
+		close(fd);
+		if (!bitset(MF_OPTIONAL, map->map_mflags))
+			syserr("initialization of cdb map failed");
+		return false;
 	}
-
-	/* NOTREACHED */
-	return false;
+	map->map_db1 = (ARBPTR_T)cdbp;
+	return true;
 }
 
 char *
-cdb_map_lookup (map, name, av, statp)
-	MAP * map;
+cdb_map_lookup(map, name, av, statp)
+	MAP *map;
 	char *name;
 	char **av;
 	int *statp;
 {
-	char * data;
+	char *data;
 	struct cdb *cdbmap;
 	unsigned int klen, dlen;
-	int st;
-	char key[MAXNAME+1];
+	int st, fd;
+	char key[MAXNAME + 1];	/* EAI:ok */
+	char buf[MAXPATHLEN];
+	struct stat stbuf;
 
 	data = NULL;
 	cdbmap = map->map_db1;
 	if (tTd(38, 20))
 		sm_dprintf("cdb_map_lookup(%s, %s)\n", map->map_mname, name);
 
+	if (!smdb_add_extension(buf, sizeof(buf), map->map_file, CDBext))
+	{
+		errno = 0;
+		if (!bitset(MF_OPTIONAL, map->map_mflags))
+			syserr("cdb map \"%s\": map file %s name too long",
+				map->map_mname, map->map_file);
+		return false;
+	}
+
 	klen = strlen(name);
 	if (klen > sizeof(key) - 1)
 		klen = sizeof(key) - 1;
@@ -2905,7 +2919,48 @@ cdb_map_lookup (map, name, av, statp)
 	key[klen] = '\0';
 
 	if (!bitset(MF_NOFOLDCASE, map->map_mflags))
-		makelower(key);
+		makelower_buf(key, key, sizeof(key));
+
+  lockdb:
+	fd = map->map_lockfd;
+	if (fd >= 0 && !bitset(MF_LOCKED, map->map_mflags))
+		(void) lockfile(fd, buf, NULL, LOCK_SH);
+
+	if (fd < 0 || fstat(fd, &stbuf) < 0 || stbuf.st_mtime > map->map_mtime)
+	{
+		/* Reopen the database to sync the cache */
+		int omode = bitset(map->map_mflags, MF_WRITABLE) ? O_RDWR
+								 : O_RDONLY;
+
+		if (fd >= 0 && !bitset(MF_LOCKED, map->map_mflags))
+			(void) lockfile(fd, buf, NULL, LOCK_UN);
+		map->map_mflags |= MF_CLOSING;
+		map->map_class->map_close(map);
+		map->map_mflags &= ~(MF_OPEN|MF_WRITABLE|MF_CLOSING);
+		if (map->map_class->map_open(map, omode))
+		{
+			map->map_mflags |= MF_OPEN;
+			if ((omode & O_ACCMODE) == O_RDWR)
+				map->map_mflags |= MF_WRITABLE;
+			cdbmap = map->map_db1;
+			goto lockdb;
+		}
+		else
+		{
+			if (!bitset(MF_OPTIONAL, map->map_mflags))
+			{
+				extern MAPCLASS BogusMapClass;
+
+				*statp = EX_TEMPFAIL;
+				map->map_orgclass = map->map_class;
+				map->map_class = &BogusMapClass;
+				map->map_mflags |= MF_OPEN;
+				syserr("Cannot reopen CDB database %s",
+					map->map_file);
+			}
+			return NULL;
+		}
+	}
 
 	st = 0;
 	if (bitset(MF_TRY0NULL, map->map_mflags))
@@ -2920,6 +2975,8 @@ cdb_map_lookup (map, name, av, statp)
 		if (st == 1)
 			 map->map_mflags &= ~MF_TRY0NULL;
 	}
+	if (fd >= 0 && !bitset(MF_LOCKED, map->map_mflags))
+		(void) lockfile(fd, buf, NULL, LOCK_UN);
 	if (st != 1)
 	{
 		if (st < 0)
@@ -2953,7 +3010,7 @@ cdb_map_store(map, lhs, rhs)
 	size_t klen;
 	size_t vlen;
 	int status;
-	char keybuf[MAXNAME + 1];
+	char keybuf[MAXNAME + 1];	/* EAI:ok */
 
 	cdbmp = map->map_db2;
 	if (cdbmp == NULL)
@@ -2967,7 +3024,7 @@ cdb_map_store(map, lhs, rhs)
 			klen = sizeof(keybuf) - 1;
 		memmove(keybuf, lhs, klen);
 		keybuf[klen] = '\0';
-		makelower(keybuf);
+		makelower_buf(keybuf, keybuf, sizeof(keybuf));
 		lhs = keybuf;
 	}
 
@@ -2995,19 +3052,16 @@ cdb_map_close(map)
 	if (cdbp != NULL)
 	{
 		if (tTd(38, 20))
-			sm_dprintf("cdb_map_close(%p)\n", (void *)cdbp);
+			sm_dprintf("cdb_map_close(%p): cdbp\n", (void *)cdbp);
 		fd = cdb_fileno(cdbp);
 		cdb_free(cdbp);
-		sm_free(cdbp);
-		cdbp = NULL;
+		SM_FREE(cdbp);
 	}
 	cdbmp = map->map_db2;
 	if (cdbmp != NULL)
 	{
-		char tmpfn[MAXPATHLEN], cdbfn[MAXPATHLEN];
-
 		if (tTd(38, 20))
-			sm_dprintf("cdb_map_close(%p)\n", (void *)cdbmp);
+			sm_dprintf("cdb_map_close(%p): cdmbp\n", (void *)cdbmp);
 		fd = cdb_fileno(cdbmp);
 
 		/* write out the distinguished alias */
@@ -3015,33 +3069,17 @@ cdb_map_close(map)
 		cdb_map_store(map, "@", "@");
 
 		if (cdb_make_finish(cdbmp) != 0)
-			syserr("cdb: failed to write %s", map->map_file);
-		if (fd >=0)
+			syserr("cdb: cdb_make_finish(%s) failed",
+				map->map_file);
+		if (fd >= 0)
 		{
 			if (fsync(fd) == -1)
 				syserr("cdb: fsync(%s) failed", map->map_file);
 			if (close(fd) == -1)
 				syserr("cdb: close(%s) failed", map->map_file);
+			fd = -1;
 		}
-
-		if (!smdb_add_extension(cdbfn, sizeof(cdbfn), map->map_file,
-					CDBext))
-		{
-			syserr("cdb: add extension to %s failed",
-				map->map_file);
-		}
-		if (sm_strlcpy(tmpfn, cdbfn, sizeof tmpfn) >= sizeof tmpfn ||
-		    sm_strlcat(tmpfn, ".tmp", sizeof tmpfn) >= sizeof tmpfn)
-		{
-			syserr("cdb: set temp filename for %s failed",
-				map->map_file);
-		}
-		if (tTd(38, 80))
-			sm_dprintf("rename(%s, %s)\n", tmpfn, cdbfn);
-		if (rename(tmpfn, cdbfn) == -1)
-			syserr("cdb: rename(%s, %s) failed", tmpfn, cdbfn);
-		sm_free(cdbmp);
-		cdbmp = NULL;
+		SM_FREE(cdbmp);
 	}
 	if (fd >=0)
 		close(fd);
@@ -3114,8 +3152,7 @@ nis_map_open(map, mode)
 	if (tTd(38, 10))
 		sm_dprintf("nis_map_open: yp_match(@, %s, %s) => %s\n",
 			map->map_domain, map->map_file, yperr_string(yperr));
-	if (vp != NULL)
-		sm_free(vp);
+	SM_FREE(vp);
 
 	if (yperr == 0 || yperr == YPERR_KEY || yperr == YPERR_BUSY)
 	{
@@ -3159,7 +3196,7 @@ nis_map_lookup(map, name, av, statp)
 	auto int vsize;
 	int buflen;
 	int yperr;
-	char keybuf[MAXNAME + 1];
+	char keybuf[MAXNAME + 1];	/* EAI:ok */
 	char *SM_NONVOLATILE result = NULL;
 
 	if (tTd(38, 20))
@@ -3172,7 +3209,7 @@ nis_map_lookup(map, name, av, statp)
 	memmove(keybuf, name, buflen);
 	keybuf[buflen] = '\0';
 	if (!bitset(MF_NOFOLDCASE, map->map_mflags))
-		makelower(keybuf);
+		makelower_buf(keybuf, keybuf, sizeof(keybuf));
 	yperr = YPERR_KEY;
 	vp = NULL;
 	if (bitset(MF_TRY0NULL, map->map_mflags))
@@ -3195,8 +3232,7 @@ nis_map_lookup(map, name, av, statp)
 	{
 		if (yperr != YPERR_KEY && yperr != YPERR_BUSY)
 			map->map_mflags &= ~(MF_VALID|MF_OPEN);
-		if (vp != NULL)
-			sm_free(vp);
+		SM_FREE(vp);
 		return NULL;
 	}
 	SM_TRY
@@ -3205,8 +3241,7 @@ nis_map_lookup(map, name, av, statp)
 		else
 			result = map_rewrite(map, vp, vsize, av);
 	SM_FINALLY
-		if (vp != NULL)
-			sm_free(vp);
+		SM_FREE(vp);
 	SM_END_TRY
 	return result;
 }
@@ -3230,8 +3265,8 @@ nis_getcanonname(name, hbsize, statp)
 	static bool try1null = true;
 	static char *yp_domain = NULL;
 	char host_record[MAXLINE];
-	char cbuf[MAXNAME];
-	char nbuf[MAXNAME + 1];
+	char cbuf[MAXNAME];	/* EAI:hostname */
+	char nbuf[MAXNAME + 1];	/* EAI:hostname */
 
 	if (tTd(38, 20))
 		sm_dprintf("nis_getcanonname(%s)\n", name);
@@ -3246,7 +3281,7 @@ nis_getcanonname(name, hbsize, statp)
 
 	if (yp_domain == NULL)
 		(void) yp_get_default_domain(&yp_domain);
-	makelower(nbuf);
+	makelower_buf(nbuf, nbuf, sizeof(nbuf));
 	yperr = YPERR_KEY;
 	vp = NULL;
 	if (try0null)
@@ -3273,8 +3308,7 @@ nis_getcanonname(name, hbsize, statp)
 			*statp = EX_TEMPFAIL;
 		else
 			*statp = EX_UNAVAILABLE;
-		if (vp != NULL)
-			sm_free(vp);
+		SM_FREE(vp);
 		return false;
 	}
 	(void) sm_strlcpy(host_record, vp, sizeof(host_record));
@@ -3475,7 +3509,7 @@ nisplus_map_lookup(map, name, av, statp)
 	auto int vsize;
 	char *skp;
 	int skleft;
-	char search_key[MAXNAME + 4];
+	char search_key[MAXNAME + 4];	/* EAI:ok */
 	char qbuf[MAXLINE + NIS_MAXNAMELEN];
 	nis_result *result;
 
@@ -3532,7 +3566,7 @@ nisplus_map_lookup(map, name, av, statp)
 	}
 	*skp = '\0';
 	if (!bitset(MF_NOFOLDCASE, map->map_mflags))
-		makelower(search_key);
+		makelower_buf(search_key, search_key, sizeof(search_key));
 
 	/* construct the query */
 	if (PARTIAL_NAME(map->map_file))
@@ -3614,8 +3648,8 @@ nisplus_getcanonname(name, hbsize, statp)
 	auto int vsize;
 	nis_result *result;
 	char *p;
-	char nbuf[MAXNAME + 1];
-	char qbuf[MAXLINE + NIS_MAXNAMELEN];
+	char nbuf[MAXNAME + 1];	/* EAI:hostname */
+	char qbuf[MAXLINE + NIS_MAXNAMELEN];	/* EAI:hostname */
 
 	if (sm_strlcpy(nbuf, name, sizeof(nbuf)) >= sizeof(nbuf))
 	{
@@ -3722,7 +3756,7 @@ nisplus_getcanonname(name, hbsize, statp)
 char *
 nisplus_default_domain()
 {
-	static char default_domain[MAXNAME + 1] = "";
+	static char default_domain[MAXNAME + 1] = "";	/* EAI:hostname */
 	char *p;
 
 	if (default_domain[0] != '\0')
@@ -3800,14 +3834,14 @@ ldapmap_open(map, mode)
 	if (tTd(38, 2))
 		sm_dprintf("ldapmap_open(%s, %d): ", map->map_mname, mode);
 
-#if defined(SUN_EXTENSIONS) && defined(SUN_SIMPLIFIED_LDAP) && \
+# if defined(SUN_EXTENSIONS) && defined(SUN_SIMPLIFIED_LDAP) && \
     HASLDAPGETALIASBYNAME
 	if (VendorCode == VENDOR_SUN &&
 	    strcmp(map->map_mname, "aliases.ldap") == 0)
 	{
 		return true;
 	}
-#endif /* defined(SUN_EXTENSIONS) && defined(SUN_SIMPLIFIED_LDAP) && ... */
+# endif /* defined(SUN_EXTENSIONS) && defined(SUN_SIMPLIFIED_LDAP) && ... */
 
 	mode &= O_ACCMODE;
 
@@ -4002,26 +4036,45 @@ ldapmap_lookup(map, name, av, statp)
 	SM_LDAP_STRUCT *lmap = NULL;
 	char *argv[SM_LDAP_ARGS];
 	char keybuf[MAXKEY];
-#if SM_LDAP_ARGS != MAX_MAP_ARGS
-# ERROR _SM_LDAP_ARGS must be the same as _MAX_MAP_ARGS
-#endif
+# if SM_LDAP_ARGS != MAX_MAP_ARGS
+#  ERROR "SM_LDAP_ARGS must be the same as MAX_MAP_ARGS"
+# endif
 
-#if defined(SUN_EXTENSIONS) && defined(SUN_SIMPLIFIED_LDAP) && \
+# define AV_FREE(av)	\
+	do		\
+	{		\
+		int ai;	\
+		for (ai = 0; ai < SM_LDAP_ARGS && av[ai] != NULL; ai++) \
+			SM_FREE(av[ai]);	\
+	} while (0)
+
+# if USE_EAI
+	bool allascii;
+	char *largv[SM_LDAP_ARGS];
+	char **largs;
+
+#  define LARGV_FREE	AV_FREE(largv)
+# else
+#  define largs av
+#  define LARGV_FREE
+# endif
+
+# if defined(SUN_EXTENSIONS) && defined(SUN_SIMPLIFIED_LDAP) && \
     HASLDAPGETALIASBYNAME
 	if (VendorCode == VENDOR_SUN &&
 	    strcmp(map->map_mname, "aliases.ldap") == 0)
 	{
 		int rc;
-#if defined(GETLDAPALIASBYNAME_VERSION) && (GETLDAPALIASBYNAME_VERSION >= 2)
+#  if defined(GETLDAPALIASBYNAME_VERSION) && (GETLDAPALIASBYNAME_VERSION >= 2)
 		extern char *__getldapaliasbyname();
 		char *answer;
 
 		answer = __getldapaliasbyname(name, &rc);
-#else
-		char answer[MAXNAME + 1];
+#  else
+		char answer[MAXNAME + 1];	/* EAI:Sun only, ignore */
 
 		rc = __getldapaliasbyname(name, answer, sizeof(answer));
-#endif
+#  endif
 		if (rc != 0)
 		{
 			if (tTd(38, 20))
@@ -4038,38 +4091,79 @@ ldapmap_lookup(map, name, av, statp)
 			result = map_rewrite(map, name, strlen(name), NULL);
 		else
 			result = map_rewrite(map, answer, strlen(answer), av);
-#if defined(GETLDAPALIASBYNAME_VERSION) && (GETLDAPALIASBYNAME_VERSION >= 2)
+#  if defined(GETLDAPALIASBYNAME_VERSION) && (GETLDAPALIASBYNAME_VERSION >= 2)
 		free(answer);
-#endif
+#  endif
 		return result;
 	}
-#endif /* defined(SUN_EXTENSIONS) && defined(SUN_SIMPLIFIED_LDAP) && ... */
+# endif /* defined(SUN_EXTENSIONS) && defined(SUN_SIMPLIFIED_LDAP) && ... */
 
 	/* Get ldap struct pointer from map */
 	lmap = (SM_LDAP_STRUCT *) map->map_db1;
 	sm_ldap_setopts(lmap->ldap_ld, lmap);
 
+	/* initialize first element so AV_FREE can work */
+	argv[0] = NULL;
+# if USE_EAI
+	largv[0] = NULL;
+# endif
+
 	if (lmap->ldap_multi_args)
 	{
 		SM_REQUIRE(av != NULL);
 		memset(argv, '\0', sizeof(argv));
+# if USE_EAI
+		largs = av;
+		memset(largv, '\0', sizeof(largv));
+
+		/* this is ugly - can we merge it with the next loop? */
+		allascii = true;
+		if (!bitset(MF_MATCHONLY, map->map_mflags))
+		{
+			for (i = 0, allascii = true;
+			     i < SM_LDAP_ARGS && av[i] != NULL;
+			     i++)
+			{
+				if (!addr_is_ascii(av[i]))
+				{
+					allascii = false;
+					largs = largv;
+					break;
+				}
+			}
+		}
+# endif /* USE_EAI */
 		for (i = 0; i < SM_LDAP_ARGS && av[i] != NULL; i++)
 		{
 			argv[i] = sm_strdup(av[i]);
 			if (argv[i] == NULL)
 			{
-				int save_errno, j;
-
-				save_errno = errno;
-				for (j = 0; j < i && argv[j] != NULL; j++)
-					SM_FREE(argv[j]);
 				*statp = EX_TEMPFAIL;
-				errno = save_errno;
-				return NULL;
+				goto none;
 			}
 
 			if (!bitset(MF_NOFOLDCASE, map->map_mflags))
+/*			    && !bitset(MF_MATCHONLY, map->map_mflags))	*/
+/* see below: av[]/largs onluy used if !MF_MATCHONLY !? */
+			{
+# if USE_EAI
+				if (!allascii)
+				{
+					char *lower;
+
+					lower = makelower(av[i]);
+					largv[i] = sm_strdup(lower);
+					if (largv[i] == NULL)
+					{
+						*statp = EX_TEMPFAIL;
+						goto none;
+					}
+				}
+				else
+# endif /* USE_EAI */
+				/* NOTE: see else above! */
 				SM_CONVERT_ID(av[i]);
+			}
 		}
 	}
 	else
@@ -4104,8 +4198,7 @@ ldapmap_lookup(map, name, av, statp)
 		msgid = sm_ldap_search_m(lmap, argv);
 
 		/* free the argv array and its content, no longer needed */
-		for (i = 0; i < SM_LDAP_ARGS && argv[i] != NULL; i++)
-			SM_FREE(argv[i]);
+		AV_FREE(argv);
 	}
 	else
 		msgid = sm_ldap_search(lmap, keybuf);
@@ -4140,9 +4233,9 @@ ldapmap_lookup(map, name, av, statp)
 			break;
 		}
 		errno = save_errno;
-		return NULL;
+		goto none;
 	}
-#if SM_LDAP_ERROR_ON_MISSING_ARGS
+# if SM_LDAP_ERROR_ON_MISSING_ARGS
 	else if (msgid == SM_LDAP_ERR_ARG_MISS)
 	{
 		if (bitset(MF_NODEFER, map->map_mflags))
@@ -4152,9 +4245,9 @@ ldapmap_lookup(map, name, av, statp)
 			syserr("554 5.3.5 Error in ldap_search in map %s, too few arguments",
 			       map->map_mname);
 		*statp = EX_CONFIG;
-		return NULL;
+		goto none;
 	}
-#endif /* SM_LDAP_ERROR_ON_MISSING_ARGS */
+# endif /* SM_LDAP_ERROR_ON_MISSING_ARGS */
 
 	*statp = EX_NOTFOUND;
 	vp = NULL;
@@ -4202,12 +4295,12 @@ ldapmap_lookup(map, name, av, statp)
 				       map->map_mname, name);
 		}
 		errno = save_errno;
-		return NULL;
+		goto none;
 	}
 
 	/* Did we match anything? */
 	if (vp == NULL && !bitset(MF_MATCHONLY, map->map_mflags))
-		return NULL;
+		goto none;
 
 	if (*statp == EX_OK)
 	{
@@ -4220,12 +4313,20 @@ ldapmap_lookup(map, name, av, statp)
 		else
 		{
 			/* vp != NULL according to test above */
-			result = map_rewrite(map, vp, strlen(vp), av);
+			result = map_rewrite(map, vp, strlen(vp), largs);
 		}
-		if (vp != NULL)
-			sm_free(vp); /* XXX */
+		SM_FREE(vp); /* XXX */
 	}
+	LARGV_FREE;
 	return result;
+
+  none:
+	/* other cleanup? */
+	save_errno = errno;
+	AV_FREE(argv);
+	LARGV_FREE;
+	errno = save_errno;
+	return NULL;
 }
 
 /*
@@ -4322,6 +4423,7 @@ ldapmap_parseargs(map, args)
 	int i;
 	register char *p = args;
 	SM_LDAP_STRUCT *lmap;
+	SM_LDAP_STRUCT *lmap_alloc;
 	struct lamvalues *lam;
 	struct ladvalues *lad;
 	struct lssvalues *lss;
@@ -4335,12 +4437,14 @@ ldapmap_parseargs(map, args)
 	if (lmap == NULL || lmap != LDAPDefaults)
 	{
 		/* We need to alloc an SM_LDAP_STRUCT struct */
-		lmap = (SM_LDAP_STRUCT *) xalloc(sizeof(*lmap));
+		lmap_alloc = lmap = (SM_LDAP_STRUCT *) xalloc(sizeof(*lmap));
 		if (LDAPDefaults == NULL)
 			sm_ldap_clear(lmap);
 		else
 			STRUCTCOPY(*LDAPDefaults, *lmap);
 	}
+	else
+		lmap_alloc = NULL;
 
 	/* there is no check whether there is really an argument */
 	map->map_mflags |= MF_TRY0NULL|MF_TRY1NULL;
@@ -4382,7 +4486,7 @@ ldapmap_parseargs(map, args)
 			{
 				syserr("%s: Default LDAP string too long",
 				       map->map_mname);
-				return false;
+				goto fail;
 			}
 
 			/* default args for an alias LDAP entry */
@@ -4527,22 +4631,22 @@ ldapmap_parseargs(map, args)
 			break;
 
 		  case 'H':		/* Use LDAP URI */
-#  if !USE_LDAP_INIT
+# if !USE_LDAP_INIT
 			syserr("Must compile with -DUSE_LDAP_INIT to use LDAP URIs (-H) in map %s",
 			       map->map_mname);
-			return false;
-#   else /* !USE_LDAP_INIT */
+			goto fail;
+# else /* !USE_LDAP_INIT */
 			if (lmap->ldap_host != NULL)
 			{
 				syserr("Can not specify both an LDAP host and an LDAP URI in map %s",
 				       map->map_mname);
-				return false;
+				goto fail;
 			}
 			while (isascii(*++p) && isspace(*p))
 				continue;
 			lmap->ldap_uri = p;
 			break;
-#  endif /* !USE_LDAP_INIT */
+# endif /* !USE_LDAP_INIT */
 
 		  case 'h':		/* ldap host */
 			while (isascii(*++p) && isspace(*p))
@@ -4551,7 +4655,7 @@ ldapmap_parseargs(map, args)
 			{
 				syserr("Can not specify both an LDAP host and an LDAP URI in map %s",
 				       map->map_mname);
-				return false;
+				goto fail;
 			}
 			lmap->ldap_host = p;
 			break;
@@ -4603,7 +4707,7 @@ ldapmap_parseargs(map, args)
 						p, map->map_mname);
 					if (ptr != NULL)
 						*ptr = ' ';
-					return false;
+					goto fail;
 				}
 			}
 			break;
@@ -4669,7 +4773,7 @@ ldapmap_parseargs(map, args)
 						p, map->map_mname);
 					if (ptr != NULL)
 						*ptr = ' ';
-					return false;
+					goto fail;
 				}
 			}
 			break;
@@ -4704,7 +4808,7 @@ ldapmap_parseargs(map, args)
 						p, map->map_mname);
 					if (ptr != NULL)
 						*ptr = ' ';
-					return false;
+					goto fail;
 				}
 			}
 			break;
@@ -4748,7 +4852,7 @@ ldapmap_parseargs(map, args)
 				syserr("LDAP version %d exceeds max of %d in map %s",
 				       lmap->ldap_version, LDAP_VERSION_MAX,
 				       map->map_mname);
-				return false;
+				goto fail;
 			}
 # endif /* LDAP_VERSION_MAX */
 # ifdef LDAP_VERSION_MIN
@@ -4757,7 +4861,7 @@ ldapmap_parseargs(map, args)
 				syserr("LDAP version %d is lower than min of %d in map %s",
 				       lmap->ldap_version, LDAP_VERSION_MIN,
 				       map->map_mname);
-				return false;
+				goto fail;
 			}
 # endif /* LDAP_VERSION_MIN */
 			break;
@@ -4864,7 +4968,7 @@ ldapmap_parseargs(map, args)
 			{
 				syserr("LDAP map: cannot open secret %s",
 				       ldapmap_dequote(lmap->ldap_secret));
-				return false;
+				goto fail;
 			}
 			lmap->ldap_secret = sfgets(m_tmp, sizeof(m_tmp),
 						   sfd, TimeOuts.to_fileopen,
@@ -4874,7 +4978,7 @@ ldapmap_parseargs(map, args)
 			{
 				syserr("LDAP map: secret in %s too long",
 				       ldapmap_dequote(lmap->ldap_secret));
-				return false;
+				goto fail;
 			}
 			if (lmap->ldap_secret != NULL &&
 			    strlen(m_tmp) > 0)
@@ -4904,7 +5008,7 @@ ldapmap_parseargs(map, args)
 
 		  default:	       /* Should NEVER get here */
 			syserr("LDAP map: Illegal value in lmap method");
-			return false;
+			goto fail;
 			/* NOTREACHED */
 			break;
 		}
@@ -4944,7 +5048,7 @@ ldapmap_parseargs(map, args)
 		if (!bitset(MCF_OPTFILE, map->map_class->map_cflags))
 		{
 			syserr("No filter given in map %s", map->map_mname);
-			return false;
+			goto fail;
 		}
 	}
 
@@ -4980,7 +5084,7 @@ ldapmap_parseargs(map, args)
 			{
 				syserr("Too many return attributes in %s (max %d)",
 				       map->map_mname, LDAPMAP_MAX_ATTR);
-				return false;
+				goto fail;
 			}
 			if (*v != '\0')
 			{
@@ -5005,7 +5109,7 @@ ldapmap_parseargs(map, args)
 				use = i;
 
 				/* allow override on "objectClass" type */
-				if (sm_strcasecmp(v, "objectClass") == 0 &&
+				if (SM_STRCASEEQ(v, "objectClass") &&
 				    lmap->ldap_attr_type[0] == SM_LDAP_ATTR_OBJCLASS)
 				{
 					use = 0;
@@ -5019,11 +5123,11 @@ ldapmap_parseargs(map, args)
 
 					for (j = 1; j < i; j++)
 					{
-						if (sm_strcasecmp(v, lmap->ldap_attr[j]) == 0)
+						if (SM_STRCASEEQ(v, lmap->ldap_attr[j]))
 						{
 							syserr("Duplicate attribute (%s) in %s",
 							       v, map->map_mname);
-							return false;
+							goto fail;
 						}
 					}
 
@@ -5043,22 +5147,22 @@ ldapmap_parseargs(map, args)
 
 				if (type != NULL && *type != '\0')
 				{
-					if (sm_strcasecmp(type, "dn") == 0)
+					if (SM_STRCASEEQ(type, "dn"))
 					{
 						recurse = true;
 						lmap->ldap_attr_type[use] = SM_LDAP_ATTR_DN;
 					}
-					else if (sm_strcasecmp(type, "filter") == 0)
+					else if (SM_STRCASEEQ(type, "filter"))
 					{
 						recurse = true;
 						lmap->ldap_attr_type[use] = SM_LDAP_ATTR_FILTER;
 					}
-					else if (sm_strcasecmp(type, "url") == 0)
+					else if (SM_STRCASEEQ(type, "url"))
 					{
 						recurse = true;
 						lmap->ldap_attr_type[use] = SM_LDAP_ATTR_URL;
 					}
-					else if (sm_strcasecmp(type, "normal") == 0)
+					else if (SM_STRCASEEQ(type, "normal"))
 					{
 						lmap->ldap_attr_type[use] = SM_LDAP_ATTR_NORMAL;
 						normalseen = true;
@@ -5067,7 +5171,7 @@ ldapmap_parseargs(map, args)
 					{
 						syserr("Unknown attribute type (%s) in %s",
 						       type, map->map_mname);
-						return false;
+						goto fail;
 					}
 				}
 				else
@@ -5087,17 +5191,20 @@ ldapmap_parseargs(map, args)
 		{
 			syserr("LDAP recursion requested in %s but no returnable attribute given",
 			       map->map_mname);
-			return false;
+			goto fail;
 		}
 		if (recurse && lmap->ldap_attrsonly == LDAPMAP_TRUE)
 		{
 			syserr("LDAP recursion requested in %s can not be used with -n",
 			       map->map_mname);
-			return false;
+			goto fail;
 		}
 	}
 	map->map_db1 = (ARBPTR_T) lmap;
 	return true;
+  fail:
+	SM_FREE(lmap_alloc);
+	return false;
 }
 
 /*
@@ -5387,12 +5494,12 @@ ph_timeout(unused)
 }
 
 static void
-#if NPH_VERSION >= 10200
+# if NPH_VERSION >= 10200
 ph_map_send_debug(appdata, text)
 	void *appdata;
-#else
+# else
 ph_map_send_debug(text)
-#endif
+# endif
 	char *text;
 {
 	if (LogLevel > 9)
@@ -5403,12 +5510,12 @@ ph_map_send_debug(text)
 }
 
 static void
-#if NPH_VERSION >= 10200
+# if NPH_VERSION >= 10200
 ph_map_recv_debug(appdata, text)
 	void *appdata;
-#else
+# else
 ph_map_recv_debug(text)
-#endif
+# endif
 	char *text;
 {
 	if (LogLevel > 10)
@@ -5488,9 +5595,9 @@ ph_map_open(map, mode)
 		if (ph_open(&(pmap->ph), host,
 			    PH_OPEN_ROUNDROBIN|PH_OPEN_DONTID,
 			    ph_map_send_debug, ph_map_recv_debug
-#if NPH_VERSION >= 10200
+# if NPH_VERSION >= 10200
 			    , NULL
-#endif
+# endif
 			    ) == 0
 		    && ph_id(pmap->ph, phmap_id) == 0)
 		{
@@ -5664,51 +5771,54 @@ syslog_map_parseargs(map, args)
 			priority += 4;
 
 #ifdef LOG_EMERG
-		if (sm_strcasecmp("EMERG", priority) == 0)
+		if (SM_STRCASEEQ("EMERG", priority))
 			map->map_prio = LOG_EMERG;
 		else
-#endif /* LOG_EMERG */
+#endif
 #ifdef LOG_ALERT
-		if (sm_strcasecmp("ALERT", priority) == 0)
+		if (SM_STRCASEEQ("ALERT", priority))
 			map->map_prio = LOG_ALERT;
 		else
-#endif /* LOG_ALERT */
+#endif
 #ifdef LOG_CRIT
-		if (sm_strcasecmp("CRIT", priority) == 0)
+		if (SM_STRCASEEQ("CRIT", priority))
 			map->map_prio = LOG_CRIT;
 		else
-#endif /* LOG_CRIT */
+#endif
 #ifdef LOG_ERR
-		if (sm_strcasecmp("ERR", priority) == 0)
+		if (SM_STRCASEEQ("ERR", priority))
 			map->map_prio = LOG_ERR;
 		else
-#endif /* LOG_ERR */
+#endif
 #ifdef LOG_WARNING
-		if (sm_strcasecmp("WARNING", priority) == 0)
+		if (SM_STRCASEEQ("WARNING", priority))
 			map->map_prio = LOG_WARNING;
 		else
-#endif /* LOG_WARNING */
+#endif
 #ifdef LOG_NOTICE
-		if (sm_strcasecmp("NOTICE", priority) == 0)
+		if (SM_STRCASEEQ("NOTICE", priority))
 			map->map_prio = LOG_NOTICE;
 		else
-#endif /* LOG_NOTICE */
+#endif
 #ifdef LOG_INFO
-		if (sm_strcasecmp("INFO", priority) == 0)
+		if (SM_STRCASEEQ("INFO", priority))
 			map->map_prio = LOG_INFO;
 		else
-#endif /* LOG_INFO */
+#endif
 #ifdef LOG_DEBUG
-		if (sm_strcasecmp("DEBUG", priority) == 0)
+		if (SM_STRCASEEQ("DEBUG", priority))
 			map->map_prio = LOG_DEBUG;
 		else
-#endif /* LOG_DEBUG */
+#endif
 		{
 			syserr("syslog_map_parseargs: Unknown priority %s",
 			       priority);
 			return false;
 		}
 	}
+#if _FFR_8BITENVADDR
+	map->map_mflags |= MF_KEEPXFMT;
+#endif
 	return true;
 }
 
@@ -5808,6 +5918,9 @@ dprintf_map_parseargs(map, args)
 		}
 		map->map_dbg_level = atoi(dbg_level);
 	}
+# if _FFR_8BITENVADDR
+	map->map_mflags |= MF_KEEPXFMT;
+# endif
 	return true;
 }
 
@@ -5895,23 +6008,23 @@ hes_map_lookup(map, name, av, statp)
 		char *np;
 		int nl;
 		int save_errno;
-		char nbuf[MAXNAME];
+		char nbuf[MAXNAME]; /* EAI:ok - larger buffer used if needed */
 
 		nl = strlen(name);
 		if (nl < sizeof(nbuf) - 1)
 			np = nbuf;
 		else
-			np = xalloc(strlen(name) + 2);
+			np = xalloc(nl + 2);
 		np[0] = '\\';
-		(void) sm_strlcpy(&np[1], name, (sizeof(nbuf)) - 1);
+		(void) sm_strlcpy(&np[1], name, sizeof(nbuf) - 1);
 # ifdef HESIOD_INIT
 		hp = hesiod_resolve(HesiodContext, np, map->map_file);
 # else
 		hp = hes_resolve(np, map->map_file);
-# endif /* HESIOD_INIT */
+# endif
 		save_errno = errno;
 		if (np != nbuf)
-			sm_free(np); /* XXX */
+			SM_FREE(np); /* XXX */
 		errno = save_errno;
 	}
 	else
@@ -6079,7 +6192,7 @@ ni_getcanonname(name, hbsize, statp)
 {
 	char *vptr;
 	char *ptr;
-	char nbuf[MAXNAME + 1];
+	char nbuf[MAXNAME + 1];	/* EAI:hostname */
 
 	if (tTd(38, 20))
 		sm_dprintf("ni_getcanonname(%s)\n", name);
@@ -6250,13 +6363,13 @@ text_map_lookup(map, name, av, statp)
 	int key_idx;
 	bool found_it;
 	long sff = map->map_sff;
-	char search_key[MAXNAME + 1];
+	char search_key[MAXNAME + 1];	/* EAI:ok */
 	char linebuf[MAXLINE];
-	char buf[MAXNAME + 1];
+	char buf[MAXNAME + 1];	/* EAI:ok */
 
 	found_it = false;
 	if (tTd(38, 20))
-		sm_dprintf("text_map_lookup(%s, %s)\n", map->map_mname,  name);
+		sm_dprintf("text_map_lookup(%s, %s)\n", map->map_mname, name);
 
 	buflen = strlen(name);
 	if (buflen > sizeof(search_key) - 1)
@@ -6264,7 +6377,7 @@ text_map_lookup(map, name, av, statp)
 	memmove(search_key, name, buflen);
 	search_key[buflen] = '\0';
 	if (!bitset(MF_NOFOLDCASE, map->map_mflags))
-		makelower(search_key);
+		makelower_buf(search_key, search_key, sizeof(search_key));
 
 	f = safefopen(map->map_file, O_RDONLY, FileMode, sff);
 	if (f == NULL)
@@ -6287,7 +6400,7 @@ text_map_lookup(map, name, av, statp)
 		if (p != NULL)
 			*p = '\0';
 		p = get_column(linebuf, key_idx, delim, buf, sizeof(buf));
-		if (p != NULL && sm_strcasecmp(search_key, p) == 0)
+		if (p != NULL && SM_STRCASEEQ(search_key, p))
 		{
 			found_it = true;
 			break;
@@ -6327,8 +6440,8 @@ text_getcanonname(name, hbsize, statp)
 	char *dot;
 	SM_FILE_T *f;
 	char linebuf[MAXLINE];
-	char cbuf[MAXNAME + 1];
-	char nbuf[MAXNAME + 1];
+	char cbuf[MAXNAME + 1];	/* EAI:hostname */
+	char nbuf[MAXNAME + 1];	/* EAI:hostname */
 
 	if (tTd(38, 20))
 		sm_dprintf("text_getcanonname(%s)\n", name);
@@ -6675,19 +6788,19 @@ user_map_open(map, mode)
 	if (map->map_valcolnm == NULL)
 		/* EMPTY */
 		/* nothing */ ;
-	else if (sm_strcasecmp(map->map_valcolnm, "name") == 0)
+	else if (SM_STRCASEEQ(map->map_valcolnm, "name"))
 		map->map_valcolno = 1;
-	else if (sm_strcasecmp(map->map_valcolnm, "passwd") == 0)
+	else if (SM_STRCASEEQ(map->map_valcolnm, "passwd"))
 		map->map_valcolno = 2;
-	else if (sm_strcasecmp(map->map_valcolnm, "uid") == 0)
+	else if (SM_STRCASEEQ(map->map_valcolnm, "uid"))
 		map->map_valcolno = 3;
-	else if (sm_strcasecmp(map->map_valcolnm, "gid") == 0)
+	else if (SM_STRCASEEQ(map->map_valcolnm, "gid"))
 		map->map_valcolno = 4;
-	else if (sm_strcasecmp(map->map_valcolnm, "gecos") == 0)
+	else if (SM_STRCASEEQ(map->map_valcolnm, "gecos"))
 		map->map_valcolno = 5;
-	else if (sm_strcasecmp(map->map_valcolnm, "dir") == 0)
+	else if (SM_STRCASEEQ(map->map_valcolnm, "dir"))
 		map->map_valcolno = 6;
-	else if (sm_strcasecmp(map->map_valcolnm, "shell") == 0)
+	else if (SM_STRCASEEQ(map->map_valcolnm, "shell"))
 		map->map_valcolno = 7;
 	else
 	{
@@ -7005,7 +7118,7 @@ switch_map_open(map, mode)
 	for (mapno = 0; mapno < nmaps; mapno++)
 	{
 		register STAB *s;
-		char nbuf[MAXNAME + 1];
+		char nbuf[MAXNAME + 1];	/* EAI:not relevant (map name) */
 
 		if (maptype[mapno] == NULL)
 			continue;
@@ -7397,6 +7510,9 @@ regex_map_init(map, ap)
 		if (*p != '\0')
 			*p++ = '\0';
 	}
+# if _FFR_8BITENVADDR
+	(void) dequote_internal_chars(p, p, strlen(p) + 1);
+# endif
 	if (tTd(38, 3))
 		sm_dprintf("regex_map_init: compile '%s' 0x%x\n", p, pflags);
 
@@ -7435,8 +7551,9 @@ regex_map_init(map, ap)
 		if (substrings >= MAX_MATCH)
 		{
 			syserr("too many substrings, %d max", MAX_MATCH);
-			sm_free(map_p->regex_pattern_buf); /* XXX */
-			sm_free(map_p); /* XXX */
+			SM_FREE(map_p->regex_pattern_buf); /* XXX */
+			SM_FREE(map_p); /* XXX */
+			SM_FREE(fields);
 			return false;
 		}
 		if (sub_param != NULL && sub_param[0] != '\0')
@@ -7444,7 +7561,12 @@ regex_map_init(map, ap)
 			/* optional parameter -sfields */
 			if (parse_fields(sub_param, fields,
 					 MAX_MATCH + 1, substrings) == -1)
+			{
+				SM_FREE(map_p->regex_pattern_buf); /* XXX */
+				SM_FREE(map_p); /* XXX */
+				SM_FREE(fields);
 				return false;
+			}
 		}
 		else
 		{
@@ -7498,7 +7620,8 @@ regex_map_lookup(map, name, av, statp)
 	{
 		char **cpp;
 
-		sm_dprintf("regex_map_lookup: key '%s'\n", name);
+		sm_dprintf("regex_map_lookup: name=%s, key='%s'\n",
+			map->map_mname, name);
 		for (cpp = av; cpp != NULL && *cpp != NULL; cpp++)
 			sm_dprintf("regex_map_lookup: arg '%s'\n", *cpp);
 	}
@@ -7521,7 +7644,7 @@ regex_map_lookup(map, name, av, statp)
 	if (map_p->regex_subfields != NULL)
 	{
 		/* option -s */
-		static char retbuf[MAXNAME];
+		static char retbuf[MAXNAME];	/* EAI:not relevant */
 		int fields[MAX_MATCH + 1];
 		bool first = true;
 		int anglecnt = 0, cmntcnt = 0, spacecnt = 0;
@@ -7620,9 +7743,7 @@ regex_map_lookup(map, name, av, statp)
 				  map->map_mname, name);
 			return NULL;
 		}
-
 		*dp = '\0';
-
 		return regex_map_rewrite(map, retbuf, strlen(retbuf), av);
 	}
 	return regex_map_rewrite(map, "", (size_t)0, av);
@@ -7681,7 +7802,7 @@ nsd_map_lookup(map, name, av, statp)
 	int buflen, r;
 	char *p;
 	ns_map_t *ns_map;
-	char keybuf[MAXNAME + 1];
+	char keybuf[MAXNAME + 1];	/* EAI:ok */
 	char buf[MAXLINE];
 
 	if (tTd(38, 20))
@@ -7693,7 +7814,7 @@ nsd_map_lookup(map, name, av, statp)
 	memmove(keybuf, name, buflen);
 	keybuf[buflen] = '\0';
 	if (!bitset(MF_NOFOLDCASE, map->map_mflags))
-		makelower(keybuf);
+		makelower_buf(keybuf, keybuf, sizeof(keybuf));
 
 	ns_map = ns_map_t_find(map->map_file);
 	if (ns_map == NULL)
@@ -7861,7 +7982,7 @@ arpa_map_lookup(map, name, av, statp)
 	memset(result, '\0', sizeof(result));
 	rval = NULL;
 
-# if NETINET6
+#if NETINET6
 	if (sm_strncasecmp(name, "IPv6:", 5) == 0)
 	{
 		struct in6_addr in6_addr;
@@ -7890,8 +8011,8 @@ arpa_map_lookup(map, name, av, statp)
 		}
 	}
 	else
-# endif /* NETINET6 */
-# if NETINET
+#endif /* NETINET6 */
+#if NETINET
 	{
 		struct in_addr in_addr;
 
@@ -7907,7 +8028,7 @@ arpa_map_lookup(map, name, av, statp)
 			*statp = EX_OK;
 		}
 	}
-# endif /* NETINET */
+#endif /* NETINET */
 	if (r < 0)
 		*statp = EX_UNAVAILABLE;
 	if (tTd(38, 2))
@@ -7986,7 +8107,7 @@ setopt_map_lookup(map, name, av, statp)
 	/* check for valid number? */
 
 	/* use a table? */
-	if (sm_strcasecmp(name, "LogLevel") == 0)
+	if (SM_STRCASEEQ(name, "LogLevel"))
 	{
 		LogLevel = val;
 		sm_dprintf("LogLevel=%d\n", val);
@@ -8101,16 +8222,16 @@ socket_map_open(map, mode)
 # endif /* NETUNIX */
 		}
 # if NETUNIX
-		else if (sm_strcasecmp(p, "unix") == 0 ||
-			 sm_strcasecmp(p, "local") == 0)
+		else if (SM_STRCASEEQ(p, "unix") ||
+			 SM_STRCASEEQ(p, "local"))
 			addr.sa.sa_family = AF_UNIX;
 # endif /* NETUNIX */
 # if NETINET
-		else if (sm_strcasecmp(p, "inet") == 0)
+		else if (SM_STRCASEEQ(p, "inet"))
 			addr.sa.sa_family = AF_INET;
 # endif /* NETINET */
 # if NETINET6
-		else if (sm_strcasecmp(p, "inet6") == 0)
+		else if (SM_STRCASEEQ(p, "inet6"))
 			addr.sa.sa_family = AF_INET6;
 # endif /* NETINET6 */
 		else
@@ -8129,24 +8250,24 @@ socket_map_open(map, mode)
 	else
 	{
 		colon = p;
-#if NETUNIX
+# if NETUNIX
 		/* default to AF_UNIX */
 		addr.sa.sa_family = AF_UNIX;
-#else /* NETUNIX */
-# if NETINET
+# else /* NETUNIX */
+#  if NETINET
 		/* default to AF_INET */
 		addr.sa.sa_family = AF_INET;
-# else /* NETINET */
-#  if NETINET6
+#  else /* NETINET */
+#   if NETINET6
 		/* default to AF_INET6 */
 		addr.sa.sa_family = AF_INET6;
-#  else /* NETINET6 */
+#   else /* NETINET6 */
 		syserr("socket map \"%s\": unknown socket type %s",
 		       map->map_mname, p);
 		return false;
-#  endif /* NETINET6 */
-# endif /* NETINET */
-#endif /* NETUNIX */
+#   endif /* NETINET6 */
+#  endif /* NETINET */
+# endif /* NETUNIX */
 	}
 
 # if NETUNIX
@@ -8508,7 +8629,7 @@ socket_map_close(map)
 }
 
 /*
-** SOCKET_MAP_LOOKUP -- look up a datum in a SOCKET table
+**  SOCKET_MAP_LOOKUP -- look up a datum in a SOCKET table
 */
 
 char *
@@ -8522,7 +8643,7 @@ socket_map_lookup(map, name, av, statp)
 	int ret;
 	char *replybuf, *rval, *value, *status, *key;
 	SM_FILE_T *f;
-	char keybuf[MAXNAME + 1];
+	char keybuf[MAXNAME + 1];	/* EAI:ok */
 
 	replybuf = NULL;
 	rval = NULL;
@@ -8538,7 +8659,7 @@ socket_map_lookup(map, name, av, statp)
 			nettolen = sizeof(keybuf) - 1;
 		memmove(keybuf, name, nettolen);
 		keybuf[nettolen] = '\0';
-		makelower(keybuf);
+		makelower_buf(keybuf, keybuf, sizeof(keybuf));
 		key = keybuf;
 	}
 	else
@@ -8659,15 +8780,13 @@ socket_map_lookup(map, name, av, statp)
 			*statp = EX_PROTOCOL;
 	}
 
-	if (replybuf != NULL)
-		sm_free(replybuf);
+	SM_FREE(replybuf);
 	return rval;
 
   errcl:
 	socket_map_close(map);
   error:
-	if (replybuf != NULL)
-		sm_free(replybuf);
+	SM_FREE(replybuf);
 	return rval;
 }
 #endif /* SOCKETMAP */
diff --git a/contrib/sendmail/src/mci.c b/contrib/sendmail/src/mci.c
index a50fd8ed0fe2..c134bd87e8c0 100644
--- a/contrib/sendmail/src/mci.c
+++ b/contrib/sendmail/src/mci.c
@@ -249,9 +249,7 @@ mci_uncache(mcislot, doquit)
 
 		mci->mci_retryrcpt = false;
 		mci->mci_tolist = NULL;
-#if PIPELINING
 		mci->mci_okrcpts = 0;
-#endif
 	}
 
 	SM_FREE(mci->mci_status);
@@ -389,9 +387,7 @@ mci_get(host, m)
 	/* initialize per-message data */
 	mci->mci_retryrcpt = false;
 	mci->mci_tolist = NULL;
-#if PIPELINING
 	mci->mci_okrcpts = 0;
-#endif
 	mci->mci_flags &= ~MCIF_NOTSTICKY;
 
 	if (mci->mci_rpool == NULL)
@@ -1204,7 +1200,7 @@ mci_traverse_persistent(action, pathname)
 		struct dirent *e;
 		char newpath[MAXPATHLEN];
 #if MAXPATHLEN <= MAXNAMLEN - 3
- ERROR "MAXPATHLEN <= MAXNAMLEN - 3"
+# ERROR "MAXPATHLEN <= MAXNAMLEN - 3"
 #endif
 
 		if ((d = opendir(pathname)) == NULL)
@@ -1590,14 +1586,14 @@ mci_generate_persistent_path(host, path, pathlen, createflag)
 	if (host[0] == '[')
 	{
 		bool good = false;
-# if NETINET6
+#if NETINET6
 		if (anynet_pton(AF_INET6, t_host, &in6_addr) == 1)
 			good = true;
-# endif
-# if NETINET
+#endif
+#if NETINET
 		if (inet_addr(t_host) != INADDR_NONE)
 			good = true;
-# endif
+#endif
 		if (!good)
 			return -1;
 	}
diff --git a/contrib/sendmail/src/milter.c b/contrib/sendmail/src/milter.c
index ba783c4c61db..215a52003a00 100644
--- a/contrib/sendmail/src/milter.c
+++ b/contrib/sendmail/src/milter.c
@@ -16,6 +16,9 @@ SM_RCSID("@(#)$Id: milter.c,v 8.281 2013-11-22 20:51:56 ca Exp $")
 # include 
 # include 
 # include 
+# if _FFR_8BITENVADDR
+#  include 
+# endif
 
 # include 
 # include 
@@ -175,11 +178,7 @@ static size_t MilterMaxDataSize = MILTER_MAX_DATA_SIZE;
 		sm_syslog(LOG_ERR, e->e_id, msg, dfname, sm_errstring(save_errno)); \
 	if (SuperSafe == SAFE_REALLY) \
 	{ \
-		if (e->e_dfp != NULL) \
-		{ \
-			(void) sm_io_close(e->e_dfp, SM_TIME_DEFAULT); \
-			e->e_dfp = NULL; \
-		} \
+		SM_CLOSE_FP(e->e_dfp);	\
 		e->e_flags &= ~EF_HAS_DF; \
 	} \
 	errno = save_errno; \
@@ -537,8 +536,9 @@ milter_write(m, cmd, buf, len, to, e, where)
 	{
 		if (tTd(64, 5))
 		{
-			sm_dprintf("milter_write(%s): length %ld out of range, cmd=%c\n",
-				m->mf_name, (long) len, command);
+			sm_dprintf("milter_write(%s): length %ld out of range, mds=%ld, cmd=%c\n",
+				m->mf_name, (long) len,
+				(long) MilterMaxDataSize, command);
 			sm_dprintf("milter_write(%s): buf=%s\n",
 				m->mf_name, str2prt(buf));
 		}
@@ -661,7 +661,7 @@ milter_open(m, parseonly, e)
 	struct hostent *hp = NULL;
 	SOCKADDR addr;
 
-	if (m->mf_conn == NULL || m->mf_conn[0] == '\0')
+	if (SM_IS_EMPTY(m->mf_conn))
 	{
 		if (tTd(64, 5))
 			sm_dprintf("X%s: empty or missing socket information\n",
@@ -711,16 +711,16 @@ milter_open(m, parseonly, e)
 # endif /* NETUNIX */
 		}
 # if NETUNIX
-		else if (sm_strcasecmp(p, "unix") == 0 ||
-			 sm_strcasecmp(p, "local") == 0)
+		else if (SM_STRCASEEQ(p, "unix") ||
+			 SM_STRCASEEQ(p, "local"))
 			addr.sa.sa_family = AF_UNIX;
 # endif
 # if NETINET
-		else if (sm_strcasecmp(p, "inet") == 0)
+		else if (SM_STRCASEEQ(p, "inet"))
 			addr.sa.sa_family = AF_INET;
 # endif
 # if NETINET6
-		else if (sm_strcasecmp(p, "inet6") == 0)
+		else if (SM_STRCASEEQ(p, "inet6"))
 			addr.sa.sa_family = AF_INET6;
 # endif
 		else
@@ -1249,11 +1249,11 @@ milter_setup(line)
 	m->mf_timeout[SMFTO_WRITE] = (time_t) 10;
 	m->mf_timeout[SMFTO_READ] = (time_t) 10;
 	m->mf_timeout[SMFTO_EOM] = (time_t) 300;
-#if _FFR_MILTER_CHECK
+# if _FFR_MILTER_CHECK
 	m->mf_mta_prot_version = SMFI_PROT_VERSION;
 	m->mf_mta_prot_flags = SMFI_CURR_PROT;
 	m->mf_mta_actions = SMFI_CURR_ACTS;
-#endif /* _FFR_MILTER_CHECK */
+# endif /* _FFR_MILTER_CHECK */
 
 	/* now scan through and assign info from the fields */
 	while (*p != '\0')
@@ -1271,6 +1271,8 @@ milter_setup(line)
 		if (*p++ != '=')
 		{
 			syserr("X%s: `=' expected", m->mf_name);
+			/* this should not be reached, but just in case */
+			SM_FREE(m);
 			return;
 		}
 		while (SM_ISSPACE(*p))
@@ -1301,7 +1303,7 @@ milter_setup(line)
 			milter_parse_timeouts(p, m);
 			break;
 
-#if _FFR_MILTER_CHECK
+# if _FFR_MILTER_CHECK
 		  case 'a':
 			m->mf_mta_actions = strtoul(p, NULL, 0);
 			break;
@@ -1311,7 +1313,7 @@ milter_setup(line)
 		  case 'v':
 			m->mf_mta_prot_version = strtoul(p, NULL, 0);
 			break;
-#endif /* _FFR_MILTER_CHECK */
+# endif /* _FFR_MILTER_CHECK */
 
 		  default:
 			syserr("X%s: unknown filter equate %c=",
@@ -1601,7 +1603,7 @@ milter_set_option(name, val, sticky)
 
 	for (mo = MilterOptTab; mo->mo_name != NULL; mo++)
 	{
-		if (sm_strcasecmp(mo->mo_name, name) == 0)
+		if (SM_STRCASEEQ(mo->mo_name, name))
 			break;
 	}
 
@@ -1710,7 +1712,7 @@ milter_reopen_df(e)
 		/* close read-only data file */
 		if (bitset(EF_HAS_DF, e->e_flags) && e->e_dfp != NULL)
 		{
-			(void) sm_io_close(e->e_dfp, SM_TIME_DEFAULT);
+			SM_CLOSE_FP(e->e_dfp);
 			e->e_flags &= ~EF_HAS_DF;
 		}
 
@@ -1891,7 +1893,7 @@ milter_send_macros(m, macros, cmd, e)
 	ssize_t s;
 
 	/* sanity check */
-	if (macros == NULL || macros[0] == NULL)
+	if (NULL == macros || NULL == macros[0])
 		return;
 
 	/* put together data */
@@ -1905,7 +1907,13 @@ milter_send_macros(m, macros, cmd, e)
 		if (v == NULL)
 			continue;
 		expand(v, exp, sizeof(exp), e);
-		s += strlen(macros[i]) + 1 + strlen(exp) + 1;
+		s += strlen(macros[i]) + 1 +
+# if _FFR_8BITENVADDR
+			ilenx(exp)
+# else
+			strlen(exp)
+# endif
+			+ 1;
 	}
 
 	if (s < 0)
@@ -1923,6 +1931,9 @@ milter_send_macros(m, macros, cmd, e)
 		if (v == NULL)
 			continue;
 		expand(v, exp, sizeof(exp), e);
+# if _FFR_8BITENVADDR
+		dequote_internal_chars(exp, exp, sizeof(exp));
+# endif
 
 		if (tTd(64, 10))
 			sm_dprintf("milter_send_macros(%s, %c): %s=%s\n",
@@ -2385,21 +2396,21 @@ milter_negotiate(m, e, milters)
 		return -1;
 	}
 
-#if _FFR_MILTER_CHECK
+# if _FFR_MILTER_CHECK
 	mta_prot_vers = m->mf_mta_prot_version;
 	mta_prot_flags = m->mf_mta_prot_flags;
 	mta_actions = m->mf_mta_actions;
-#else /* _FFR_MILTER_CHECK */
+# else /* _FFR_MILTER_CHECK */
 	mta_prot_vers = SMFI_PROT_VERSION;
 	mta_prot_flags = SMFI_CURR_PROT;
 	mta_actions = SMFI_CURR_ACTS;
-#endif /* _FFR_MILTER_CHECK */
-#if _FFR_MDS_NEGOTIATE
+# endif /* _FFR_MILTER_CHECK */
+# if _FFR_MDS_NEGOTIATE
 	if (MilterMaxDataSize == MILTER_MDS_256K)
 		mta_prot_flags |= SMFIP_MDS_256K;
 	else if (MilterMaxDataSize == MILTER_MDS_1M)
 		mta_prot_flags |= SMFIP_MDS_1M;
-#endif /* _FFR_MDS_NEGOTIATE */
+# endif /* _FFR_MDS_NEGOTIATE */
 
 	fvers = htonl(mta_prot_vers);
 	pflags = htonl(mta_prot_flags);
@@ -2424,7 +2435,10 @@ milter_negotiate(m, e, milters)
 	response = milter_read(m, &rcmd, &rlen, m->mf_timeout[SMFTO_READ], e,
 				"negotiate");
 	if (m->mf_state == SMFS_ERROR)
+	{
+		SM_FREE(response);
 		return -1;
+	}
 
 	if (rcmd != SMFIC_OPTNEG)
 	{
@@ -2513,8 +2527,8 @@ milter_negotiate(m, e, milters)
 		goto error;
 	}
 
-#if _FFR_MDS_NEGOTIATE
-#define MDSWARNING(sz) \
+# if _FFR_MDS_NEGOTIATE
+#  define MDSWARNING(sz) \
 	do	\
 	{	\
 		sm_syslog(LOG_WARNING, NOQID,	\
@@ -2545,7 +2559,7 @@ milter_negotiate(m, e, milters)
 	else if (MilterMaxDataSize != MILTER_MDS_64K)
 		MDSWARNING(MILTER_MDS_64K);
 	m->mf_pflags &= ~SMFI_INTERNAL;
-#endif /* _FFR_MDS_NEGOTIATE */
+# endif /* _FFR_MDS_NEGOTIATE */
 
 	/* check for protocol feature mismatch */
 	if ((m->mf_pflags & mta_prot_flags) != m->mf_pflags)
@@ -2785,10 +2799,10 @@ milter_body(m, e, state)
 		/*  Change LF to CRLF */
 		if (c == '\n')
 		{
-#if !_FFR_MILTER_CONVERT_ALL_LF_TO_CRLF
+# if !_FFR_MILTER_CONVERT_ALL_LF_TO_CRLF
 			/* Not a CRLF already? */
 			if (prevchar != '\r')
-#endif
+# endif
 			{
 				/* Room for CR now? */
 				if (bp + 2 > &buf[sizeof(buf)])
@@ -2973,14 +2987,14 @@ milter_addheader(m, response, rlen, e)
 
 	for (h = e->e_header; h != NULL; h = h->h_link)
 	{
-		if (sm_strcasecmp(h->h_field, response) == 0 &&
+		if (SM_STRCASEEQ(h->h_field, response) &&
 		    !bitset(H_USER, h->h_flags) &&
 		    !bitset(H_TRACE, h->h_flags))
 			break;
 	}
 
 	mh_v_len = 0;
-	mh_value = quote_internal_chars(val, NULL, &mh_v_len);
+	mh_value = quote_internal_chars(val, NULL, &mh_v_len, NULL);
 
 	/* add to e_msgsize */
 	e->e_msgsize += strlen(response) + 2 + strlen(val);
@@ -2995,7 +3009,7 @@ milter_addheader(m, response, rlen, e)
 				  "Milter (%s) change: default header %s value with %s",
 				  m->mf_name, h->h_field, mh_value);
 		if (bitset(SMFIP_HDR_LEADSPC, m->mf_pflags))
-			h->h_value = mh_value;
+			h->h_value = mh_value;	/* XXX must be allocated from rpool? */
 		else
 		{
 			h->h_value = addleadingspace(mh_value, e->e_rpool);
@@ -3096,7 +3110,7 @@ milter_insheader(m, response, rlen, e)
 			  "Milter (%s) insert (%d): header: %s: %s",
 			   m->mf_name, idx, field, val);
 	mh_v_len = 0;
-	mh_value = quote_internal_chars(val, NULL, &mh_v_len);
+	mh_value = quote_internal_chars(val, NULL, &mh_v_len, NULL);
 	insheader(idx, newstr(field), mh_value, H_USER, e,
 		!bitset(SMFIP_HDR_LEADSPC, m->mf_pflags));
 	SM_FREE(mh_value);
@@ -3168,12 +3182,12 @@ milter_changeheader(m, response, rlen, e)
 	}
 
 	mh_v_len = 0;
-	mh_value = quote_internal_chars(val, NULL, &mh_v_len);
+	mh_value = quote_internal_chars(val, NULL, &mh_v_len, NULL);
 
 	sysheader = NULL;
 	for (h = e->e_header; h != NULL; h = h->h_link)
 	{
-		if (sm_strcasecmp(h->h_field, field) == 0)
+		if (SM_STRCASEEQ(h->h_field, field))
 		{
 			if (bitset(H_USER, h->h_flags) && --index <= 0)
 			{
@@ -3300,7 +3314,7 @@ milter_changeheader(m, response, rlen, e)
 	else
 	{
 		if (bitset(SMFIP_HDR_LEADSPC, m->mf_pflags))
-			h->h_value = mh_value;
+			h->h_value = mh_value;	/* XXX must be allocated from rpool? */
 		else
 		{
 			h->h_value = addleadingspace(mh_value, e->e_rpool);
@@ -3515,6 +3529,7 @@ milter_addrcpt_par(response, rlen, e, mname)
 	olderrors = Errors;
 
 	/* how to set ESMTP arguments? */
+/* XXX argv[0] must be [i] */
 	a = parseaddr(argv[0], NULLADDR, RF_COPYALL, ' ', &delimptr, e, true);
 
 	if (a != NULL && olderrors == Errors)
@@ -3998,7 +4013,7 @@ milter_connect(hostname, addr, e, state)
 	else
 		milter_per_connection_check(e);
 
-#if !_FFR_MILTER_CONNECT_REPLYCODE
+# if !_FFR_MILTER_CONNECT_REPLYCODE
 	/*
 	**  SMFIR_REPLYCODE can't work with connect due to
 	**  the requirements of SMTP.  Therefore, ignore the
@@ -4023,7 +4038,7 @@ milter_connect(hostname, addr, e, state)
 			response = NULL;
 		}
 	}
-#endif /* !_FFR_MILTER_CONNECT_REPLYCODE */
+# endif /* !_FFR_MILTER_CONNECT_REPLYCODE */
 	return response;
 }
 
@@ -4683,8 +4698,7 @@ milter_data(e, state)
 
 		if (dfopen)
 		{
-			(void) sm_io_close(e->e_dfp, SM_TIME_DEFAULT);
-			e->e_dfp = NULL;
+			SM_CLOSE_FP(e->e_dfp);
 			e->e_flags &= ~EF_HAS_DF;
 			dfopen = false;
 		}
diff --git a/contrib/sendmail/src/mime.c b/contrib/sendmail/src/mime.c
index 48849d6a48ea..126304cb9819 100644
--- a/contrib/sendmail/src/mime.c
+++ b/contrib/sendmail/src/mime.c
@@ -15,6 +15,7 @@
 #include 
 
 SM_RCSID("@(#)$Id: mime.c,v 8.149 2013-11-22 20:51:56 ca Exp $")
+#include 
 
 /*
 **  MIME support.
@@ -256,18 +257,18 @@ mime8to7(mci, header, e, boundaries, flags, level)
 	**	Do a recursive descent into the message.
 	*/
 
-	if (sm_strcasecmp(type, "multipart") == 0 &&
+	if (SM_STRCASEEQ(type, "multipart") &&
 	    (!bitset(M87F_NO8BIT, flags) || bitset(M87F_NO8TO7, flags)) &&
 	    !bitset(EF_TOODEEP, e->e_flags)
 	   )
 	{
 
-		if (sm_strcasecmp(subtype, "digest") == 0)
+		if (SM_STRCASEEQ(subtype, "digest"))
 			flags |= M87F_DIGEST;
 
 		for (i = 0; i < argc; i++)
 		{
-			if (sm_strcasecmp(argv[i].a_field, "boundary") == 0)
+			if (SM_STRCASEEQ(argv[i].a_field, "boundary"))
 				break;
 		}
 		if (i >= argc || argv[i].a_value == NULL)
@@ -393,7 +394,7 @@ mime8to7(mci, header, e, boundaries, flags, level)
 	**	Class 's' is predefined to have "rfc822" only.
 	*/
 
-	if (sm_strcasecmp(type, "message") == 0)
+	if (SM_STRCASEEQ(type, "message"))
 	{
 		if (!wordinclass(subtype, 's') ||
 		    bitset(EF_TOODEEP, e->e_flags))
@@ -493,7 +494,7 @@ mime8to7(mci, header, e, boundaries, flags, level)
 			type == NULL ? "[none]" : type,
 			subtype == NULL ? "[none]" : subtype);
 	}
-	if (cte != NULL && sm_strcasecmp(cte, "binary") == 0)
+	if (cte != NULL && SM_STRCASEEQ(cte, "binary"))
 		sectionsize = sectionhighbits;
 	linelen = 0;
 	bp = buf;
@@ -902,9 +903,9 @@ mimeboundary(line, boundaries)
 
 	/* strip off trailing whitespace */
 	while (i > 0 && (line[i - 1] == ' ' || line[i - 1] == '\t'
-#if _FFR_MIME_CR_OK
+# if _FFR_MIME_CR_OK
 		|| line[i - 1] == '\r'
-#endif
+# endif
 	       ))
 		i--;
 	savec = line[i];
@@ -1095,7 +1096,7 @@ mime7to8(mci, header, e)
 	*/
 
 	pxflags = PXLF_MAPFROM;
-	if (sm_strcasecmp(cte, "base64") == 0)
+	if (SM_STRCASEEQ(cte, "base64"))
 	{
 		int c1, c2, c3, c4;
 
@@ -1132,16 +1133,16 @@ mime7to8(mci, header, e)
 			c1 = CHAR64(c1);
 			c2 = CHAR64(c2);
 
-#if MIME7TO8_OLD
-#define CHK_EOL if (*--fbufp != '\n' || (fbufp > fbuf && *--fbufp != '\r')) \
+# if MIME7TO8_OLD
+#  define CHK_EOL if (*--fbufp != '\n' || (fbufp > fbuf && *--fbufp != '\r')) \
 			++fbufp;
-#else /* MIME7TO8_OLD */
-#define CHK_EOL if (*--fbufp != '\n' || (fbufp > fbuf && *--fbufp != '\r')) \
+# else /* MIME7TO8_OLD */
+#  define CHK_EOL if (*--fbufp != '\n' || (fbufp > fbuf && *--fbufp != '\r')) \
 		{					\
 			++fbufp;			\
 			pxflags |= PXLF_NOADDEOL;	\
 		}
-#endif /* MIME7TO8_OLD */
+# endif /* MIME7TO8_OLD */
 
 #define PUTLINE64	\
 	do		\
diff --git a/contrib/sendmail/src/parseaddr.c b/contrib/sendmail/src/parseaddr.c
index 58dde0f92465..f73a2688309f 100644
--- a/contrib/sendmail/src/parseaddr.c
+++ b/contrib/sendmail/src/parseaddr.c
@@ -16,6 +16,7 @@
 SM_RCSID("@(#)$Id: parseaddr.c,v 8.407 2013-11-22 20:51:56 ca Exp $")
 
 #include 
+#include 
 #include "map.h"
 
 static void	allocaddr __P((ADDRESS *, int, char *, ENVELOPE *));
@@ -42,7 +43,7 @@ static bool	hasctrlchar __P((register char *, bool, bool));
 **	of 'berkeley' -- to be transmitted over the arpanet.
 **
 **	Parameters:
-**		addr -- the address to parse.
+**		addr -- the address to parse. [i]
 **		a -- a pointer to the address descriptor buffer.
 **			If NULL, an address will be created.
 **		flags -- describe detail for parsing.  See RF_ definitions
@@ -85,6 +86,14 @@ parseaddr(addr, a, flags, delim, delimptr, e, isrcpt)
 	/*
 	**  Initialize and prescan address.
 	*/
+#if _FFR_8BITENVADDR
+	if (bitset(RF_IS_EXT, flags) && addr != NULL)
+	{
+		int len = 0;
+
+		addr = quote_internal_chars(addr, NULL, &len, NULL);
+	}
+#endif
 
 	e->e_to = addr;
 	if (tTd(20, 1))
@@ -145,6 +154,13 @@ parseaddr(addr, a, flags, delim, delimptr, e, isrcpt)
 	*/
 
 	a = buildaddr(pvp, a, flags, e);
+#if _FFR_8BITENVADDR
+	{
+		int len = 0;
+
+		a->q_user = quote_internal_chars(a->q_user, NULL, &len, e->e_rpool); /* EAI: ok */
+	}
+#endif
 
 	if (hasctrlchar(a->q_user, isrcpt, true))
 	{
@@ -239,6 +255,7 @@ parseaddr(addr, a, flags, delim, delimptr, e, isrcpt)
 
 	return a;
 }
+
 /*
 **  INVALIDADDR -- check for address containing characters used for macros
 **
@@ -264,7 +281,7 @@ invalidaddr(addr, delimptr, isrcpt)
 	bool result = false;
 	char savedelim = '\0';
 	char *b = addr;
-	int len = 0;
+	XLENDECL
 
 	if (delimptr != NULL)
 	{
@@ -275,7 +292,7 @@ invalidaddr(addr, delimptr, isrcpt)
 	}
 	for (; *addr != '\0'; addr++)
 	{
-#if !_FFR_EAI
+#if !USE_EAI
 		if (!EightBitAddrOK && (*addr & 0340) == 0200)
 		{
 			setstat(EX_USAGE);
@@ -283,18 +300,22 @@ invalidaddr(addr, delimptr, isrcpt)
 			*addr = BAD_CHAR_REPLACEMENT;
 		}
 #endif
-		if (++len > MAXNAME - 1)
+		XLEN(*addr);
+		if (xlen > MAXNAME - 1)	/* EAI:ok */
 		{
 			char saved = *addr;
 
 			*addr = '\0';
 			usrerr("553 5.1.0 Address \"%s\" too long (%d bytes max)",
-			       b, MAXNAME - 1);
+			       b, MAXNAME - 1);	/* EAI:ok */
 			*addr = saved;
 			result = true;
 			goto delim;
 		}
 	}
+#if USE_EAI
+	/* check for valid UTF8 string? */
+#endif
 	if (result)
 	{
 		if (isrcpt)
@@ -309,6 +330,7 @@ invalidaddr(addr, delimptr, isrcpt)
 		*delimptr = savedelim;	/* restore old character at delimptr */
 	return result;
 }
+
 /*
 **  HASCTRLCHAR -- check for address containing meta-characters
 **
@@ -327,6 +349,9 @@ invalidaddr(addr, delimptr, isrcpt)
 **		true -- if the address has any "weird" characters or
 **			non-printable characters or if a quote is unbalanced.
 **		false -- otherwise.
+**
+**	Side Effects:
+**		Might invoke shorten_rfc822_string() to change addr in place.
 */
 
 static bool
@@ -335,21 +360,22 @@ hasctrlchar(addr, isrcpt, complain)
 	bool isrcpt, complain;
 {
 	bool quoted = false;
-	int len = 0;
 	char *result = NULL;
 	char *b = addr;
+	XLENDECL
 
 	if (addr == NULL)
 		return false;
 	for (; *addr != '\0'; addr++)
 	{
-		if (++len > MAXNAME - 1)
+		XLEN(*addr);
+		if (xlen > MAXNAME - 1)	/* EAI:ok */
 		{
 			if (complain)
 			{
-				(void) shorten_rfc822_string(b, MAXNAME - 1);
+				(void) shorten_rfc822_string(b, MAXNAME - 1);	/* EAI:ok */
 				usrerr("553 5.1.0 Address \"%s\" too long (%d bytes max)",
-				       b, MAXNAME - 1);
+				       b, MAXNAME - 1);	/* EAI:ok */
 				return true;
 			}
 			result = "too long";
@@ -372,15 +398,13 @@ hasctrlchar(addr, isrcpt, complain)
 				break;
 			}
 		}
-#if !_FFR_EAI
-		if (!EightBitAddrOK && (*addr & 0340) == 0200)
+		if (!SMTPUTF8 && !EightBitAddrOK && (*addr & 0340) == 0200)
 		{
 			setstat(EX_USAGE);
 			result = "8-bit character";
 			*addr = BAD_CHAR_REPLACEMENT;
 			continue;
 		}
-#endif
 	}
 	if (quoted)
 		result = "unbalanced quote"; /* unbalanced quote */
@@ -395,6 +419,7 @@ hasctrlchar(addr, isrcpt, complain)
 	}
 	return result != NULL;
 }
+
 /*
 **  ALLOCADDR -- do local allocations of address on demand.
 **
@@ -690,6 +715,7 @@ prescan(addr, delim, pvpbuf, pvpbsize, delimptr, toktab, ignore)
 	char *saveto = CurEnv->e_to;
 	static char *av[MAXATOM + 1];
 	static bool firsttime = true;
+	XLENDECL
 
 	if (firsttime)
 	{
@@ -744,6 +770,7 @@ prescan(addr, delim, pvpbuf, pvpbsize, delimptr, toktab, ignore)
 	{
 		/* read a token */
 		tok = q;
+		XLENRESET;
 		for (;;)
 		{
 			/* store away any old lookahead character */
@@ -754,6 +781,8 @@ prescan(addr, delim, pvpbuf, pvpbsize, delimptr, toktab, ignore)
 				{
 	addrtoolong:
 					usrerr("553 5.1.1 Address too long");
+
+					/* ilenx()? */
 					if (strlen(addr) > MAXNAME)
 						addr[MAXNAME] = '\0';
 	returnnull:
@@ -764,6 +793,8 @@ prescan(addr, delim, pvpbuf, pvpbsize, delimptr, toktab, ignore)
 						*delimptr = p;
 					}
 					CurEnv->e_to = saveto;
+					if (tTd(22, 12))
+						sm_dprintf("prescan: ==> NULL\n");
 					return NULL;
 				}
 
@@ -773,6 +804,7 @@ prescan(addr, delim, pvpbuf, pvpbsize, delimptr, toktab, ignore)
 				    !EightBitAddrOK)
 					c &= 0x7f;
 #endif /* !ALLOW_255 */
+				XLEN(c);
 				*q++ = c;
 			}
 
@@ -838,6 +870,7 @@ prescan(addr, delim, pvpbuf, pvpbsize, delimptr, toktab, ignore)
 					if (q >= &pvpbuf[pvpbsize - 5])
 						goto addrtoolong;
 					*q++ = '\\';
+					XLEN('\\');
 					continue;
 				}
 			}
@@ -943,7 +976,7 @@ prescan(addr, delim, pvpbuf, pvpbsize, delimptr, toktab, ignore)
 				usrerr("553 5.1.0 prescan: too many tokens");
 				goto returnnull;
 			}
-			if (q - tok > MAXNAME)
+			if (xlen > MAXNAME)	/* EAI:ok */
 			{
 				usrerr("553 5.1.0 prescan: token too long");
 				goto returnnull;
@@ -995,7 +1028,7 @@ prescan(addr, delim, pvpbuf, pvpbsize, delimptr, toktab, ignore)
 **	and try over again.
 **
 **	Parameters:
-**		pvp -- pointer to token vector.
+**		pvp -- pointer to token vector. [i]
 **		ruleset -- the ruleset to use for rewriting.
 **		reclevel -- recursion level (to catch loops).
 **		e -- the current envelope.
@@ -1241,7 +1274,7 @@ rewrite(pvp, ruleset, reclevel, e, maxatom)
 
 			  default:
 				/* must have exact match */
-				if (sm_strcasecmp(rp, ap))
+				if (!SM_STRCASEEQ(rp, ap))
 					goto backup;
 				avp++;
 				break;
@@ -1406,7 +1439,7 @@ rewrite(pvp, ruleset, reclevel, e, maxatom)
 						sm_dprintf("rewrite: RHS $&{%s} => \"%s\"\n",
 							macname(rp[1]),
 							mval == NULL ? "(NULL)" : mval);
-					if (mval == NULL || *mval == '\0')
+					if (SM_IS_EMPTY(mval))
 						continue;
 
 					/* save the remainder of the input */
@@ -1543,7 +1576,7 @@ rewrite(pvp, ruleset, reclevel, e, maxatom)
 				{
 					cataddr(xpvp, NULL, replac,
 						&pvpbuf[sizeof(pvpbuf)] - replac,
-						'\0', false);
+						'\0', true);
 					if (arg_rvp <
 					    &argvect[MAX_MAP_ARGS - 1])
 						*++arg_rvp = replac;
@@ -1567,7 +1600,7 @@ rewrite(pvp, ruleset, reclevel, e, maxatom)
 			{
 				cataddr(xpvp, NULL, replac,
 					&pvpbuf[sizeof(pvpbuf)] - replac,
-					'\0', false);
+					'\0', true);
 				if (arg_rvp < &argvect[MAX_MAP_ARGS - 1])
 					*++arg_rvp = replac;
 			}
@@ -1670,6 +1703,7 @@ rewrite(pvp, ruleset, reclevel, e, maxatom)
 	}
 	return rstat;
 }
+
 /*
 **  CALLSUBR -- call subroutines in rewrite vector
 **
@@ -1786,8 +1820,8 @@ callsubr(pvp, reclevel, e)
 **
 **	Parameters:
 **		smap -- the map to use for the lookup.
-**		key -- the key to look up.
-**		argvect -- arguments to pass to the map lookup.
+**		key -- the key to look up. [x]
+**		argvect -- arguments to pass to the map lookup. [x]
 **		pstat -- a pointer to an integer in which to store the
 **			status from the lookup.
 **		e -- the current envelope.
@@ -1979,8 +2013,8 @@ buildaddr(tv, a, flags, e)
 	register char *p;
 	char *mname;
 	char **hostp;
-	char hbuf[MAXNAME + 1];
-	static char ubuf[MAXNAME + 2];
+	char hbuf[MAXNAME + 1];	/* EAI:ok */
+	static char ubuf[MAXNAME_I + 2];
 
 	if (tTd(24, 5))
 	{
@@ -2047,12 +2081,12 @@ buildaddr(tv, a, flags, e)
 	if (tv == hostp)
 		hostp = NULL;
 	else if (hostp != NULL)
-		cataddr(hostp, tv - 1, hbuf, sizeof(hbuf), '\0', false);
+		cataddr(hostp, tv - 1, hbuf, sizeof(hbuf), '\0', true);
 	cataddr(++tv, NULL, ubuf, sizeof(ubuf), ' ', false);
 	--maxatom;
 
 	/* save away the host name */
-	if (sm_strcasecmp(mname, "error") == 0)
+	if (SM_STRCASEEQ(mname, "error"))
 	{
 		/* Set up triplet for use by -bv */
 		a->q_mailer = &errormailer;
@@ -2077,7 +2111,7 @@ buildaddr(tv, a, flags, e)
 			else
 			{
 				for (ep = ErrorCodes; ep->ec_name != NULL; ep++)
-					if (sm_strcasecmp(ep->ec_name, hbuf) == 0)
+					if (SM_STRCASEEQ(ep->ec_name, hbuf))
 						break;
 				setstat(ep->ec_code);
 			}
@@ -2123,7 +2157,7 @@ buildaddr(tv, a, flags, e)
 
 	for (mp = Mailer; (m = *mp++) != NULL; )
 	{
-		if (sm_strcasecmp(m->m_name, mname) == 0)
+		if (SM_STRCASEEQ(m->m_name, mname))
 			break;
 	}
 	if (m == NULL)
@@ -2198,9 +2232,9 @@ buildaddr(tv, a, flags, e)
 	*/
 
 	if (a->q_host != NULL && !bitnset(M_HST_UPPER, m->m_flags))
-		makelower(a->q_host);
+		makelower_a(&a->q_host, e->e_rpool);
 	if (!bitnset(M_USR_UPPER, m->m_flags))
-		makelower(a->q_user);
+		makelower_a(&a->q_user, e->e_rpool);
 
 	if (tTd(24, 6))
 	{
@@ -2214,7 +2248,7 @@ buildaddr(tv, a, flags, e)
 **  CATADDR -- concatenate pieces of addresses (putting in  subs)
 **
 **	Parameters:
-**		pvp -- parameter vector to rebuild.
+**		pvp -- parameter vector to rebuild. [i]
 **		evp -- last parameter to include.  Can be NULL to
 **			use entire pvp.
 **		buf -- buffer to build the string into.
@@ -2223,7 +2257,7 @@ buildaddr(tv, a, flags, e)
 **			'\0': SpaceSub.
 **			NOSPACESEP: no separator
 **		external -- convert to external form?
-**			(no metacharacters; METAQUOTEs removed, see below)
+**			(undo "meta quoting")
 **
 **	Returns:
 **		none.
@@ -2242,9 +2276,6 @@ buildaddr(tv, a, flags, e)
 **	The cataddr routine needs to be aware of whether it is producing
 **	an internal or external form as output (it only takes internal
 **	form as input).
-**
-**	The parseaddr routine has a similar issue on input, but that
-**	is flagged on the basis of which token table is passed in.
 */
 
 void
@@ -2453,7 +2484,6 @@ static struct qflags	AddressFlags[] =
 	{ "QBYNRELAY",		QBYNRELAY	},
 	{ "QINTBCC",		QINTBCC		},
 	{ "QDYNMAILER",		QDYNMAILER	},
-	{ "QRCPTOK",		QRCPTOK		},
 	{ "QSECURE",		QSECURE		},
 	{ "QTHISPASS",		QTHISPASS	},
 	{ "QRCPTOK",		QRCPTOK		},
@@ -2644,11 +2674,12 @@ emptyaddr(a)
 	return a->q_paddr == NULL || strcmp(a->q_paddr, "<>") == 0 ||
 	       a->q_user == NULL || strcmp(a->q_user, "<>") == 0;
 }
+
 /*
 **  REMOTENAME -- return the name relative to the current mailer
 **
 **	Parameters:
-**		name -- the name to translate.
+**		name -- the name to translate. [i]
 **		m -- the mailer that we want to do rewriting relative to.
 **		flags -- fine tune operations.
 **		pstat -- pointer to status word.
@@ -2656,10 +2687,7 @@ emptyaddr(a)
 **
 **	Returns:
 **		the text string representing this address relative to
-**			the receiving mailer.
-**
-**	Side Effects:
-**		none.
+**			the receiving mailer. [i]
 **
 **	Warnings:
 **		The text string returned is tucked away locally;
@@ -2678,8 +2706,8 @@ remotename(name, m, flags, pstat, e)
 	char *SM_NONVOLATILE fancy;
 	char *oldg;
 	int rwset;
-	static char buf[MAXNAME + 1];
-	char lbuf[MAXNAME + 1];
+	static char buf[MAXNAME_I + 1];
+	char lbuf[MAXNAME_I + 1];
 	char pvpbuf[PSBUFSIZE];
 	char addrtype[4];
 
@@ -3096,6 +3124,7 @@ dequote_map(map, name, av, statp)
 **
 **	Returns:
 **		EX_OK -- if the rwset doesn't resolve to $#error
+**			or is not defined
 **		else -- the failure status (message printed)
 */
 
diff --git a/contrib/sendmail/src/queue.c b/contrib/sendmail/src/queue.c
index 526b40469486..42ac3eb603fd 100644
--- a/contrib/sendmail/src/queue.c
+++ b/contrib/sendmail/src/queue.c
@@ -16,19 +16,23 @@
 
 SM_RCSID("@(#)$Id: queue.c,v 8.1000 2013-11-22 20:51:56 ca Exp $")
 
+#include 
 #include 
+#if _FFR_DMTRIGGER
+# include 
+#endif
 
-# define RELEASE_QUEUE	(void) 0
-# define ST_INODE(st)	(st).st_ino
+#define RELEASE_QUEUE	(void) 0
+#define ST_INODE(st)	(st).st_ino
 
-#  define sm_file_exists(errno) ((errno) == EEXIST)
+#define sm_file_exists(errno) ((errno) == EEXIST)
 
-# if HASFLOCK && defined(O_EXLOCK)
-#   define SM_OPEN_EXLOCK 1
-#   define TF_OPEN_FLAGS (O_CREAT|O_WRONLY|O_EXCL|O_EXLOCK)
-# else
-#  define TF_OPEN_FLAGS (O_CREAT|O_WRONLY|O_EXCL)
-# endif
+#if HASFLOCK && defined(O_EXLOCK)
+# define SM_OPEN_EXLOCK 1
+# define TF_OPEN_FLAGS (O_CREAT|O_WRONLY|O_EXCL|O_EXLOCK)
+#else
+# define TF_OPEN_FLAGS (O_CREAT|O_WRONLY|O_EXCL)
+#endif
 
 #ifndef SM_OPEN_EXLOCK
 # define SM_OPEN_EXLOCK 0
@@ -43,6 +47,7 @@ SM_RCSID("@(#)$Id: queue.c,v 8.1000 2013-11-22 20:51:56 ca Exp $")
 **	QF_VERSION == 8 is  sendmail 8.13
 */
 
+/* XREF: op.me: QUEUE FILE FORMAT: V */
 #define QF_VERSION	8	/* version number of this queue format */
 
 static char	queue_letter __P((ENVELOPE *, int));
@@ -141,22 +146,16 @@ static int	sm_strshufflecmp __P((char *, char *));
 static void	init_shuffle_alphabet __P(());
 #endif
 
-/*
-**  Note: workcmpf?() don't use a prototype because it will cause a conflict
-**  with the qsort() call (which expects something like
-**  int (*compar)(const void *, const void *), not (WORK *, WORK *))
-*/
-
-static int	workcmpf0();
-static int	workcmpf1();
-static int	workcmpf2();
-static int	workcmpf3();
-static int	workcmpf4();
+static int	workcmpf0 __P((const void *, const void *));
+static int	workcmpf1 __P((const void *, const void *));
+static int	workcmpf2 __P((const void *, const void *));
+static int	workcmpf3 __P((const void *, const void *));
+static int	workcmpf4 __P((const void *, const void *));
 static int	randi = 3;	/* index for workcmpf5() */
-static int	workcmpf5();
-static int	workcmpf6();
+static int	workcmpf5 __P((const void *, const void *));
+static int	workcmpf6 __P((const void *, const void *));
 #if _FFR_RHS
-static int	workcmpf7();
+static int	workcmpf7 __P((const void *, const void *));
 #endif
 
 #if RANDOMSHIFT
@@ -333,8 +332,10 @@ hash_q(p, h)
 **
 **	Parameters:
 **		e -- the envelope to queue up.
-**		announce -- if true, tell when you are queueing up.
-**		msync -- if true, then fsync() if SuperSafe interactive mode.
+**		flags -- QUP_FL_*:
+**			QUP_FL_ANNOUNCE -- tell when queueing up.
+**			QUP_FL_MSYNC -- fsync() if SuperSafe interactive mode.
+**			QUP_FL_UNLOCK -- invoke unlockqueue().
 **
 **	Returns:
 **		none.
@@ -345,10 +346,9 @@ hash_q(p, h)
 */
 
 void
-queueup(e, announce, msync)
+queueup(e, flags)
 	register ENVELOPE *e;
-	bool announce;
-	bool msync;
+	unsigned int flags;
 {
 	register SM_FILE_T *tfp;
 	register HDR *h;
@@ -378,7 +378,9 @@ queueup(e, announce, msync)
 			if (bitset(S_IWGRP, QueueFileMode))		\
 				(void) umask(oldumask);			\
 		} while (0)
-
+#define QUP_ANNOUNCE bitset(QUP_FL_ANNOUNCE, flags)
+#define QUP_MSYNC bitset(QUP_FL_MSYNC, flags)
+#define QUP_UNLOCK bitset(QUP_FL_UNLOCK, flags)
 
 	newid = (e->e_id == NULL) || !bitset(EF_INQUEUE, e->e_flags);
 	(void) sm_strlcpy(tf, queuename(e, NEWQFL_LETTER), sizeof(tf));
@@ -523,7 +525,7 @@ queueup(e, announce, msync)
 			       queuename(e, DATAFL_LETTER), (long) geteuid());
 		}
 		if (e->e_dfp != NULL &&
-		    SuperSafe == SAFE_INTERACTIVE && msync)
+		    SuperSafe == SAFE_INTERACTIVE && QUP_MSYNC)
 		{
 			if (tTd(40,32))
 				sm_syslog(LOG_INFO, e->e_id,
@@ -578,7 +580,8 @@ queueup(e, announce, msync)
 
 		if (SuperSafe == SAFE_REALLY ||
 		    SuperSafe == SAFE_REALLY_POSTMILTER ||
-		    (SuperSafe == SAFE_INTERACTIVE && msync))
+		    (SuperSafe == SAFE_INTERACTIVE &&
+		     QUP_MSYNC))
 		{
 			if (tTd(40,32))
 				sm_syslog(LOG_INFO, e->e_id,
@@ -673,7 +676,7 @@ queueup(e, announce, msync)
 		*p++ = 'n';
 	if (bitset(EF_SPLIT, e->e_flags))
 		*p++ = 's';
-#if _FFR_EAI
+#if USE_EAI
 	if (e->e_smtputf8)
 		*p++ = 'e';
 #endif
@@ -751,7 +754,7 @@ queueup(e, announce, msync)
 		(void) sm_io_putc(tfp, SM_TIME_DEFAULT, ':');
 		(void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "%s\n",
 				     denlstring(q->q_paddr, true, false));
-		if (announce)
+		if (QUP_ANNOUNCE)
 		{
 			char *tag = "queued";
 
@@ -890,7 +893,7 @@ queueup(e, announce, msync)
 	if (sm_io_flush(tfp, SM_TIME_DEFAULT) != 0 ||
 	    ((SuperSafe == SAFE_REALLY ||
 	      SuperSafe == SAFE_REALLY_POSTMILTER ||
-	      (SuperSafe == SAFE_INTERACTIVE && msync)) &&
+	      (SuperSafe == SAFE_INTERACTIVE && QUP_MSYNC)) &&
 	     fsync(sm_io_getinfo(tfp, SM_IO_WHAT_FD, NULL)) < 0) ||
 	    sm_io_error(tfp))
 	{
@@ -980,7 +983,35 @@ queueup(e, announce, msync)
 
 	if (tTd(40, 1))
 		sm_dprintf("<<<<< done queueing %s <<<<<\n\n", e->e_id);
+#if _FFR_DMTRIGGER
+	if (SM_TRIGGER == e->e_sendmode && !SM_IS_EMPTY(e->e_id))
+	{
+		char buf[64];
+
+		if (QUP_UNLOCK)
+			unlockqueue(e);
+		(void) sm_snprintf(buf, sizeof(buf), "N:%d:%d:%s",
+			e->e_qgrp, e->e_qdir, e->e_id);
+		i = sm_notify_snd(buf, strlen(buf));
+		sm_syslog(LOG_DEBUG, e->e_id, "queueup: mode=%c, id=%s, unlock=%d, snd=%d",
+			e->e_sendmode, e->e_id, QUP_UNLOCK, i);
+		if (i < 0)
+		{
+			/*
+			**  What to do about this?
+			**  Notify caller (change return type)?
+			**  A queue runner will eventually pick it up.
+			*/
+
+			sm_syslog(LOG_ERR, e->e_id, "queueup: notify_snd=%d",
+				i);
+		}
+	}
+#endif /* _FFR_DMTRIGGER */
 	return;
+#undef QUP_ANNOUNCE
+#undef QUP_MSYNC
+#undef QUP_UNLOCK
 }
 
 /*
@@ -1517,7 +1548,7 @@ runqueue(forkflag, verbose, persistent, runall)
 		int wasblocked;
 
 		/*
-		**  If MaxQueueChildren active then test whether the start
+		**  If MaxQueueChildren is active then test whether the start
 		**  of the next queue group's additional queue runners (maximum)
 		**  will result in MaxQueueChildren being exceeded.
 		**
@@ -1529,9 +1560,13 @@ runqueue(forkflag, verbose, persistent, runall)
 #if _FFR_QUEUE_SCHED_DBG
 		if (tTd(69, 10))
 			sm_syslog(LOG_INFO, NOQID,
-				"rq: curnum=%d, MaxQueueChildren=%d, CurRunners=%d, WorkGrp[curnum].wg_maxact=%d",
-				curnum, MaxQueueChildren, CurRunners,
-				WorkGrp[curnum].wg_maxact);
+				"rq: i=%d, curnum=%d, MaxQueueChildren=%d, CurRunners=%d, WorkGrp[curnum].wg_maxact=%d, skip=%d",
+				i, curnum, MaxQueueChildren, CurRunners,
+				WorkGrp[curnum].wg_maxact,
+				MaxQueueChildren > 0 &&
+				CurRunners + WorkGrp[curnum].wg_maxact >
+					MaxQueueChildren
+				);
 #endif /* _FFR_QUEUE_SCHED_DBG */
 		if (MaxQueueChildren > 0 &&
 		    CurRunners + WorkGrp[curnum].wg_maxact > MaxQueueChildren)
@@ -1641,8 +1676,8 @@ skip_domains(skip)
 			if (WorkQ->w_host != NULL &&
 			    WorkQ->w_next->w_host != NULL)
 			{
-				if (sm_strcasecmp(WorkQ->w_host,
-						WorkQ->w_next->w_host) != 0)
+				if (!SM_STRCASEEQ(WorkQ->w_host,
+						WorkQ->w_next->w_host))
 					n++;
 			}
 			else
@@ -1718,7 +1753,7 @@ runner_work(e, sequenceno, didfork, skip, njobs)
 		{
 			oldgroup = sm_heap_group();
 			sm_heap_newgroup();
-			sm_dprintf("run_queue_group() heap group #%d\n",
+			sm_dprintf("runner_work(): heap group #%d\n",
 				sm_heap_group());
 		}
 #endif /* SM_HEAP_CHECK */
@@ -1756,9 +1791,8 @@ runner_work(e, sequenceno, didfork, skip, njobs)
 				if (WorkQ->w_host != NULL &&
 				    WorkQ->w_next->w_host != NULL)
 				{
-					if (sm_strcasecmp(WorkQ->w_host,
-							WorkQ->w_next->w_host)
-								!= 0)
+					if (!SM_STRCASEEQ(WorkQ->w_host,
+							WorkQ->w_next->w_host))
 						seqjump = skip_domains(skip);
 					else
 						WorkQ = WorkQ->w_next;
@@ -1779,6 +1813,7 @@ runner_work(e, sequenceno, didfork, skip, njobs)
 		}
 		else
 #endif /* _FFR_SKIP_DOMAINS */
+		/* "else" in #if code above */
 		{
 			for (n = 0; n < skip && WorkQ != NULL; n++)
 				WorkQ = WorkQ->w_next;
@@ -1846,9 +1881,9 @@ runner_work(e, sequenceno, didfork, skip, njobs)
 			}
 			if (tTd(63, 100))
 				sm_syslog(LOG_DEBUG, NOQID,
-					  "runqueue %s dowork(%s)",
+					  "runqueue %s dowork(%s) pid=%d",
 					  qid_printqueue(w->w_qgrp, w->w_qdir),
-					  w->w_name + 2);
+					  w->w_name + 2, (int) CurrentPid);
 
 			(void) dowork(w->w_qgrp, w->w_qdir, w->w_name + 2,
 				      ForkQueueRuns, false, e);
@@ -1869,7 +1904,7 @@ runner_work(e, sequenceno, didfork, skip, njobs)
 			int sl;
 
 			sl = tTdlevel(76) - 100;
-			sm_dprintf("run_work_group: sleep=%d\n", sl);
+			sm_dprintf("runner_work(): sleep=%d\n", sl);
 			sleep(sl);
 		}
 #endif
@@ -2078,7 +2113,7 @@ run_work_group(wgrp, flags)
 
 	/*
 	**  Here is where we choose the queue group from the work group.
-	**  The caller of the "domorework" label must setup a new envelope.
+	**  The caller of the "domorework" label must set up a new envelope.
 	*/
 
 	endgrp = WorkGrp[wgrp].wg_curqgrp; /* to not spin endlessly */
@@ -2143,7 +2178,6 @@ run_work_group(wgrp, flags)
 	**  Start making passes through the queue.
 	**	First, read and sort the entire queue.
 	**	Then, process the work in that order.
-	**		But if you take too long, start over.
 	*/
 
 	for (i = 0; i < Queue[qgrp]->qg_numqueues; i++)
@@ -2170,7 +2204,6 @@ run_work_group(wgrp, flags)
 	njobs = sortq(Queue[qgrp]->qg_maxlist);
 	Queue[qgrp]->qg_curnum = qdir; /* update */
 
-
 	if (!Verbose && bitnset(QD_FORK, Queue[qgrp]->qg_flags))
 	{
 		int loop, maxrunners;
@@ -2224,11 +2257,10 @@ run_work_group(wgrp, flags)
 				mci_flush(false, NULL);
 #if _FFR_SKIP_DOMAINS
 				if (QueueSortOrder == QSO_BYHOST)
-				{
 					sequenceno += skip_domains(1);
-				}
 				else
 #endif /* _FFR_SKIP_DOMAINS */
+				/* "else" in #if code above */
 				{
 					/* for the skip */
 					WorkQ = WorkQ->w_next;
@@ -2329,6 +2361,7 @@ run_work_group(wgrp, flags)
 	/* free memory allocated by newenvelope() above */
 	sm_rpool_free(rpool);
 	QueueEnvelope.e_rpool = NULL;
+	QueueEnvelope.e_id = NULL; /* might be allocated from rpool */
 
 	/* Are there still more queues in the work group to process? */
 	if (endgrp != WorkGrp[wgrp].wg_curqgrp)
@@ -2609,7 +2642,7 @@ gatherq(qgrp, qdir, doall, full, more, pnentries)
 	{
 		SM_FILE_T *cf;
 		int qfver = 0;
-		char lbuf[MAXNAME + 1];
+		char lbuf[MAXNAME_I + 1];
 		struct stat sbuf;
 
 		if (tTd(41, 50))
@@ -2844,13 +2877,15 @@ gatherq(qgrp, qdir, doall, full, more, pnentries)
 				if (w->w_host == NULL &&
 				    (p = strrchr(&lbuf[1], '@')) != NULL)
 				{
+					char *str;
 #if _FFR_RHS
 					if (QueueSortOrder == QSO_BYSHUFFLE)
 						w->w_host = newstr(&p[1]);
 					else
 #endif
 						w->w_host = strrev(&p[1]);
-					makelower(w->w_host);
+					str = makelower_a(&w->w_host, NULL);
+					ASSIGN_IFDIFF(w->w_host, str);
 					i &= ~NEED_H;
 				}
 				if (QueueLimitRecipient == NULL)
@@ -2960,6 +2995,8 @@ gatherq(qgrp, qdir, doall, full, more, pnentries)
 
 	if (pnentries != NULL)
 		*pnentries = nentries;
+	if (tTd(41, 2))
+		sm_dprintf("gatherq: %s=%d\n", qd, i);
 	return i;
 }
 /*
@@ -3046,8 +3083,8 @@ sortq(max)
 					WorkList[i].w_lock = true;
 				else if (WorkList[i].w_host != NULL &&
 					 w->w_host != NULL &&
-					 sm_strcasecmp(WorkList[i].w_host,
-						       w->w_host) == 0)
+					 SM_STRCASEEQ(WorkList[i].w_host,
+						       w->w_host))
 					WorkList[i].w_lock = true;
 				else
 					break;
@@ -3237,23 +3274,22 @@ grow_wlist(qgrp, qdir)
 **  WORKCMPF0 -- simple priority-only compare function.
 **
 **	Parameters:
-**		a -- the first argument.
-**		b -- the second argument.
+**		av -- the first argument.
+**		bv -- the second argument.
 **
 **	Returns:
-**		-1 if a < b
-**		 0 if a == b
-**		+1 if a > b
-**
+**		-1 if av < bv
+**		 0 if av == bv
+**		+1 if av > bv
 */
 
 static int
-workcmpf0(a, b)
-	register WORK *a;
-	register WORK *b;
+workcmpf0(av, bv)
+	const void *av;
+	const void *bv;
 {
-	long pa = a->w_pri;
-	long pb = b->w_pri;
+	long pa = ((WORK *)av)->w_pri;
+	long pb = ((WORK *)bv)->w_pri;
 
 	if (pa == pb)
 		return 0;
@@ -3268,22 +3304,23 @@ workcmpf0(a, b)
 **	Sorts on host name, lock status, and priority in that order.
 **
 **	Parameters:
-**		a -- the first argument.
-**		b -- the second argument.
+**		av -- the first argument.
+**		bv -- the second argument.
 **
 **	Returns:
-**		<0 if a < b
-**		 0 if a == b
-**		>0 if a > b
-**
+**		<0 if av < bv
+**		 0 if av == bv
+**		>0 if av > bv
 */
 
 static int
-workcmpf1(a, b)
-	register WORK *a;
-	register WORK *b;
+workcmpf1(av, bv)
+	const void *av;
+	const void *bv;
 {
 	int i;
+	WORK *a = (WORK *)av;
+	WORK *b = (WORK *)bv;
 
 	/* host name */
 	if (a->w_host != NULL && b->w_host == NULL)
@@ -3307,22 +3344,23 @@ workcmpf1(a, b)
 **	Sorts on lock status, host name, and priority in that order.
 **
 **	Parameters:
-**		a -- the first argument.
-**		b -- the second argument.
+**		av -- the first argument.
+**		bv -- the second argument.
 **
 **	Returns:
-**		<0 if a < b
-**		 0 if a == b
-**		>0 if a > b
-**
+**		<0 if av < bv
+**		 0 if av == bv
+**		>0 if av > bv
 */
 
 static int
-workcmpf2(a, b)
-	register WORK *a;
-	register WORK *b;
+workcmpf2(av, bv)
+	const void *av;
+	const void *bv;
 {
 	int i;
+	WORK *a = (WORK *)av;
+	WORK *b = (WORK *)bv;
 
 	/* lock status */
 	if (a->w_lock != b->w_lock)
@@ -3344,21 +3382,23 @@ workcmpf2(a, b)
 **  WORKCMPF3 -- simple submission-time-only compare function.
 **
 **	Parameters:
-**		a -- the first argument.
-**		b -- the second argument.
+**		av -- the first argument.
+**		bv -- the second argument.
 **
 **	Returns:
-**		-1 if a < b
-**		 0 if a == b
-**		+1 if a > b
-**
+**		-1 if av < bv
+**		 0 if av == bv
+**		+1 if av > bv
 */
 
 static int
-workcmpf3(a, b)
-	register WORK *a;
-	register WORK *b;
+workcmpf3(av, bv)
+	const void *av;
+	const void *bv;
 {
+	WORK *a = (WORK *)av;
+	WORK *b = (WORK *)bv;
+
 	if (a->w_ctime > b->w_ctime)
 		return 1;
 	else if (a->w_ctime < b->w_ctime)
@@ -3370,29 +3410,31 @@ workcmpf3(a, b)
 **  WORKCMPF4 -- compare based on file name
 **
 **	Parameters:
-**		a -- the first argument.
-**		b -- the second argument.
+**		av -- the first argument.
+**		bv -- the second argument.
 **
 **	Returns:
-**		-1 if a < b
-**		 0 if a == b
-**		+1 if a > b
-**
+**		-1 if av < bv
+**		 0 if av == bv
+**		+1 if av > bv
 */
 
 static int
-workcmpf4(a, b)
-	register WORK *a;
-	register WORK *b;
+workcmpf4(av, bv)
+	const void *av;
+	const void *bv;
 {
+	WORK *a = (WORK *)av;
+	WORK *b = (WORK *)bv;
+
 	return strcmp(a->w_name, b->w_name);
 }
 /*
 **  WORKCMPF5 -- compare based on assigned random number
 **
 **	Parameters:
-**		a -- the first argument.
-**		b -- the second argument.
+**		av -- the first argument.
+**		bv -- the second argument.
 **
 **	Returns:
 **		randomly 1/-1
@@ -3400,10 +3442,13 @@ workcmpf4(a, b)
 
 /* ARGSUSED0 */
 static int
-workcmpf5(a, b)
-	register WORK *a;
-	register WORK *b;
+workcmpf5(av, bv)
+	const void *av;
+	const void *bv;
 {
+	WORK *a = (WORK *)av;
+	WORK *b = (WORK *)bv;
+
 	if (strlen(a->w_name) < randi || strlen(b->w_name) < randi)
 		return -1;
 	return a->w_name[randi] - b->w_name[randi];
@@ -3412,21 +3457,23 @@ workcmpf5(a, b)
 **  WORKCMPF6 -- simple modification-time-only compare function.
 **
 **	Parameters:
-**		a -- the first argument.
-**		b -- the second argument.
+**		av -- the first argument.
+**		bv -- the second argument.
 **
 **	Returns:
-**		-1 if a < b
-**		 0 if a == b
-**		+1 if a > b
-**
+**		-1 if av < bv
+**		 0 if av == bv
+**		+1 if av > bv
 */
 
 static int
-workcmpf6(a, b)
-	register WORK *a;
-	register WORK *b;
+workcmpf6(av, bv)
+	const void *av;
+	const void *bv;
 {
+	WORK *a = (WORK *)av;
+	WORK *b = (WORK *)bv;
+
 	if (a->w_mtime > b->w_mtime)
 		return 1;
 	else if (a->w_mtime < b->w_mtime)
@@ -3441,22 +3488,23 @@ workcmpf6(a, b)
 **	Sorts on lock status, host name, and priority in that order.
 **
 **	Parameters:
-**		a -- the first argument.
-**		b -- the second argument.
+**		av -- the first argument.
+**		bv -- the second argument.
 **
 **	Returns:
-**		<0 if a < b
-**		 0 if a == b
-**		>0 if a > b
-**
+**		<0 if av < bv
+**		 0 if av == bv
+**		>0 if av > bv
 */
 
 static int
-workcmpf7(a, b)
-	register WORK *a;
-	register WORK *b;
+workcmpf7(av, bv)
+	const void *av;
+	const void *bv;
 {
 	int i;
+	WORK *a = (WORK *)av;
+	WORK *b = (WORK *)bv;
 
 	/* lock status */
 	if (a->w_lock != b->w_lock)
@@ -3596,7 +3644,7 @@ dowork(qgrp, qdir, id, forkflag, requeueflag, e)
 	SM_RPOOL_T *rpool;
 
 	if (tTd(40, 1))
-		sm_dprintf("dowork(%s/%s)\n", qid_printqueue(qgrp, qdir), id);
+		sm_dprintf("dowork(%s/%s), forkflag=%d\n", qid_printqueue(qgrp, qdir), id, forkflag);
 
 	/*
 	**  Fork for work.
@@ -3732,7 +3780,12 @@ dowork(qgrp, qdir, id, forkflag, requeueflag, e)
 		eatheader(e, requeueflag, true);
 
 		if (requeueflag)
-			queueup(e, false, false);
+			queueup(e, QUP_FL_NONE);
+
+		if (tTd(40, 9))
+			sm_dprintf("dowork(%s/%s), forkflag=%d, pid=%d, CurRunners=%d\n",
+				qid_printqueue(qgrp, qdir), id, forkflag,
+				(int) CurrentPid, CurRunners);
 
 		/* do the delivery */
 		sendall(e, SM_DELIVER);
@@ -3916,7 +3969,7 @@ doworklist(el, forkflag, requeueflag)
 			eatheader(&e, requeueflag, true);
 
 			if (requeueflag)
-				queueup(&e, false, false);
+				queueup(&e, QUP_FL_NONE);
 
 			/* do the delivery */
 			sendall(&e, SM_DELIVER);
@@ -4010,7 +4063,7 @@ readqf(e, openonly)
 		if (tTd(40, 8))
 			sm_dprintf("%s: locked\n", e->e_id);
 		if (LogLevel > 19)
-			sm_syslog(LOG_DEBUG, e->e_id, "locked");
+			sm_syslog(LOG_DEBUG, e->e_id, "queueup: locked");
 		(void) sm_io_close(qfp, SM_TIME_DEFAULT);
 		RELEASE_QUEUE;
 		return false;
@@ -4318,11 +4371,11 @@ readqf(e, openonly)
 					e->e_flags |= EF_WARNING;
 					break;
 
-#if _FFR_EAI
+#if USE_EAI
 				  case 'e':	/* message requires EAI */
 					e->e_smtputf8 = true;
 					break;
-#endif /* _FFR_EAI */
+#endif /* USE_EAI */
 				}
 			}
 			break;
@@ -4469,6 +4522,7 @@ readqf(e, openonly)
 				qflags |= QPRIMARY;
 			macdefine(&e->e_macro, A_PERM, macid("{addr_type}"),
 				((qflags & QINTBCC) != 0) ? "e b" : "e r");
+/* XXX p must be [i] */
 			if (*p != '\0')
 				q = parseaddr(++p, NULLADDR, RF_COPYALL, '\0',
 						NULL, e, true);
@@ -4585,26 +4639,6 @@ readqf(e, openonly)
 	/* other checks? */
 #endif /* _FFR_QF_PARANOIA */
 
-#if _FFR_EAI
-	/*
-	**  If this message originates from something other than
-	**  srvrsmtp.c, then it might use UTF8 addresses but not be
-	**  marked.  We'll just add the mark so we're sure that it
-	**  either can be delivered or will be returned.
-	*/
-
-	if (!e->e_smtputf8)
-	{
-		ADDRESS *q;
-
-		for (q = e->e_sendqueue; q != NULL; q = q->q_next)
-			if (!addr_is_ascii(q->q_paddr) && !e->e_smtputf8)
-				e->e_smtputf8 = true;
-		if (!addr_is_ascii(e->e_from.q_paddr) && !e->e_smtputf8)
-			e->e_smtputf8 = true;
-	}
-#endif /* _FFR_EAI */
-
 	/* possibly set ${dsn_ret} macro */
 	if (bitset(EF_RET_PARAM, e->e_flags))
 	{
@@ -4951,7 +4985,7 @@ print_single_queue(qgrp, qdir)
 		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
 			")\n-----Q-ID----- --Size-- -Priority- ---Q-Time--- --------Sender/Recipient--------\n");
 	else
-		(void) sm_io_fprintf(smioout,  SM_TIME_DEFAULT,
+		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
 			")\n-----Q-ID----- --Size-- -----Q-Time----- ------------Sender/Recipient-----------\n");
 	for (w = WorkQ; w != NULL; w = w->w_next)
 	{
@@ -4962,7 +4996,7 @@ print_single_queue(qgrp, qdir)
 		int qfver;
 		char quarmsg[MAXLINE];
 		char statmsg[MAXLINE];
-		char bodytype[MAXNAME + 1];
+		char bodytype[MAXNAME + 1];	/* EAI:ok */
 		char qf[MAXPATHLEN];
 
 		if (StopRequest)
@@ -5013,11 +5047,7 @@ print_single_queue(qgrp, qdir)
 				if (stat(df, &st) >= 0)
 					dfsize = st.st_size;
 			}
-			if (e.e_lockfp != NULL)
-			{
-				(void) sm_io_close(e.e_lockfp, SM_TIME_DEFAULT);
-				e.e_lockfp = NULL;
-			}
+			SM_CLOSE_FP(e.e_lockfp);
 			clearenvelope(&e, false, e.e_rpool);
 			sm_rpool_free(e.e_rpool);
 		}
@@ -5489,8 +5519,12 @@ assign_queueid(e)
 	idbuf[5] = QueueIdChars[tm->tm_sec % QIC_LEN_R];
 	idbuf[6] = QueueIdChars[seq / QIC_LEN];
 	idbuf[7] = QueueIdChars[seq % QIC_LEN];
-	(void) sm_snprintf(&idbuf[8], sizeof(idbuf) - 8, "%06d",
-			   (int) LastQueuePid);
+	if (tTd(78, 100))
+		(void) sm_snprintf(&idbuf[8], sizeof(idbuf) - 8, "%07d",
+				   (int) LastQueuePid);
+	else
+		(void) sm_snprintf(&idbuf[8], sizeof(idbuf) - 8, "%06d",
+				   (int) LastQueuePid);
 	e->e_id = sm_rpool_strdup_x(e->e_rpool, idbuf);
 	macdefine(&e->e_macro, A_PERM, 'i', e->e_id);
 #if 0
@@ -5561,9 +5595,7 @@ unlockqueue(e)
 
 
 	/* if there is a lock file in the envelope, close it */
-	if (e->e_lockfp != NULL)
-		(void) sm_io_close(e->e_lockfp, SM_TIME_DEFAULT);
-	e->e_lockfp = NULL;
+	SM_CLOSE_FP(e->e_lockfp);
 
 	/* don't create a queue id if we don't already have one */
 	if (e->e_id == NULL)
@@ -5606,7 +5638,7 @@ setctluser(user, qfver, e)
 	**  See if this clears our concept of controlling user.
 	*/
 
-	if (user == NULL || *user == '\0')
+	if (SM_IS_EMPTY(user))
 		return NULL;
 
 	/*
@@ -5690,7 +5722,7 @@ loseqfile(e, why)
 	if (sm_strlcpy(buf, p, sizeof(buf)) >= sizeof(buf))
 		return;
 	if (!bitset(EF_INQUEUE, e->e_flags))
-		queueup(e, false, true);
+		queueup(e, QUP_FL_MSYNC);
 	else if (QueueMode == QM_LOST)
 		loseit = false;
 
@@ -5705,11 +5737,7 @@ loseqfile(e, why)
 			sm_syslog(LOG_ALERT, e->e_id,
 				  "Losing %s: %s", buf, why);
 	}
-	if (e->e_dfp != NULL)
-	{
-		(void) sm_io_close(e->e_dfp, SM_TIME_DEFAULT);
-		e->e_dfp = NULL;
-	}
+	SM_CLOSE_FP(e->e_dfp);
 	e->e_flags &= ~EF_HAS_DF;
 }
 /*
@@ -6682,7 +6710,7 @@ disk_status(out, prefix)
 **		none.
 */
 
-#if _FFR_USE_SEM_LOCKING && SM_CONF_SEM
+# if _FFR_USE_SEM_LOCKING && SM_CONF_SEM
 static int SemId = -1;		/* Semaphore Id */
 int SemKey = SM_SEM_KEY;
 #  define SEM_LOCK(r)	\
@@ -6691,16 +6719,16 @@ int SemKey = SM_SEM_KEY;
 		if (SemId >= 0)	\
 			r = sm_sem_acq(SemId, 0, 1);	\
 	} while (0)
-# define SEM_UNLOCK(r)	\
+#  define SEM_UNLOCK(r)	\
 	do	\
 	{	\
 		if (SemId >= 0 && r >= 0)	\
 			r = sm_sem_rel(SemId, 0, 1);	\
 	} while (0)
-#else /* _FFR_USE_SEM_LOCKING && SM_CONF_SEM */
-# define SEM_LOCK(r)
-# define SEM_UNLOCK(r)
-#endif /* _FFR_USE_SEM_LOCKING && SM_CONF_SEM */
+# else /* _FFR_USE_SEM_LOCKING && SM_CONF_SEM */
+#  define SEM_LOCK(r)
+#  define SEM_UNLOCK(r)
+# endif /* _FFR_USE_SEM_LOCKING && SM_CONF_SEM */
 
 static void init_sem __P((bool));
 
@@ -6708,8 +6736,8 @@ static void
 init_sem(owner)
 	bool owner;
 {
-#if _FFR_USE_SEM_LOCKING
-#if SM_CONF_SEM
+# if _FFR_USE_SEM_LOCKING
+#  if SM_CONF_SEM
 	SemId = sm_sem_start(SemKey, 1, 0, owner);
 	if (SemId < 0)
 	{
@@ -6728,8 +6756,8 @@ init_sem(owner)
 				"key=%ld, sm_semsetowner=%d, RunAsUid=%ld, RunAsGid=%ld",
 				(long) SemKey, r, (long) RunAsUid, (long) RunAsGid);
 	}
-#endif /* SM_CONF_SEM */
-#endif /* _FFR_USE_SEM_LOCKING */
+#  endif /* SM_CONF_SEM */
+# endif /* _FFR_USE_SEM_LOCKING */
 	return;
 }
 
@@ -6749,12 +6777,12 @@ static void
 stop_sem(owner)
 	bool owner;
 {
-#if _FFR_USE_SEM_LOCKING
-#if SM_CONF_SEM
+# if _FFR_USE_SEM_LOCKING
+#  if SM_CONF_SEM
 	if (owner && SemId >= 0)
 		sm_sem_stop(SemId);
-#endif
-#endif /* _FFR_USE_SEM_LOCKING */
+#  endif
+# endif /* _FFR_USE_SEM_LOCKING */
 	return;
 }
 
@@ -6823,7 +6851,7 @@ occ_exceeded(e, mci, host, addr)
 }
 
 /*
-**  OCC_CLOSE -- "close" an outgoing connection: up connection status
+**  OCC_CLOSE -- "close" an outgoing connection: update connection status
 **
 **	Parameters:
 **		e -- envelope
@@ -6962,7 +6990,7 @@ write_key_file(keypath, key)
 	SM_FILE_T *keyf;
 
 	ok = false;
-	if (keypath == NULL || *keypath == '\0')
+	if (SM_IS_EMPTY(keypath))
 		return ok;
 	sff = SFF_NOLINK|SFF_ROOTOK|SFF_REGONLY|SFF_CREAT;
 	if (TrustedUid != 0 && RealUid == TrustedUid)
@@ -6977,7 +7005,7 @@ write_key_file(keypath, key)
 	{
 		if (geteuid() == 0 && RunAsUid != 0)
 		{
-#  if HASFCHOWN
+# if HASFCHOWN
 			int fd;
 
 			fd = keyf->f_file;
@@ -6989,7 +7017,7 @@ write_key_file(keypath, key)
 					  "ownership change on %s to %ld failed: %s",
 					  keypath, (long) RunAsUid, sm_errstring(err));
 			}
-#  endif /* HASFCHOWN */
+# endif /* HASFCHOWN */
 		}
 		ok = sm_io_fprintf(keyf, SM_TIME_DEFAULT, "%ld\n", key) !=
 		     SM_IO_EOF;
@@ -7018,7 +7046,7 @@ read_key_file(keypath, key)
 	long sff, n;
 	SM_FILE_T *keyf;
 
-	if (keypath == NULL || *keypath == '\0')
+	if (SM_IS_EMPTY(keypath))
 		return key;
 	sff = SFF_NOLINK|SFF_ROOTOK|SFF_REGONLY;
 	if (RealUid == 0 || (TrustedUid != 0 && RealUid == TrustedUid))
@@ -7245,7 +7273,7 @@ setup_queues(owner)
 	/* Provide space for trailing '/' */
 	if (len >= sizeof(basedir) - 1)
 	{
-		syserr("QueueDirectory: path too long: %d,  max %d",
+		syserr("QueueDirectory: path too long: %d, max %d",
 			len, (int) sizeof(basedir) - 1);
 		ExitStat = EX_CONFIG;
 		return;
@@ -7676,7 +7704,7 @@ makequeue(line, qdef)
 
 	if (qg->qg_qdir == NULL)
 	{
-		if (QueueDir == NULL || *QueueDir == '\0')
+		if (SM_IS_EMPTY(QueueDir))
 		{
 			syserr("QueueDir must be defined before queue groups");
 			return;
@@ -7803,7 +7831,7 @@ cmpidx(a, b)
 **  result in more than one queue group per work group. In such a case
 **  the number of running queue groups in that work group will have no
 **  more than the work group maximum number of runners (a "fair" portion
-**  of MaxQueueRunners). All queue groups within a work group will get a
+**  of MaxQueueRun). All queue groups within a work group will get a
 **  chance at running.
 **
 **	Parameters:
@@ -7899,8 +7927,8 @@ makeworkgroups()
 	{
 		h = si[i].sg_idx;
 		if (tTd(41, 49))
-			sm_dprintf("sortqg: i=%d, j=%d, h=%d, skip=%d\n",
-				i, j, h, IS_BOUNCE_QUEUE(h));
+			sm_dprintf("sortqg: i=%d, j=%d, h=%d, runners=%d, skip=%d\n",
+				i, j, h, Queue[h]->qg_maxqrun, IS_BOUNCE_QUEUE(h));
 		SKIP_BOUNCE_QUEUE(h);
 
 		/* a to-and-fro packing scheme, continue from last position */
@@ -8015,7 +8043,7 @@ dup_df(old, new)
 		**  and a bounce mail is sent that is split.
 		*/
 
-		queueup(old, false, true);
+		queueup(old, QUP_FL_MSYNC);
 	}
 	SM_REQUIRE(ISVALIDQGRP(old->e_qgrp) && ISVALIDQDIR(old->e_qdir));
 	SM_REQUIRE(ISVALIDQGRP(new->e_qgrp) && ISVALIDQDIR(new->e_qdir));
@@ -9055,7 +9083,7 @@ quarantine_queue(reason, qgrplimit)
 
 	if (reason != NULL)
 	{
-		/* clean it */
+		/* clean it; leak does not matter: one time invocation */
 		reason = newstr(denlstring(reason, true, true));
 	}
 
@@ -9102,7 +9130,7 @@ quarantine_queue(reason, qgrplimit)
 				if (StopRequest)
 					stop_sendmail();
 
-				/* setup envelope */
+				/* set up envelope */
 				clearenvelope(&e, true, sm_rpool_new_x(NULL));
 				e.e_id = WorkList[i].w_name + 2;
 				e.e_qgrp = qgrp;
@@ -9141,3 +9169,141 @@ quarantine_queue(reason, qgrplimit)
 					     changed == 1 ? "" : "s");
 	}
 }
+
+#if _FFR_DMTRIGGER
+/*
+**  QM -- queue "manager"
+**
+**	Parameters:
+**		none.
+**
+**	Results:
+**		false on error
+**
+**	Side Effects:
+**		fork()s and runs as process to deliver queue entries
+*/
+
+bool
+qm()
+{
+	int r;
+	pid_t pid;
+	long tmo;
+
+	sm_syslog(LOG_DEBUG, NOQID, "queue manager: start");
+
+	(void) sm_blocksignal(SIGCHLD);
+	(void) sm_signal(SIGCHLD, reapchild);
+
+	pid = dofork();
+	if (pid == -1)
+	{
+		const char *msg = "queue manager -- fork() failed";
+		const char *err = sm_errstring(errno);
+
+		if (LogLevel > 8)
+			sm_syslog(LOG_INFO, NOQID, "%s: %s",
+				  msg, err);
+		(void) sm_releasesignal(SIGCHLD);
+		return false;
+	}
+	if (pid != 0)
+	{
+		/* parent -- pick up intermediate zombie */
+		(void) sm_releasesignal(SIGCHLD);
+		return true;
+	}
+
+/* XXX put this into a macro/function because it is used several times? */
+	/* child -- clean up signals */
+
+	/* Reset global flags */
+	RestartRequest = NULL;
+	RestartWorkGroup = false;
+	ShutdownRequest = NULL;
+	PendingSignal = 0;
+	CurrentPid = getpid();
+	close_sendmail_pid();
+
+	/*
+	**  Initialize exception stack and default exception
+	**  handler for child process.
+	*/
+
+	sm_exc_newthread(fatal_error);
+	clrcontrol();
+	proc_list_clear();
+
+	/* Add parent process as first child item */
+	proc_list_add(CurrentPid, "Queue manager", PROC_QM, 0, -1, NULL);
+	(void) sm_releasesignal(SIGCHLD);
+	(void) sm_signal(SIGCHLD, SIG_DFL);
+	(void) sm_signal(SIGHUP, SIG_DFL);
+	(void) sm_signal(SIGTERM, intsig);
+
+	/* drop privileges */
+	if (geteuid() == (uid_t) 0)
+		(void) drop_privileges(false);
+	disconnect(1, NULL);
+	QuickAbort = false;
+
+	r = sm_notify_start(true, 0);
+	if (r != 0)
+		syserr("sm_notify_start() failed=%d", r);
+
+	/*
+	**  Initially wait indefinitely, then only wait
+	**  until something needs to get done (not yet implemented).
+	*/
+
+	tmo = -1;
+	while (true)
+	{
+		char buf[64];
+		ENVELOPE *e;
+		SM_RPOOL_T *rpool;
+
+/*
+**  TODO: This should try to receive multiple ids:
+**  after it got one, check for more with a very short timeout
+**  and collect them in a list.
+**  but them some other code should be used to run all of them.
+*/
+
+		sm_syslog(LOG_DEBUG, NOQID, "queue manager: rcv=start");
+		r = sm_notify_rcv(buf, sizeof(buf), tmo);
+		if (-ETIMEDOUT == r)
+		{
+			sm_syslog(LOG_DEBUG, NOQID, "queue manager: rcv=timed_out");
+			continue;
+		}
+		if (r < 0)
+		{
+			sm_syslog(LOG_DEBUG, NOQID, "queue manager: rcv=%d", r);
+			goto end;
+		}
+		if (r > 0 && r < sizeof(buf))
+			buf[r] = '\0';
+		buf[sizeof(buf) - 1] = '\0';
+		sm_syslog(LOG_DEBUG, NOQID, "queue manager: got=%s", buf);
+		CurEnv = &QueueEnvelope;
+		rpool = sm_rpool_new_x(NULL);
+		e = newenvelope(&QueueEnvelope, CurEnv, rpool);
+		e->e_flags = BlankEnvelope.e_flags;
+		e->e_parent = NULL;
+		r = sm_io_sscanf(buf, "N:%d:%d:%s", &e->e_qgrp, &e->e_qdir, e->e_id);
+		if (r != 3)
+		{
+			sm_syslog(LOG_DEBUG, NOQID, "queue manager: buf=%s, scan=%d", buf, r);
+			goto end;
+		}
+		dowork(e->e_qgrp, e->e_qdir, e->e_id, true, false, e);
+	}
+
+  end:
+	sm_syslog(LOG_DEBUG, NOQID, "queue manager: stop");
+	finis(false, false, EX_OK);
+	return false;
+}
+#endif /* _FFR_DMTRIGGER */
diff --git a/contrib/sendmail/src/ratectrl.c b/contrib/sendmail/src/ratectrl.c
index 6b9d5c4af3fa..bfbf7d57990a 100644
--- a/contrib/sendmail/src/ratectrl.c
+++ b/contrib/sendmail/src/ratectrl.c
@@ -228,7 +228,7 @@ gen_hash(saddr)
 		}
 	}
 #else /* HASH_ALG == 1 */
-# ERROR: unsupported HASH_ALG
+# ERROR "unsupported HASH_ALG"
 	hv = ((hv << 1) ^ (*p & 0377)) % cctx->cc_size; ???
 #endif /* HASH_ALG == 1 */
 
@@ -424,7 +424,7 @@ conn_limits(e, now, saddr, clflags, hashary, ratelimit, conclimit)
 	logit = true;
 #endif
 #if RATECTL_DEBUG || _FFR_OCC
-#if _FFR_OCC
+# if _FFR_OCC
 	if (!exceeded)
 	{
 		if (prv != NULL)
@@ -433,7 +433,7 @@ conn_limits(e, now, saddr, clflags, hashary, ratelimit, conclimit)
 			++*pcv;
 	}
 	logit = exceeded || LogLevel > 11;
-#endif
+# endif
 	if (logit)
 		sm_syslog(LOG_DEBUG, e != NULL ? e->e_id : NOQID,
 			"conn_limits: addr=%s, flags=0x%x, rate=%d/%d, conc=%d/%d, exc=%d",
@@ -556,13 +556,13 @@ dump_ch(fp)
 		bool valid;
 
 		valid = false;
-#if NETINET
+# if NETINET
 		valid = (ch->ch_Family == AF_INET);
 		if (valid)
 			sm_io_fprintf(fp, SM_TIME_DEFAULT, "ip=%s ",
 				inet_ntoa(ch->ch_Addr4));
-#endif /* NETINET */
-#if NETINET6
+# endif /* NETINET */
+# if NETINET6
 		if (ch->ch_Family == AF_INET6)
 		{
 			char buf[64], *str;
@@ -573,7 +573,7 @@ dump_ch(fp)
 				sm_io_fprintf(fp, SM_TIME_DEFAULT, "ip=%s ",
 					str);
 		}
-#endif /* NETINET6 */
+# endif /* NETINET6 */
 		if (!valid)
 			continue;
 
@@ -589,9 +589,9 @@ dump_ch(fp)
 
 		sm_io_fprintf(fp, SM_TIME_DEFAULT, "time=%ld cnt=%d ",
 			(long) ch->ch_LTime, cnt);
-#if _FFR_OCC
+# if _FFR_OCC
 		sm_io_fprintf(fp, SM_TIME_DEFAULT, "oc=%d", ch->ch_oc);
-#endif
+# endif
 		sm_io_fprintf(fp, SM_TIME_DEFAULT, "\n");
 	}
 	sm_io_flush(fp, SM_TIME_DEFAULT);
diff --git a/contrib/sendmail/src/readcf.c b/contrib/sendmail/src/readcf.c
index e6f62961f220..47c9777cc33b 100644
--- a/contrib/sendmail/src/readcf.c
+++ b/contrib/sendmail/src/readcf.c
@@ -63,7 +63,7 @@ initbouncequeue()
 	STAB *s;
 
 	BounceQueue = NOQGRP;
-	if (bouncequeue == NULL || bouncequeue[0] == '\0')
+	if (SM_IS_EMPTY(bouncequeue))
 		return;
 
 	s = stab(bouncequeue, ST_QUEUE, ST_FIND);
@@ -247,11 +247,13 @@ readcf(cfname, safe, e)
 			if (rwp == NULL)
 			{
 				RewriteRules[ruleset] = rwp =
-					(struct rewrite *) xalloc(sizeof(*rwp));
+					(struct rewrite *) sm_malloc_tagged_x(sizeof(*rwp),
+								"rwr1", 1, 0);
 			}
 			else
 			{
-				rwp->r_next = (struct rewrite *) xalloc(sizeof(*rwp));
+				rwp->r_next = (struct rewrite *) sm_malloc_tagged_x(sizeof(*rwp),
+								"rwr2", 2, 0);
 				rwp = rwp->r_next;
 			}
 			rwp->r_next = NULL;
@@ -531,6 +533,14 @@ readcf(cfname, safe, e)
 			mid = macid_parse(&bp[1], &ep);
 			if (mid == 0)
 				break;
+#if USE_EAI && 0
+			if ('j' == mid && !addr_is_ascii(ep))
+			{
+				usrerr("hostname %s must be ASCII", ep);
+				finis(false, true, EX_CONFIG);
+				/* NOTREACHED */
+			}
+#endif
 			p = munchstring(ep, NULL, '\0');
 			macdefine(&e->e_macro, A_TEMP, mid, p);
 			break;
@@ -548,6 +558,9 @@ readcf(cfname, safe, e)
 					break;
 				expand(ep, exbuf, sizeof(exbuf), e);
 				p = exbuf;
+#if _FFR_8BITENVADDR
+				dequote_internal_chars(p, exbuf, sizeof(exbuf));
+#endif
 			}
 			else
 			{
@@ -833,7 +846,7 @@ translate_dollars(ibp, obp, bsp)
 		sm_dprintf(")\n");
 	}
 
-	bp = quote_internal_chars(ibp, obp, bsp);
+	bp = quote_internal_chars(ibp, obp, bsp, NULL);
 
 	for (p = bp; *p != '\0'; p++)
 	{
@@ -1381,7 +1394,7 @@ makemailer(line)
 	static int nextmailer = 0;	/* "free" index into Mailer struct */
 
 	/* allocate a mailer and set up defaults */
-	m = (struct mailer *) xalloc(sizeof(*m));
+	m = (struct mailer *) sm_malloc_tagged_x(sizeof(*m), "mailer", 0, 0);
 	memset((char *) m, '\0', sizeof(*m));
 	errno = 0; /* avoid bogus error text */
 
@@ -1561,12 +1574,12 @@ makemailer(line)
 				if (*p == '\0')
 					p = NULL;
 			}
-			if (m->m_addrtype == NULL || *m->m_addrtype == '\0')
+			if (SM_IS_EMPTY(m->m_addrtype))
 				m->m_addrtype = "rfc822";
 
 			/* extract diagnostic type; default to "smtp" */
 			m->m_diagtype = p;
-			if (m->m_diagtype == NULL || *m->m_diagtype == '\0')
+			if (SM_IS_EMPTY(m->m_diagtype))
 				m->m_diagtype = "smtp";
 			break;
 
@@ -1577,11 +1590,11 @@ makemailer(line)
 				struct passwd *pw;
 
 				while (*p != '\0' && isascii(*p) &&
-# if _FFR_DOTTED_USERNAMES
+#if _FFR_DOTTED_USERNAMES
 				       (isalnum(*p) || strchr(SM_PWN_CHARS, *p) != NULL))
-# else
+#else
 				       (isalnum(*p) || strchr("-_", *p) != NULL))
-# endif
+#endif
 					p++;
 				while (SM_ISSPACE(*p))
 					*p++ = '\0';
@@ -1987,7 +2000,7 @@ makeargv(p)
 	argv[i++] = NULL;
 
 	/* now make a copy of the argv */
-	avp = (char **) xalloc(sizeof(*avp) * i);
+	avp = (char **) sm_malloc_tagged_x(sizeof(*avp) * i, "makeargv", 0, 0);
 	memmove((char *) avp, (char *) argv, sizeof(*avp) * i);
 
 	return avp;
@@ -2114,137 +2127,137 @@ static struct ssl_options
 } SSL_Option[] =
 {
 /* Workaround for bugs are turned on by default (as well as some others) */
-#ifdef SSL_OP_MICROSOFT_SESS_ID_BUG
+# ifdef SSL_OP_MICROSOFT_SESS_ID_BUG
 	{ "SSL_OP_MICROSOFT_SESS_ID_BUG",	SSL_OP_MICROSOFT_SESS_ID_BUG	},
-#endif
-#ifdef SSL_OP_NETSCAPE_CHALLENGE_BUG
+# endif
+# ifdef SSL_OP_NETSCAPE_CHALLENGE_BUG
 	{ "SSL_OP_NETSCAPE_CHALLENGE_BUG",	SSL_OP_NETSCAPE_CHALLENGE_BUG	},
-#endif
-#ifdef SSL_OP_LEGACY_SERVER_CONNECT
+# endif
+# ifdef SSL_OP_LEGACY_SERVER_CONNECT
 	{ "SSL_OP_LEGACY_SERVER_CONNECT",	SSL_OP_LEGACY_SERVER_CONNECT	},
-#endif
-#ifdef SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG
+# endif
+# ifdef SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG
 	{ "SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG",	SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG	},
-#endif
-#ifdef SSL_OP_SSLREF2_REUSE_CERT_TYPE_BUG
+# endif
+# ifdef SSL_OP_SSLREF2_REUSE_CERT_TYPE_BUG
 	{ "SSL_OP_SSLREF2_REUSE_CERT_TYPE_BUG",	SSL_OP_SSLREF2_REUSE_CERT_TYPE_BUG	},
-#endif
-#ifdef SSL_OP_MICROSOFT_BIG_SSLV3_BUFFER
+# endif
+# ifdef SSL_OP_MICROSOFT_BIG_SSLV3_BUFFER
 	{ "SSL_OP_MICROSOFT_BIG_SSLV3_BUFFER",	SSL_OP_MICROSOFT_BIG_SSLV3_BUFFER	},
-#endif
-#ifdef SSL_OP_MSIE_SSLV2_RSA_PADDING
+# endif
+# ifdef SSL_OP_MSIE_SSLV2_RSA_PADDING
 	{ "SSL_OP_MSIE_SSLV2_RSA_PADDING",	SSL_OP_MSIE_SSLV2_RSA_PADDING	},
-#endif
-#ifdef SSL_OP_SSLEAY_080_CLIENT_DH_BUG
+# endif
+# ifdef SSL_OP_SSLEAY_080_CLIENT_DH_BUG
 	{ "SSL_OP_SSLEAY_080_CLIENT_DH_BUG",	SSL_OP_SSLEAY_080_CLIENT_DH_BUG	},
-#endif
-#ifdef SSL_OP_TLS_D5_BUG
+# endif
+# ifdef SSL_OP_TLS_D5_BUG
 	{ "SSL_OP_TLS_D5_BUG",	SSL_OP_TLS_D5_BUG	},
-#endif
-#ifdef SSL_OP_TLS_BLOCK_PADDING_BUG
+# endif
+# ifdef SSL_OP_TLS_BLOCK_PADDING_BUG
 	{ "SSL_OP_TLS_BLOCK_PADDING_BUG",	SSL_OP_TLS_BLOCK_PADDING_BUG	},
-#endif
-#ifdef SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS
+# endif
+# ifdef SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS
 	{ "SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS",	SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS	},
-#endif
-#ifdef SSL_OP_ALL
+# endif
+# ifdef SSL_OP_ALL
 	{ "SSL_OP_ALL",	SSL_OP_ALL	},
-#endif
-#ifdef SSL_OP_NO_QUERY_MTU
+# endif
+# ifdef SSL_OP_NO_QUERY_MTU
 	{ "SSL_OP_NO_QUERY_MTU",	SSL_OP_NO_QUERY_MTU	},
-#endif
-#ifdef SSL_OP_COOKIE_EXCHANGE
+# endif
+# ifdef SSL_OP_COOKIE_EXCHANGE
 	{ "SSL_OP_COOKIE_EXCHANGE",	SSL_OP_COOKIE_EXCHANGE	},
-#endif
-#ifdef SSL_OP_NO_TICKET
+# endif
+# ifdef SSL_OP_NO_TICKET
 	{ "SSL_OP_NO_TICKET",	SSL_OP_NO_TICKET	},
-#endif
-#ifdef SSL_OP_CISCO_ANYCONNECT
+# endif
+# ifdef SSL_OP_CISCO_ANYCONNECT
 	{ "SSL_OP_CISCO_ANYCONNECT",	SSL_OP_CISCO_ANYCONNECT	},
-#endif
-#ifdef SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION
+# endif
+# ifdef SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION
 	{ "SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION",	SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION	},
-#endif
-#ifdef SSL_OP_NO_COMPRESSION
+# endif
+# ifdef SSL_OP_NO_COMPRESSION
 	{ "SSL_OP_NO_COMPRESSION",	SSL_OP_NO_COMPRESSION	},
-#endif
-#ifdef SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION
+# endif
+# ifdef SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION
 	{ "SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION",	SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION	},
-#endif
-#ifdef SSL_OP_SINGLE_ECDH_USE
+# endif
+# ifdef SSL_OP_SINGLE_ECDH_USE
 	{ "SSL_OP_SINGLE_ECDH_USE",	SSL_OP_SINGLE_ECDH_USE	},
-#endif
-#ifdef SSL_OP_SINGLE_DH_USE
+# endif
+# ifdef SSL_OP_SINGLE_DH_USE
 	{ "SSL_OP_SINGLE_DH_USE",	SSL_OP_SINGLE_DH_USE	},
-#endif
-#ifdef SSL_OP_EPHEMERAL_RSA
+# endif
+# ifdef SSL_OP_EPHEMERAL_RSA
 	{ "SSL_OP_EPHEMERAL_RSA",	SSL_OP_EPHEMERAL_RSA	},
-#endif
-#ifdef SSL_OP_CIPHER_SERVER_PREFERENCE
+# endif
+# ifdef SSL_OP_CIPHER_SERVER_PREFERENCE
 	{ "SSL_OP_CIPHER_SERVER_PREFERENCE",	SSL_OP_CIPHER_SERVER_PREFERENCE	},
-#endif
-#ifdef SSL_OP_TLS_ROLLBACK_BUG
+# endif
+# ifdef SSL_OP_TLS_ROLLBACK_BUG
 	{ "SSL_OP_TLS_ROLLBACK_BUG",	SSL_OP_TLS_ROLLBACK_BUG	},
-#endif
-#ifdef SSL_OP_NO_SSLv2
+# endif
+# ifdef SSL_OP_NO_SSLv2
 	{ "SSL_OP_NO_SSLv2",	SSL_OP_NO_SSLv2	},
-#endif
-#ifdef SSL_OP_NO_SSLv3
+# endif
+# ifdef SSL_OP_NO_SSLv3
 	{ "SSL_OP_NO_SSLv3",	SSL_OP_NO_SSLv3	},
-#endif
-#ifdef SSL_OP_NO_TLSv1
+# endif
+# ifdef SSL_OP_NO_TLSv1
 	{ "SSL_OP_NO_TLSv1",	SSL_OP_NO_TLSv1	},
-#endif
-#ifdef SSL_OP_NO_TLSv1_3
+# endif
+# ifdef SSL_OP_NO_TLSv1_3
 	{ "SSL_OP_NO_TLSv1_3",	SSL_OP_NO_TLSv1_3	},
-#endif
-#ifdef SSL_OP_NO_TLSv1_2
+# endif
+# ifdef SSL_OP_NO_TLSv1_2
 	{ "SSL_OP_NO_TLSv1_2",	SSL_OP_NO_TLSv1_2	},
-#endif
-#ifdef SSL_OP_NO_TLSv1_1
+# endif
+# ifdef SSL_OP_NO_TLSv1_1
 	{ "SSL_OP_NO_TLSv1_1",	SSL_OP_NO_TLSv1_1	},
-#endif
-#ifdef SSL_OP_PKCS1_CHECK_1
+# endif
+# ifdef SSL_OP_PKCS1_CHECK_1
 	{ "SSL_OP_PKCS1_CHECK_1",	SSL_OP_PKCS1_CHECK_1	},
-#endif
-#ifdef SSL_OP_PKCS1_CHECK_2
+# endif
+# ifdef SSL_OP_PKCS1_CHECK_2
 	{ "SSL_OP_PKCS1_CHECK_2",	SSL_OP_PKCS1_CHECK_2	},
-#endif
-#ifdef SSL_OP_NETSCAPE_CA_DN_BUG
+# endif
+# ifdef SSL_OP_NETSCAPE_CA_DN_BUG
 	{ "SSL_OP_NETSCAPE_CA_DN_BUG",	SSL_OP_NETSCAPE_CA_DN_BUG	},
-#endif
-#ifdef SSL_OP_NETSCAPE_DEMO_CIPHER_CHANGE_BUG
+# endif
+# ifdef SSL_OP_NETSCAPE_DEMO_CIPHER_CHANGE_BUG
 	{ "SSL_OP_NETSCAPE_DEMO_CIPHER_CHANGE_BUG",	SSL_OP_NETSCAPE_DEMO_CIPHER_CHANGE_BUG	},
-#endif
-#ifdef SSL_OP_CRYPTOPRO_TLSEXT_BUG
+# endif
+# ifdef SSL_OP_CRYPTOPRO_TLSEXT_BUG
 	{ "SSL_OP_CRYPTOPRO_TLSEXT_BUG",	SSL_OP_CRYPTOPRO_TLSEXT_BUG	},
-#endif
-#ifdef SSL_OP_TLSEXT_PADDING
+# endif
+# ifdef SSL_OP_TLSEXT_PADDING
 	{ "SSL_OP_TLSEXT_PADDING",	SSL_OP_TLSEXT_PADDING	},
-#endif
-#ifdef SSL_OP_NO_RENEGOTIATION
-	{ "SSL_OP_NO_RENEGOTIATION",    SSL_OP_NO_RENEGOTIATION },
-#endif
-#ifdef SSL_OP_NO_ANTI_REPLAY
+# endif
+# ifdef SSL_OP_NO_RENEGOTIATION
+	{ "SSL_OP_NO_RENEGOTIATION",	SSL_OP_NO_RENEGOTIATION },
+# endif
+# ifdef SSL_OP_NO_ANTI_REPLAY
 	{ "SSL_OP_NO_ANTI_REPLAY",	SSL_OP_NO_ANTI_REPLAY },
-#endif
-#ifdef SSL_OP_ALLOW_NO_DHE_KEX
+# endif
+# ifdef SSL_OP_ALLOW_NO_DHE_KEX
 	{ "SSL_OP_ALLOW_NO_DHE_KEX",	SSL_OP_ALLOW_NO_DHE_KEX },
-#endif
-#ifdef SSL_OP_NO_ENCRYPT_THEN_MAC
+# endif
+# ifdef SSL_OP_NO_ENCRYPT_THEN_MAC
 	{ "SSL_OP_NO_ENCRYPT_THEN_MAC",	SSL_OP_NO_ENCRYPT_THEN_MAC },
-#endif
-#ifdef SSL_OP_ENABLE_MIDDLEBOX_COMPAT
+# endif
+# ifdef SSL_OP_ENABLE_MIDDLEBOX_COMPAT
 	{ "SSL_OP_ENABLE_MIDDLEBOX_COMPAT",	SSL_OP_ENABLE_MIDDLEBOX_COMPAT },
-#endif
-#ifdef SSL_OP_PRIORITIZE_CHACHA
+# endif
+# ifdef SSL_OP_PRIORITIZE_CHACHA
 	{ "SSL_OP_PRIORITIZE_CHACHA",	SSL_OP_PRIORITIZE_CHACHA },
-#endif
+# endif
 	{ NULL,		0		}
 };
 
 /*
-** READSSLOPTIONS  -- read SSL_OP_* values
+**  READSSLOPTIONS  -- read SSL_OP_* values
 **
 **	Parameters:
 **		opt -- name of option (can be NULL)
@@ -2256,8 +2269,8 @@ static struct ssl_options
 **		0 on success.
 */
 
-#define SSLOPERR_NAN	1
-#define SSLOPERR_NOTFOUND	2
+# define SSLOPERR_NAN	1
+# define SSLOPERR_NOTFOUND	2
 
 static int readssloptions __P((char *, char *, unsigned long *, int ));
 
@@ -2314,7 +2327,7 @@ readssloptions(opt, val, pssloptions, delim)
 			for (sslopts = SSL_Option;
 			     sslopts->sslopt_name != NULL; sslopts++)
 			{
-				if (sm_strcasecmp(q, sslopts->sslopt_name) == 0)
+				if (SM_STRCASEEQ(q, sslopts->sslopt_name))
 				{
 					sslopt_val = sslopts->sslopt_bits;
 					break;
@@ -2341,7 +2354,7 @@ readssloptions(opt, val, pssloptions, delim)
 }
 
 /*
-** GET_TLS_SE_OPTIONS -- get TLS session options (from ruleset)
+**  GET_TLS_SE_FEATURES -- get TLS session features (from ruleset)
 **
 **	Parameters:
 **		e -- envelope
@@ -2350,40 +2363,44 @@ readssloptions(opt, val, pssloptions, delim)
 **		srv -- server?
 **
 **	Returns:
-**		0 on success.
+**		EX_OK on success.
 */
 
 int
-get_tls_se_options(e, ssl, tlsi_ctx, srv)
+get_tls_se_features(e, ssl, tlsi_ctx, srv)
 	ENVELOPE *e;
 	SSL *ssl;
 	tlsi_ctx_T *tlsi_ctx;
 	bool srv;
 {
-	bool saveQuickAbort, saveSuprErrs, ok;
+	bool saveQuickAbort, saveSuprErrs;
 	char *optionlist, *opt, *val;
 	char *keyfile, *certfile;
 	size_t len, i;
-	int ret;
+	int ret, rv;
 
-#  define who (srv ? "server" : "client")
-#  define NAME_C_S macvalue(macid(srv ? "{client_name}" : "{server_name}"), e)
-#  define ADDR_C_S macvalue(macid(srv ? "{client_addr}" : "{server_addr}"), e)
-#  define WHICH srv ? "srv" : "clt"
+# define who (srv ? "server" : "client")
+# define NAME_C_S macvalue(macid(srv ? "{client_name}" : "{server_name}"), e)
+# define ADDR_C_S macvalue(macid(srv ? "{client_addr}" : "{server_addr}"), e)
+# define WHICH srv ? "srv" : "clt"
 
-	ret = 0;
+	SM_REQUIRE(ssl != NULL);
+	rv = EX_OK;
 	keyfile = certfile = opt = val = NULL;
 	saveQuickAbort = QuickAbort;
 	saveSuprErrs = SuprErrs;
 	SuprErrs = true;
 	QuickAbort = false;
+# if _FFR_MTA_STS
+	SM_FREE(STS_SNI);
+# endif
 
 	optionlist = NULL;
-	ok = rscheck(srv ? "tls_srv_features" : "tls_clt_features",
+	rv = rscheck(srv ? "tls_srv_features" : "tls_clt_features",
 		     NAME_C_S, ADDR_C_S, e,
 		     RSF_RMCOMM|RSF_ADDR|RSF_STRING,
-		     5, NULL, NOQID, NULL, &optionlist) == EX_OK;
-	if (!ok && LogLevel > 8)
+		     5, NULL, NOQID, NULL, &optionlist);
+	if (EX_OK != rv && LogLevel > 8)
 	{
 		sm_syslog(LOG_NOTICE, NOQID,
 			  "rscheck(tls_%s_features)=failed, relay=%s [%s], errors=%d",
@@ -2392,20 +2409,19 @@ get_tls_se_options(e, ssl, tlsi_ctx, srv)
 	}
 	QuickAbort = saveQuickAbort;
 	SuprErrs = saveSuprErrs;
-	if (ok && LogLevel > 9)
+	if (EX_OK == rv && LogLevel > 9)
 	{
 		sm_syslog(LOG_INFO, NOQID,
 			  "tls_%s_features=%s, relay=%s [%s]",
 			  WHICH, optionlist, NAME_C_S, ADDR_C_S);
 	}
-	if (!ok || optionlist == NULL || (len = strlen(optionlist)) < 2)
+	if (EX_OK != rv || optionlist == NULL || (len = strlen(optionlist)) < 2)
 	{
 		if (LogLevel > 9)
 			sm_syslog(LOG_INFO, NOQID,
-				  "tls_%s_features=empty, relay=%s [%s]",
-				  WHICH, NAME_C_S, ADDR_C_S);
-
-		return ok ? 0 : 1;
+				  "tls_%s_features=empty, stat=%d, relay=%s [%s]",
+				  WHICH, rv, NAME_C_S, ADDR_C_S);
+		return rv;
 	}
 
 	i = 0;
@@ -2421,7 +2437,7 @@ get_tls_se_options(e, ssl, tlsi_ctx, srv)
 				  WHICH, NAME_C_S, ADDR_C_S);
 
 			/* this is not treated as error! */
-			return 0;
+			return EX_OK;
 		}
 		i = 1;
 	}
@@ -2432,7 +2448,7 @@ get_tls_se_options(e, ssl, tlsi_ctx, srv)
 			sm_syslog(LOG_INFO, NOQID,	\
 				  "tls_%s_features=invalid_syntax, opt=%s, relay=%s [%s]",	\
 				  WHICH, opt, NAME_C_S, ADDR_C_S);	\
-		return -1;	\
+		goto fail;	\
 	} while (0)
 
 #  define CHECKLEN	\
@@ -2482,7 +2498,7 @@ get_tls_se_options(e, ssl, tlsi_ctx, srv)
 				  "tls_%s_features=parsed, %s=%s, relay=%s [%s]",
 				  WHICH, opt, val, NAME_C_S, ADDR_C_S);
 
-		if (sm_strcasecmp(opt, "options") == 0)
+		if (SM_STRCASEEQ(opt, "options"))
 		{
 			unsigned long ssloptions;
 
@@ -2490,22 +2506,25 @@ get_tls_se_options(e, ssl, tlsi_ctx, srv)
 			ret = readssloptions(NULL, val, &ssloptions, ';');
 			if (ret == 0)
 				(void) SSL_set_options(ssl, (long) ssloptions);
-			else if (LogLevel > 8)
+			else
 			{
-				sm_syslog(LOG_WARNING, NOQID,
-					  "tls_%s_features=%s, error=%s, relay=%s [%s]",
-					  WHICH, val,
-					  (ret == SSLOPERR_NAN) ? "not a number" :
-					  ((ret == SSLOPERR_NOTFOUND) ? "SSL_OP not found" :
-					  "unknown"),
-					  NAME_C_S, ADDR_C_S);
+				if (LogLevel > 8)
+				{
+					sm_syslog(LOG_WARNING, NOQID,
+						  "tls_%s_features=%s, error=%s, relay=%s [%s]",
+						  WHICH, val,
+						  (ret == SSLOPERR_NAN) ? "not a number" :
+						  ((ret == SSLOPERR_NOTFOUND) ? "SSL_OP not found" :
+						  "unknown"),
+						  NAME_C_S, ADDR_C_S);
+				}
+				goto fail;
 			}
 		}
-		else if (sm_strcasecmp(opt, "cipherlist") == 0)
+		else if (SM_STRCASEEQ(opt, "cipherlist"))
 		{
 			if (SSL_set_cipher_list(ssl, val) <= 0)
 			{
-				ret = 1;
 				if (LogLevel > 7)
 				{
 					sm_syslog(LOG_WARNING, NOQID,
@@ -2514,9 +2533,27 @@ get_tls_se_options(e, ssl, tlsi_ctx, srv)
 
 					tlslogerr(LOG_WARNING, 9, who);
 				}
+				goto fail;
 			}
 		}
-		else if (sm_strcasecmp(opt, "flags") == 0)
+# if MTA_HAVE_TLSv1_3
+		else if (SM_STRCASEEQ(opt, "ciphersuites"))
+		{
+			if (SSL_set_ciphersuites(ssl, val) <= 0)
+			{
+				if (LogLevel > 7)
+				{
+					sm_syslog(LOG_WARNING, NOQID,
+						  "STARTTLS=%s, error: SSL_set_ciphersuites(%s) failed",
+						  who, val);
+
+					tlslogerr(LOG_WARNING, 9, who);
+				}
+				goto fail;
+			}
+		}
+# endif /* MTA_HAVE_TLSv1_3 */
+		else if (SM_STRCASEEQ(opt, "flags"))
 		{
 			char *p;
 
@@ -2526,19 +2563,45 @@ get_tls_se_options(e, ssl, tlsi_ctx, srv)
 					setbitn(bitidx(*p), tlsi_ctx->tlsi_flags);
 			}
 		}
-		else if (sm_strcasecmp(opt, "keyfile") == 0)
+		else if (SM_STRCASEEQ(opt, "keyfile"))
 			keyfile = val;
-		else if (sm_strcasecmp(opt, "certfile") == 0)
+		else if (SM_STRCASEEQ(opt, "certfile"))
 			certfile = val;
+# if _FFR_MTA_STS
+		else if (sm_strcasecmp(opt, "servername") == 0 &&
+			 sm_strcasecmp(val, "hostname") == 0)
+		{
+			char *sn;
+
+			sn = macvalue(macid("{server_name}"), e);
+			if (sn == NULL)
+				STS_SNI = NULL;
+			else
+				STS_SNI = sm_strdup(sn);
+		}
+		else if (sm_strcasecmp(opt, "servername") == 0)
+		{
+			if (LogLevel > 7)
+			{
+				sm_syslog(LOG_INFO, NOQID,
+					  "tls_%s_features=servername, invalid_value=%s, relay=%s [%s]",
+					  WHICH, val, NAME_C_S, ADDR_C_S);
+			}
+			goto fail;
+		}
+		else if (sm_strcasecmp(opt, "sts") == 0 &&
+			 sm_strcasecmp(val, "secure") == 0)
+			setbitn(bitidx(TLSI_FL_STS_NOFB2CLR), tlsi_ctx->tlsi_flags);
+# endif /* _FFR_MTA_STS */
 		else
 		{
-			ret = 1;
 			if (LogLevel > 7)
 			{
 				sm_syslog(LOG_INFO, NOQID,
 					  "tls_%s_features=unknown_option, opt=%s, relay=%s [%s]",
 					  WHICH, opt, NAME_C_S, ADDR_C_S);
 			}
+			goto fail;
 		}
 
 	} while (optionlist[i] != '\0' && i < len);
@@ -2552,20 +2615,23 @@ get_tls_se_options(e, ssl, tlsi_ctx, srv)
 	}
 	else if (keyfile != NULL || certfile != NULL)
 	{
-		ret = 1;
 		if (LogLevel > 7)
 		{
 			sm_syslog(LOG_INFO, NOQID,
 				  "tls_%s_features=only_one_of_CertFile/KeyFile_specified, relay=%s [%s]",
 				  WHICH, NAME_C_S, ADDR_C_S);
 		}
+		goto fail;
 	}
 
-	return ret;
-#  undef who
-#  undef NAME_C_S
-#  undef ADDR_C_S
-#  undef WHICH
+	return rv;
+
+  fail:
+	return EX_CONFIG;
+# undef who
+# undef NAME_C_S
+# undef ADDR_C_S
+# undef WHICH
 }
 #endif /* STARTTLS */
 
@@ -2871,7 +2937,7 @@ static struct optioninfo
 #define O_CRLPATH	0xd7
 	{ "CRLPath",		O_CRLPATH,	OI_NONE	},
 #define O_HELONAME 0xd8
-	{ "HeloName",   O_HELONAME,     OI_NONE },
+	{ "HeloName",		O_HELONAME,	OI_NONE },
 #if _FFR_MEMSTAT
 # define O_REFUSELOWMEM	0xd9
 	{ "RefuseLowMem",	O_REFUSELOWMEM,	OI_NONE },
@@ -2892,7 +2958,7 @@ static struct optioninfo
 #endif
 #if _FFR_EIGHT_BIT_ADDR_OK
 # if !ALLOW_255
-#  ERROR FFR_EIGHT_BIT_ADDR_OK requires _ALLOW_255
+#  ERROR "_FFR_EIGHT_BIT_ADDR_OK requires ALLOW_255"
 # endif
 # define O_EIGHT_BIT_ADDR_OK	0xdf
 	{ "EightBitAddrOK",	O_EIGHT_BIT_ADDR_OK,	OI_NONE },
@@ -2970,13 +3036,24 @@ static struct optioninfo
 
 #if _FFR_CLIENTCA
 #define O_CLTCACERTFILE	0xf5
-	{ "ClientCACertFile",			O_CLTCACERTFILE, OI_NONE },
+	{ "ClientCACertFile",	O_CLTCACERTFILE, OI_NONE },
 #define O_CLTCACERTPATH	0xf6
-	{ "ClientCACertPath",			O_CLTCACERTPATH, OI_NONE },
+	{ "ClientCACertPath",	O_CLTCACERTPATH, OI_NONE },
 #endif
 #if _FFR_TLS_ALTNAMES
 # define O_CHECKALTNAMES 0xf7
-	{ "SetCertAltnames",			O_CHECKALTNAMES, OI_NONE },
+	{ "SetCertAltnames",	O_CHECKALTNAMES, OI_NONE },
+#endif
+#define O_SMTPUTF8	0xf8
+	{ "SmtpUTF8",	O_SMTPUTF8,	OI_NONE },
+#if _FFR_MTA_STS
+# define O_MTASTS	0xf9
+	{ "StrictTransportSecurity",	O_MTASTS,	OI_NONE	},
+#endif
+
+#if MTA_HAVE_TLSv1_3
+#define O_CIPHERSUITES	0xfa
+	{ "CipherSuites",		O_CIPHERSUITES,	OI_NONE	},
 #endif
 
 	{ NULL,				'\0',		OI_NONE	}
@@ -3265,6 +3342,9 @@ setoption(opt, val, safe, sticky, e)
 #if _FFR_DM_ONE
 		/* deliver first TA in background, then queue */
 		  case SM_DM_ONE:
+#endif
+#if _FFR_DMTRIGGER
+		  case SM_TRIGGER:
 #endif
 			set_delivery_mode(*val, e);
 			break;
@@ -3368,19 +3448,19 @@ setoption(opt, val, safe, sticky, e)
 				p++;
 			if (*p != '\0')
 				*p++ = '\0';
-			if (sm_strcasecmp(q, "HasWildcardMX") == 0)
+			if (SM_STRCASEEQ(q, "HasWildcardMX"))
 			{
 				HasWildcardMX = !clearmode;
 				continue;
 			}
-			if (sm_strcasecmp(q, "WorkAroundBrokenAAAA") == 0)
+			if (SM_STRCASEEQ(q, "WorkAroundBrokenAAAA"))
 			{
 				WorkAroundBrokenAAAA = !clearmode;
 				continue;
 			}
 			for (rfp = ResolverFlags; rfp->rf_name != NULL; rfp++)
 			{
-				if (sm_strcasecmp(q, rfp->rf_name) == 0)
+				if (SM_STRCASEEQ(q, rfp->rf_name))
 					break;
 			}
 			if (rfp->rf_name == NULL)
@@ -3482,7 +3562,7 @@ setoption(opt, val, safe, sticky, e)
 
 			for (pv = PrivacyValues; pv->pv_name != NULL; pv++)
 			{
-				if (sm_strcasecmp(val, pv->pv_name) == 0)
+				if (SM_STRCASEEQ(val, pv->pv_name))
 					break;
 			}
 			if (pv->pv_name == NULL)
@@ -3572,11 +3652,11 @@ setoption(opt, val, safe, sticky, e)
 	  case 'u':		/* set default uid */
 		for (p = val; *p != '\0'; p++)
 		{
-# if _FFR_DOTTED_USERNAMES
+#if _FFR_DOTTED_USERNAMES
 			if (*p == '/' || *p == ':')
-# else
+#else
 			if (*p == '.' || *p == '/' || *p == ':')
-# endif
+#endif
 			{
 				*p++ = '\0';
 				break;
@@ -3606,14 +3686,14 @@ setoption(opt, val, safe, sticky, e)
 			}
 		}
 
-# ifdef UID_MAX
+#ifdef UID_MAX
 		if (DefUid > UID_MAX)
 		{
 			syserr("readcf: option u: uid value (%ld) > UID_MAX (%ld); ignored",
 				(long)DefUid, (long)UID_MAX);
 			break;
 		}
-# endif /* UID_MAX */
+#endif /* UID_MAX */
 
 		/* handle the group if it is there */
 		if (*p == '\0')
@@ -3745,15 +3825,15 @@ setoption(opt, val, safe, sticky, e)
 		break;
 
 	  case O_NORCPTACTION:	/* what to do if no recipient */
-		if (sm_strcasecmp(val, "none") == 0)
+		if (SM_STRCASEEQ(val, "none"))
 			NoRecipientAction = NRA_NO_ACTION;
-		else if (sm_strcasecmp(val, "add-to") == 0)
+		else if (SM_STRCASEEQ(val, "add-to"))
 			NoRecipientAction = NRA_ADD_TO;
-		else if (sm_strcasecmp(val, "add-apparently-to") == 0)
+		else if (SM_STRCASEEQ(val, "add-apparently-to"))
 			NoRecipientAction = NRA_ADD_APPARENTLY_TO;
-		else if (sm_strcasecmp(val, "add-bcc") == 0)
+		else if (SM_STRCASEEQ(val, "add-bcc"))
 			NoRecipientAction = NRA_ADD_BCC;
-		else if (sm_strcasecmp(val, "add-to-undisclosed") == 0)
+		else if (SM_STRCASEEQ(val, "add-to-undisclosed"))
 			NoRecipientAction = NRA_ADD_TO_UNDISCLOSED;
 		else
 			syserr("Invalid NoRecipientAction: %s", val);
@@ -3908,11 +3988,11 @@ setoption(opt, val, safe, sticky, e)
 	  case O_RUNASUSER:	/* run bulk of code as this user */
 		for (p = val; *p != '\0'; p++)
 		{
-# if _FFR_DOTTED_USERNAMES
+#if _FFR_DOTTED_USERNAMES
 			if (*p == '/' || *p == ':')
-# else
+#else
 			if (*p == '.' || *p == '/' || *p == ':')
-# endif
+#endif
 			{
 				*p++ = '\0';
 				break;
@@ -3948,14 +4028,14 @@ setoption(opt, val, safe, sticky, e)
 						     (long) EffGid,
 						     (long) pw->pw_gid);
 		}
-# ifdef UID_MAX
+#ifdef UID_MAX
 		if (RunAsUid > UID_MAX)
 		{
 			syserr("readcf: option RunAsUser: uid value (%ld) > UID_MAX (%ld); ignored",
 				(long) RunAsUid, (long) UID_MAX);
 			break;
 		}
-# endif /* UID_MAX */
+#endif /* UID_MAX */
 		if (*p != '\0')
 		{
 			if (isascii(*p) && isdigit(*p))
@@ -4023,7 +4103,7 @@ setoption(opt, val, safe, sticky, e)
 			for (dbs = DontBlameSendmailValues;
 			     dbs->dbs_name != NULL; dbs++)
 			{
-				if (sm_strcasecmp(val, dbs->dbs_name) == 0)
+				if (SM_STRCASEEQ(val, dbs->dbs_name))
 					break;
 			}
 			if (dbs->dbs_name == NULL)
@@ -4037,7 +4117,7 @@ setoption(opt, val, safe, sticky, e)
 		break;
 
 	  case O_DPI:
-		if (sm_strcasecmp(val, "loopback") == 0)
+		if (SM_STRCASEEQ(val, "loopback"))
 			DontProbeInterfaces = DPI_SKIPLOOPBACK;
 		else if (atobool(val))
 			DontProbeInterfaces = DPI_PROBENONE;
@@ -4123,11 +4203,11 @@ setoption(opt, val, safe, sticky, e)
 		break;
 
 	  case O_TRUSTUSER:
-# if !HASFCHOWN && !defined(_FFR_DROP_TRUSTUSER_WARNING)
+#if !HASFCHOWN && !defined(_FFR_DROP_TRUSTUSER_WARNING)
 		if (!UseMSP)
 			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
 					     "readcf: option TrustedUser may cause problems on systems\n        which do not support fchown() if UseMSP is not set.\n");
-# endif /* !HASFCHOWN && !defined(_FFR_DROP_TRUSTUSER_WARNING) */
+#endif /* !HASFCHOWN && !defined(_FFR_DROP_TRUSTUSER_WARNING) */
 		if (isascii(*val) && isdigit(*val))
 			TrustedUid = atoi(val);
 		else
@@ -4145,14 +4225,14 @@ setoption(opt, val, safe, sticky, e)
 				TrustedUid = pw->pw_uid;
 		}
 
-# ifdef UID_MAX
+#ifdef UID_MAX
 		if (TrustedUid > UID_MAX)
 		{
 			syserr("readcf: option TrustedUser: uid value (%ld) > UID_MAX (%ld)",
 				(long) TrustedUid, (long) UID_MAX);
 			TrustedUid = 0;
 		}
-# endif /* UID_MAX */
+#endif /* UID_MAX */
 		break;
 
 	  case O_MAXMIMEHDRLEN:
@@ -4270,11 +4350,11 @@ setoption(opt, val, safe, sticky, e)
 				SASLOpts |= SASL_SEC_FORWARD_SECRECY;
 				break;
 
-#  if SASL >= 20101
+# if SASL >= 20101
 			  case 'm':
 				SASLOpts |= SASL_SEC_MUTUAL_AUTH;
 				break;
-#  endif /* SASL >= 20101 */
+# endif /* SASL >= 20101 */
 
 			  case 'p':
 				SASLOpts |= SASL_SEC_NOPLAINTEXT;
@@ -4337,18 +4417,23 @@ setoption(opt, val, safe, sticky, e)
 		SET_STRING_EXP(CACertFile);
 	  case O_CACERTPATH:
 		SET_STRING_EXP(CACertPath);
-#if _FFR_CLIENTCA
+# if _FFR_CLIENTCA
 	  case O_CLTCACERTFILE:
 		SET_STRING_EXP(CltCACertFile);
 	  case O_CLTCACERTPATH:
 		SET_STRING_EXP(CltCACertPath);
-#endif
+# endif
 	  case O_DHPARAMS:
 		SET_STRING_EXP(DHParams);
 	  case O_CIPHERLIST:
 		SET_STRING_EXP(CipherList);
+# if MTA_HAVE_TLSv1_3
+	  case O_CIPHERSUITES:
+		SET_STRING_EXP(CipherSuites);
+# endif
 	  case O_DIG_ALG:
 		SET_STRING_EXP(CertFingerprintAlgorithm);
+# if !defined(OPENSSL_NO_ENGINE)
 	  case O_SSLENGINEPATH:
 		SET_STRING_EXP(SSLEnginePath);
 	  case O_SSLENGINE:
@@ -4365,6 +4450,15 @@ setoption(opt, val, safe, sticky, e)
 		if (strcmp(SSLEngine, "chil") == 0)
 			SSLEngineprefork = true;
 		break;
+# else /* !defined(OPENSSL_NO_ENGINE) */
+	  case O_SSLENGINEPATH:
+	  case O_SSLENGINE:
+		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+				     "Warning: Option: %s ignored -- not supported: OPENSSL_NO_ENGINE\n",
+				     OPTNAME);
+		/* XXX fail? */
+		break;
+# endif /* !defined(OPENSSL_NO_ENGINE) */
 	  case O_SRV_SSL_OPTIONS:
 		pssloptions = &Srv_SSL_Options;
 	  case O_CLT_SSL_OPTIONS:
@@ -4445,10 +4539,10 @@ setoption(opt, val, safe, sticky, e)
 	  case O_CLTKEYFILE:
 	  case O_CACERTFILE:
 	  case O_CACERTPATH:
-#if _FFR_CLIENTCA
+# if _FFR_CLIENTCA
 	  case O_CLTCACERTFILE:
 	  case O_CLTCACERTPATH:
-#endif
+# endif
 	  case O_DHPARAMS:
 	  case O_SRV_SSL_OPTIONS:
 	  case O_CLT_SSL_OPTIONS:
@@ -4654,7 +4748,7 @@ setoption(opt, val, safe, sticky, e)
 
 #if DANE
 	  case O_DANE:
-		if (sm_strcasecmp(val, "always") == 0)
+		if (SM_STRCASEEQ(val, "always"))
 			Dane = DANE_ALWAYS;
 		else
 			Dane = atobool(val) ? DANE_SECURE : DANE_NEVER;
@@ -4673,11 +4767,33 @@ setoption(opt, val, safe, sticky, e)
 		break;
 #endif
 
-# if _FFR_TLS_ALTNAMES
+#if _FFR_TLS_ALTNAMES
 	  case O_CHECKALTNAMES:
 		SetCertAltnames = atobool(val);
 		break;
-# endif
+#endif
+	  case O_SMTPUTF8:
+#if USE_EAI
+		/* hack for testing */
+		if (isascii(*val) && isdigit(*val))
+			SMTPUTF8 = (int) strtol(val, NULL, 0);
+		else
+			SMTPUTF8 = atobool(val);
+#else
+		if (atobool(val))
+			syserr("readcf: option: %s set but no USE_EAI support",
+				     OPTNAME);
+		else
+			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+				     "Warning: Option: %s requires USE_EAI support\n",
+				     OPTNAME);
+#endif
+		break;
+#if _FFR_MTA_STS
+	  case O_MTASTS:
+		MTASTS = atobool(val);
+		break;
+#endif
 
 	  default:
 		if (tTd(37, 1))
@@ -5069,7 +5185,7 @@ settimeout(name, val, sticky)
 
 	for (to = TimeOutTab; to->to_name != NULL; to++)
 	{
-		if (sm_strcasecmp(to->to_name, name) == 0)
+		if (SM_STRCASEEQ(to->to_name, name))
 			break;
 	}
 
@@ -5423,3 +5539,25 @@ inittimeouts(val, sticky)
 		}
 	}
 }
+
+/*
+**  SHOWCFOPTS -- show cf options
+**
+**	Parameters:
+**		none
+**
+**	Returns:
+**		none.
+*/
+
+void
+showcfopts()
+{
+	struct optioninfo *o;
+
+	for (o = OptionTab; o->o_name != NULL; o++)
+	{
+		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+			"%s\n", o->o_name);
+	}
+}
diff --git a/contrib/sendmail/src/recipient.c b/contrib/sendmail/src/recipient.c
index eb325a6de385..a118f38c7168 100644
--- a/contrib/sendmail/src/recipient.c
+++ b/contrib/sendmail/src/recipient.c
@@ -15,6 +15,11 @@
 
 SM_RCSID("@(#)$Id: recipient.c,v 8.351 2013-11-22 20:51:56 ca Exp $")
 
+#include 
+#if _FFR_8BITENVADDR
+# include 
+#endif
+
 static void	includetimeout __P((int));
 static ADDRESS	*self_reference __P((ADDRESS *));
 static int	sortexpensive __P((ADDRESS *, ADDRESS *));
@@ -172,7 +177,7 @@ sendtolist(list, ctladdr, sendq, aliaslevel, e)
 	char *endp;
 	char *oldto = e->e_to;
 	char *SM_NONVOLATILE bufp;
-	char buf[MAXNAME + 1];
+	char buf[MAXNAME + 1];	/* EAI: ok, uses bufp dynamically expanded */
 
 	if (list == NULL)
 	{
@@ -225,6 +230,7 @@ sendtolist(list, ctladdr, sendq, aliaslevel, e)
 			while ((SM_ISSPACE(*p)) || *p == ',')
 				p++;
 			SM_ASSERT(p < endp);
+/* XXX p must be [i] */
 			a = parseaddr(p, NULLADDR, RF_COPYALL, delimiter,
 				      &delimptr, e, true);
 			p = delimptr;
@@ -342,7 +348,7 @@ removefromlist(list, sendq, e)
 	char *p;
 	char *oldto = e->e_to;
 	char *SM_NONVOLATILE bufp;
-	char buf[MAXNAME + 1];
+	char buf[MAXNAME + 1];	/* EAI: ok, uses bufp dynamically expanded */
 
 	if (list == NULL)
 	{
@@ -377,13 +383,17 @@ removefromlist(list, sendq, e)
 	{
 		(void) sm_strlcpy(bufp, denlstring(list, false, true), i);
 
-#if _FFR_ADDR_TYPE_MODES
+# if _FFR_ADDR_TYPE_MODES
 		if (AddrTypeModes)
 			macdefine(&e->e_macro, A_PERM, macid("{addr_type}"),
 				  "e r d");
 		else
-#endif /* _FFR_ADDR_TYPE_MODES */
-		macdefine(&e->e_macro, A_PERM, macid("{addr_type}"), "e r");
+# endif /* _FFR_ADDR_TYPE_MODES */
+		/* "else" in #if code above */
+		{
+			macdefine(&e->e_macro, A_PERM, macid("{addr_type}"),
+				  "e r");
+		}
 		for (p = bufp; *p != '\0'; )
 		{
 			ADDRESS a;	/* parsed address to be removed */
@@ -394,6 +404,7 @@ removefromlist(list, sendq, e)
 			/* parse the address */
 			while ((SM_ISSPACE(*p)) || *p == ',')
 				p++;
+			/* XXX p must be [i] */
 			if (parseaddr(p, &a, RF_COPYALL|RF_RM_ADDR,
 				      delimiter, &delimptr, e, true) == NULL)
 			{
@@ -467,7 +478,8 @@ recipient(new, sendq, aliaslevel, e)
 	int findusercount;
 	bool initialdontsend;
 	char *buf;
-	char buf0[MAXNAME + 1];		/* unquoted image of the user name */
+	char buf0[MAXNAME + 1]; /* EAI: ok, uses bufp dynamically expanded */
+		/* unquoted image of the user name */
 	sortfn_t *sortfn;
 
 	p = NULL;
@@ -508,8 +520,8 @@ recipient(new, sendq, aliaslevel, e)
 		p = e->e_from.q_mailer->m_addrtype;
 		if (p == NULL)
 			p = "rfc822";
-#if _FFR_EAI
-		if (sm_strcasecmp(p, "rfc822") == 0 &&
+#if USE_EAI
+		if (SM_STRCASEEQ(p, "rfc822") &&
 		    !addr_is_ascii(q->q_user))
 			p = "utf-8";
 #endif
@@ -1237,10 +1249,10 @@ finduser(name, fuzzyp, user)
 	(void) setpwent();
 	while ((pw = getpwent()) != NULL)
 	{
-		char buf[MAXNAME + 1];
+		char buf[MAXNAME + 1];	/* EAI: ok: for pw_gecos */
 
 # if 0
-		if (sm_strcasecmp(pw->pw_name, name) == 0)
+		if (SM_STRCASEEQ(pw->pw_name, name))
 		{
 			if (tTd(29, 4))
 				sm_dprintf("found (case wrapped)\n");
@@ -1249,7 +1261,7 @@ finduser(name, fuzzyp, user)
 # endif /* 0 */
 
 		sm_pwfullname(pw->pw_gecos, pw->pw_name, buf, sizeof(buf));
-		if (strchr(buf, ' ') != NULL && sm_strcasecmp(buf, name) == 0)
+		if (strchr(buf, ' ') != NULL && SM_STRCASEEQ(buf, name))
 		{
 			if (tTd(29, 4))
 				sm_dprintf("fuzzy matches %s\n", pw->pw_name);
@@ -1941,8 +1953,41 @@ sendtoargv(argv, e)
 {
 	register char *p;
 
+#if USE_EAI
+	if (!e->e_smtputf8)
+	{
+		char **av;
+
+		av = argv;
+		while ((p = *av++) != NULL)
+		{
+			if (!addr_is_ascii(p))
+			{
+				e->e_smtputf8 = true;
+				break;
+			}
+		}
+	}
+#endif /* USE_EAI */
+
 	while ((p = *argv++) != NULL)
+	{
+#if USE_EAI
+		if (e->e_smtputf8)
+		{
+			int len = 0;
+
+			if (!SMTPUTF8 && !asciistr(p))
+			{
+				usrerr("non-ASCII recipient address %s requires SMTPUTF8",
+					p);
+				finis(false, true, EX_USAGE);
+			}
+			p = quote_internal_chars(p, NULL, &len, NULL);
+		}
+#endif /* USE_EAI */
 		(void) sendtolist(p, NULLADDR, &e->e_sendqueue, 0, e);
+	}
 }
 
 /*
diff --git a/contrib/sendmail/src/savemail.c b/contrib/sendmail/src/savemail.c
index 945bcd702e51..0bf91fd5e06b 100644
--- a/contrib/sendmail/src/savemail.c
+++ b/contrib/sendmail/src/savemail.c
@@ -15,6 +15,8 @@
 
 SM_RCSID("@(#)$Id: savemail.c,v 8.319 2013-11-22 20:51:56 ca Exp $")
 
+#include 
+
 static bool	errbody __P((MCI *, ENVELOPE *, char *));
 static bool	pruneroute __P((char *));
 
@@ -513,7 +515,7 @@ returntosender(msg, returnq, flags, e)
 	static int returndepth = 0;
 	register ADDRESS *q;
 	char *p;
-	char buf[MAXNAME + 1];
+	char buf[MAXNAME + 1];	/* EAI:ok */
 
 	if (returnq == NULL)
 		return -1;
@@ -685,6 +687,7 @@ returntosender(msg, returnq, flags, e)
 
 	/* fake up an address header for the from person */
 	expand("\201n", buf, sizeof(buf), e);
+/* XXX buf must be [i] */
 	if (parseaddr(buf, &ee->e_from,
 		      RF_COPYALL|RF_SENDERADDR, '\0', NULL, e, false) == NULL)
 	{
@@ -697,6 +700,12 @@ returntosender(msg, returnq, flags, e)
 	ee->e_from.q_flags |= QPINGONFAILURE;
 	ee->e_sender = ee->e_from.q_paddr;
 
+#if USE_EAI
+	/* always? when is this really needed? */
+	if (e->e_smtputf8)
+		ee->e_smtputf8 = true;
+#endif
+
 	/* push state into submessage */
 	CurEnv = ee;
 	macdefine(&ee->e_macro, A_PERM, 'f', "\201n");
@@ -774,7 +783,7 @@ dsntypename(addrtype, addr)
 {
 	if (sm_strcasecmp(addrtype, "rfc822") != 0)
 		return addrtype;
-#if _FFR_EAI
+#if USE_EAI
 	if (!addr_is_ascii(addr))
 		return "utf-8";
 #endif
@@ -1120,13 +1129,13 @@ errbody(mci, e, separator)
 		(void) sm_strlcpyn(buf, sizeof(buf), 2, "--", e->e_msgboundary);
 		if (!putline("", mci) ||
 		    !putline(buf, mci) ||
-#if _FFR_EAI
-		    !putline(e->e_parent->e_smtputf8
-			     ? "Content-Type: message/global-delivery-status"
-			     : "Content-Type: message/delivery-status", mci) ||
-#else
-		    !putline("Content-Type: message/delivery-status", mci) ||
-#endif
+		    !putline(
+# if USE_EAI
+			e->e_parent->e_smtputf8
+			? "Content-Type: message/global-delivery-status"
+			:
+# endif
+			"Content-Type: message/delivery-status", mci) ||
 		    !putline("", mci))
 			goto writeerr;
 
@@ -1261,7 +1270,7 @@ errbody(mci, e, separator)
 				else
 					p = "rfc822";
 
-				if (sm_strcasecmp(p, "rfc822") == 0 &&
+				if (SM_STRCASEEQ(p, "rfc822") &&
 				    strchr(q->q_user, '@') == NULL)
 				{
 					(void) sm_snprintf(actual,
@@ -1294,7 +1303,7 @@ errbody(mci, e, separator)
 									   actual);
 			}
 
-#if _FFR_EAI
+# if USE_EAI
 			if (sm_strncasecmp("rfc822;", q->q_finalrcpt, 7) == 0 &&
 			    !addr_is_ascii(q->q_user))
 			{
@@ -1309,7 +1318,7 @@ errbody(mci, e, separator)
 				q->q_finalrcpt = sm_rpool_strdup_x(e->e_rpool,
 								   utf8rcpt);
 			}
-#endif
+# endif /* USE_EAI */
 
 			if (q->q_finalrcpt != NULL)
 			{
@@ -1436,21 +1445,20 @@ errbody(mci, e, separator)
 
 			if (!putline(buf, mci))
 				goto writeerr;
-#if _FFR_EAI
+#if USE_EAI
 			if (e->e_parent->e_smtputf8)
 				(void) sm_strlcpyn(buf, sizeof(buf), 2,
 						   "Content-Type: message/global",
 						   sendbody ? "" : "-headers");
 			else
+#endif /* USE_EAI */
+			/* "else" in #if code above */
+			{
 				(void) sm_strlcpyn(buf, sizeof(buf), 2,
-						   "Content-Type: ",
+						"Content-Type: ",
 						sendbody ? "message/rfc822"
 							 : "text/rfc822-headers");
-#else /* _FFR_EAI */
-			(void) sm_strlcpyn(buf, sizeof(buf), 2, "Content-Type: ",
-					sendbody ? "message/rfc822"
-						 : "text/rfc822-headers");
-#endif /* _FFR_EAI */
+			}
 			if (!putline(buf, mci))
 				goto writeerr;
 
@@ -1800,7 +1808,7 @@ isatom(s)
 {
 	int c;
 
-	if (s == NULL || *s == '\0')
+	if (SM_IS_EMPTY(s))
 		return false;
 	while ((c = *s++) != '\0')
 	{
diff --git a/contrib/sendmail/src/sendmail.8 b/contrib/sendmail/src/sendmail.8
index f525c3e29806..14e17a22635e 100644
--- a/contrib/sendmail/src/sendmail.8
+++ b/contrib/sendmail/src/sendmail.8
@@ -33,37 +33,37 @@ sendmail
 .B Sendmail
 sends a message to one or more
 .I recipients,
-routing the message over whatever networks 
+routing the message over whatever networks
 are necessary.
 .B Sendmail
-does internetwork forwarding as necessary 
+does internetwork forwarding as necessary
 to deliver the message to the correct place.
 .PP
 .B Sendmail
-is not intended as a user interface routine; 
-other programs provide user-friendly 
+is not intended as a user interface routine;
+other programs provide user-friendly
 front ends;
 .B sendmail
 is used only to deliver pre-formatted messages.
 .PP
 With no flags,
 .B sendmail
-reads its standard input 
-up to an end-of-file 
-or a line consisting only of a single dot 
+reads its standard input
+up to an end-of-file
+or a line consisting only of a single dot
 and sends a copy of the message found there
-to all of the addresses listed.  
-It determines the network(s) to use 
+to all of the addresses listed.
+It determines the network(s) to use
 based on the syntax and contents of the addresses.
 .PP
-Local addresses are looked up in a file 
+Local addresses are looked up in a file
 and aliased appropriately.
-Aliasing can be prevented by preceding the address 
+Aliasing can be prevented by preceding the address
 with a backslash.
-Beginning with 8.10, the sender is included in any alias 
-expansions, e.g., 
-if `john' sends to `group', 
-and `group' includes `john' in the expansion, 
+Beginning with 8.10, the sender is included in any alias
+expansions, e.g.,
+if `john' sends to `group',
+and `group' includes `john' in the expansion,
 then the letter will also be delivered to `john'.
 .SS Parameters
 .TP
@@ -78,18 +78,19 @@ an initial mail submission.
 .BI \-B type
 Set the body type to
 .IR type .
-Current legal values are 
-7BIT 
-or  
+Current legal values are
+7BIT
+or
 8BITMIME.
 .TP
 .B \-ba
-Go into 
-ARPANET 
-mode.  All input lines must end with a CR-LF, 
-and all messages will be generated with a CR-LF at the end.  
-Also, 
-the ``From:'' and ``Sender:'' 
+Go into
+ARPANET
+mode.
+All input lines must end with a CR-LF,
+and all messages will be generated with a CR-LF at the end.
+Also,
+the ``From:'' and ``Sender:''
 fields are examined for the name of the sender.
 .TP
 .B \-bC
@@ -98,11 +99,11 @@ Check the configuration file.
 .B \-bd
 Run as a daemon.
 .B Sendmail
-will fork and run in background 
+will fork and run in background
 listening on socket 25 for incoming
-SMTP 
-connections.  
-This is normally run from 
+SMTP
+connections.
+This is normally run from
 /etc/rc.
 .TP
 .B \-bD
@@ -130,24 +131,24 @@ Print number of entries in the queue(s);
 only available with shared memory support.
 .TP
 .B \-bs
-Use the 
-SMTP 
-protocol as described in 
-RFC821 
-on standard input and output.  
+Use the
+SMTP
+protocol as described in
+RFC821
+on standard input and output.
 This flag implies all the operations of the
 .B \-ba
-flag that are compatible with 
+flag that are compatible with
 SMTP.
 .TP
 .B \-bt
-Run in address test mode.  
-This mode reads addresses and shows the steps in parsing; 
+Run in address test mode.
+This mode reads addresses and shows the steps in parsing;
 it is used for debugging configuration tables.
 .TP
 .B \-bv
 Verify names only \- do not try to collect or deliver a message.
-Verify mode is normally used for validating 
+Verify mode is normally used for validating
 users or mailing lists.
 .TP
 .BI \-C file
@@ -188,7 +189,7 @@ source code.
 Set the full name of the sender.
 .TP
 .BI \-f name
-Sets the name of the ``from'' person 
+Sets the name of the ``from'' person
 (i.e., the envelope sender of the mail).
 This address may also be used in the From: header
 if that header is missing during initial submission.
@@ -196,13 +197,13 @@ The envelope sender address is used as the recipient
 for delivery status notifications
 and may also appear in a Return-Path: header.
 .B \-f
-should only be used 
-by ``trusted'' users 
+should only be used
+by ``trusted'' users
 (normally
 .IR root ", " daemon ,
 and
 .IR network )
-or if the person you are trying to become 
+or if the person you are trying to become
 is the same as the person you are.
 Otherwise,
 an X-Authentication-Warning header
@@ -218,12 +219,11 @@ calls
 .BI \-h N
 Set the hop count to
 .IR N .
-The hop count is incremented every time the mail is 
-processed.
-When it reaches a limit, 
+The hop count is incremented every time the mail is processed.
+When it reaches a limit,
 the mail is returned with an error message,
-the victim of an aliasing loop.  
-If not specified, 
+the victim of an aliasing loop.
+If not specified,
 ``Received:'' lines in the message are counted.
 .TP
 .B \-i
@@ -239,21 +239,21 @@ Set the identifier used in syslog messages to the supplied
 .BI "\-N " dsn
 Set delivery status notification conditions to
 .IR dsn ,
-which can be 
-`never' 
-for no notifications 
-or a comma separated list of the values 
-`failure' 
-to be notified if delivery failed, 
+which can be
+`never'
+for no notifications
+or a comma separated list of the values
+`failure'
+to be notified if delivery failed,
 `delay'
-to be notified if delivery is delayed, and 
-`success' 
+to be notified if delivery is delayed, and
+`success'
 to be notified when the message is successfully delivered.
 .TP
 .B \-n
 Don't do aliasing.
 .TP
-\fB\-O\fP \fIoption\fR=\fIvalue\fR 
+\fB\-O\fP \fIoption\fR=\fIvalue\fR
 Set option
 .I option
 to the specified
@@ -265,40 +265,40 @@ Set option
 .I x
 to the specified
 .IR value .
-This form uses single character names only.  
-The short names are not described in this manual page; 
+This form uses single character names only.
+The short names are not described in this manual page;
 see the
 .I "Sendmail Installation and Operation Guide"
 for details.
 .TP
 .BI \-p protocol
-Set the name of the protocol used to receive the message.  
-This can be a simple protocol name such as ``UUCP'' 
+Set the name of the protocol used to receive the message.
+This can be a simple protocol name such as ``UUCP''
 or a protocol and hostname, such as ``UUCP:ucbvax''.
 .TP
-\fB\-q\fR[\fItime\fR] 
-Process saved messages in the queue at given intervals.  
+\fB\-q\fR[\fItime\fR]
+Process saved messages in the queue at given intervals.
 If
 .I time
 is omitted, process the queue once.
 .I Time
-is given as a tagged number, 
-with 
-`s' 
-being seconds, 
-`m' 
-being minutes (default), 
-`h' 
-being hours, 
-`d' 
-being days, 
-and 
-`w' 
+is given as a tagged number,
+with
+`s'
+being seconds,
+`m'
+being minutes (default),
+`h'
+being hours,
+`d'
+being days,
+and
+`w'
 being weeks.
-For example, 
-`\-q1h30m' 
-or 
-`\-q90m' 
+For example,
+`\-q1h30m'
+or
+`\-q90m'
 would both set the timeout to one hour thirty minutes.
 By default,
 .B sendmail
@@ -306,7 +306,7 @@ will run in the background.
 This option can be used safely with
 .BR \-bd .
 .TP
-\fB\-qp\fR[\fItime\fR] 
+\fB\-qp\fR[\fItime\fR]
 Similar to \fB\-q\fItime\fR,
 except that instead of periodically forking a child to process the queue,
 sendmail forks a single persistent child for each queue
@@ -359,15 +359,15 @@ This should only be used with some sort of item matching using
 as described above.
 .TP
 .BI "\-R " return
-Set the amount of the message to be returned 
+Set the amount of the message to be returned
 if the message bounces.
 The
 .I return
-parameter can be 
-`full' 
-to return the entire message or 
-`hdrs' 
-to return only the headers.  
+parameter can be
+`full'
+to return the entire message or
+`hdrs'
+to return only the headers.
 In the latter case also local bounces return only the headers.
 .TP
 .BI \-r name
@@ -376,23 +376,39 @@ An alternate and obsolete form of the
 flag.
 .TP
 .B \-t
-Read message for recipients.  
-To:, Cc:, and Bcc: lines will be scanned for recipient addresses.  
+Read message for recipients.
+To:, Cc:, and Bcc: lines will be scanned for recipient addresses.
 The Bcc: line will be deleted before transmission.
 .TP
+.B \-U
+If a mail submission via the command line requires the use of the
+.B SMTPUTF8
+argument for the
+.B MAIL
+command,
+e.g., because a header uses UTF-8 encoding,
+but the addresses on the command line are all ASCII,
+then this option must be used.
+Only available if
+.B EAI
+support is enabled,
+and the
+.B SMTPUTF8
+option is set.
+.TP
 .BI "\-V " envid
-Set the original envelope id.  
-This is propagated across SMTP to servers that support DSNs 
+Set the original envelope id.
+This is propagated across SMTP to servers that support DSNs
 and is returned in DSN-compliant error messages.
 .TP
 .B \-v
-Go into verbose mode.  
+Go into verbose mode.
 Alias expansions will be announced, etc.
 .TP
 .BI "\-X " logfile
 Log all traffic in and out of mailers in the indicated log file.
-This should only be used as a last resort 
-for debugging mailer bugs.  
+This should only be used as a last resort
+for debugging mailer bugs.
 It will log a lot of data very quickly.
 .TP
 .B \-\-
@@ -400,17 +416,17 @@ Stop processing command flags and use the rest of the arguments as
 addresses.
 .SS Options
 There are also a number of processing options that may be set.
-Normally these will only be used by a system administrator.  
-Options may be set either on the command line 
+Normally these will only be used by a system administrator.
+Options may be set either on the command line
 using the
 .B \-o
 flag (for short names), the
 .B \-O
-flag (for long names), 
-or in the configuration file.  
+flag (for long names),
+or in the configuration file.
 This is a partial list limited to those options that are likely to be useful
-on the command line 
-and only shows the long names; 
+on the command line
+and only shows the long names;
 for a complete list (and details), consult the
 .IR "Sendmail Installation and Operation Guide" .
 The options are:
@@ -418,63 +434,62 @@ The options are:
 .RI AliasFile= file
 Use alternate alias file.
 .TP
-HoldExpensive 
-On mailers that are considered ``expensive'' to connect to, 
-don't initiate immediate connection.  
+HoldExpensive
+On mailers that are considered ``expensive'' to connect to,
+don't initiate immediate connection.
 This requires queueing.
 .TP
 .RI CheckpointInterval= N
 Checkpoint the queue file after every
 .I N
-successful deliveries (default 10).  
-This avoids excessive duplicate deliveries 
-when sending to long mailing lists 
+successful deliveries (default 10).
+This avoids excessive duplicate deliveries
+when sending to long mailing lists
 interrupted by system crashes.
 .ne 1i
 .TP
 .RI DeliveryMode= x
 Set the delivery mode to
 .IR x .
-Delivery modes are 
-`i' 
-for interactive (synchronous) delivery, 
+Delivery modes are
+`i'
+for interactive (synchronous) delivery,
 `b'
-for background (asynchronous) delivery, 
-`q' 
+for background (asynchronous) delivery,
+`q'
 for queue only \- i.e.,
-actual delivery is done the next time the queue is run, and 
+actual delivery is done the next time the queue is run, and
 `d'
-for deferred \- the same as 
-`q' 
+for deferred \- the same as
+`q'
 except that database lookups for maps which have set the \-D option
 (default for the host map) are avoided.
 .TP
 .RI ErrorMode= x
 Set error processing to mode
 .IR x .
-Valid modes are 
-`m' 
-to mail back the error message, 
-`w' 
+Valid modes are
+`m'
+to mail back the error message,
+`w'
 to ``write''
-back the error message 
-(or mail it back if the sender is not logged in), 
-`p' 
-to print the errors on the terminal 
-(default), 
-`q' 
-to throw away error messages 
-(only exit status is returned), 
-and 
+back the error message
+(or mail it back if the sender is not logged in),
+`p'
+to print the errors on the terminal
+(default),
+`q'
+to throw away error messages
+(only exit status is returned),
+and
 `e'
-to do special processing for the BerkNet.  
-If the text of the message is not mailed back 
-by 
-modes
+to do special processing for the BerkNet.
+If the text of the message is not mailed back
+by modes
 `m'
 or
 `w'
-and if the sender is local to this machine, 
+and if the sender is local to this machine,
 a copy of the message is appended to the file
 .I dead.letter
 in the sender's home directory.
@@ -485,16 +500,16 @@ UNIX-style
 From lines at the front of messages.
 .TP
 .RI MaxHopCount= N
-The maximum number of times a message is allowed to ``hop'' 
+The maximum number of times a message is allowed to ``hop''
 before we decide it is in a loop.
 .TP
 IgnoreDots
-Do not take dots on a line by themselves 
+Do not take dots on a line by themselves
 as a message terminator.
 .TP
 SendMimeErrors
-Send error messages in MIME format.  
-If not set, the DSN (Delivery Status Notification) SMTP extension 
+Send error messages in MIME format.
+If not set, the DSN (Delivery Status Notification) SMTP extension
 is disabled.
 .TP
 .RI ConnectionCacheTimeout= timeout
@@ -515,12 +530,12 @@ newaliases(1)
 command.
 .TP
 OldStyleHeaders
-If set, this message may have 
-old style headers.  
-If not set, 
-this message is guaranteed to have new style headers 
-(i.e., commas instead of spaces between addresses).  
-If set, an adaptive algorithm is used that will correctly 
+If set, this message may have
+old style headers.
+If not set,
+this message is guaranteed to have new style headers
+(i.e., commas instead of spaces between addresses).
+If set, an adaptive algorithm is used that will correctly
 determine the header format in most cases.
 .TP
 .RI QueueDirectory= queuedir
@@ -530,24 +545,24 @@ Select the directory in which to queue messages.
 Save statistics in the named file.
 .TP
 .RI Timeout.queuereturn= time
-Set the timeout on undelivered messages in the queue to the specified time.  
-After delivery has failed 
-(e.g., because of a host being down) 
-for this amount of time, 
-failed messages will be returned to the sender.  
+Set the timeout on undelivered messages in the queue to the specified time.
+After delivery has failed
+(e.g., because of a host being down)
+for this amount of time,
+failed messages will be returned to the sender.
 The default is five days.
 .TP
 .RI UserDatabaseSpec= userdatabase
 If set, a user database is consulted to get forwarding information.
-You can consider this an adjunct to the aliasing mechanism, 
-except that the database is intended to be distributed; 
-aliases are local to a particular host.  
+You can consider this an adjunct to the aliasing mechanism,
+except that the database is intended to be distributed;
+aliases are local to a particular host.
 This may not be available if your sendmail does not have the
 USERDB
 option compiled in.
 .TP
 ForkEachJob
-Fork each job during queue runs.  
+Fork each job during queue runs.
 May be convenient on memory-poor machines.
 .TP
 SevenBitInput
@@ -559,7 +574,7 @@ Set the handling of eight bit input to seven bit destinations to
 m
 (mimefy) will convert to seven-bit MIME format,
 p
-(pass) will pass it as eight bits (but violates protocols), 
+(pass) will pass it as eight bits (but violates protocols),
 and
 s
 (strict) will bounce the message.
@@ -568,16 +583,9 @@ s
 Sets how long a job must ferment in the queue between attempts to send it.
 .TP
 .RI DefaultCharSet= charset
-Sets the default character set used to label 8-bit data 
+Sets the default character set used to label 8-bit data
 that is not otherwise labelled.
 .TP
-.RI DialDelay= sleeptime
-If opening a connection fails, 
-sleep for
-.I sleeptime
-seconds and try again.  
-Useful on dial-on-demand sites.
-.TP
 .RI NoRecipientAction= action
 Set the behaviour when there are no recipient headers (To:, Cc: or
 Bcc:) in the message to
@@ -603,15 +611,14 @@ will allow to spawn at any time to
 Sets the maximum number of connections per second to the SMTP port to
 .IR N .
 .PP
-In aliases, 
-the first character of a name may be 
-a vertical bar to cause interpretation of 
-the rest of the name as a command 
-to pipe the mail to.  
-It may be necessary to quote the name 
-to keep
+In aliases,
+the first character of a name may be
+a vertical bar to cause interpretation of
+the rest of the name as a command
+to pipe the mail to.
+It may be necessary to quote the name to keep
 .B sendmail
-from suppressing the blanks from between arguments. 
+from suppressing the blanks from between arguments.
 For example, a common alias is:
 .IP
 msgs: "|/usr/bin/msgs -s"
@@ -620,7 +627,7 @@ Aliases may also have the syntax
 .RI ``:include: filename ''
 to ask
 .B sendmail
-to read the named file for a list of recipients.  
+to read the named file for a list of recipients.
 For example, an alias such as:
 .IP
 poets: ":include:/usr/local/lib/poets.list"
@@ -630,8 +637,7 @@ would read
 for the list of addresses making up the group.
 .PP
 .B Sendmail
-returns an exit status 
-describing what it did.  
+returns an exit status describing what it did.
 The codes are defined in
 .RI < sysexits.h >:
 .TP
@@ -642,18 +648,18 @@ EX_NOUSER
 User name not recognized.
 .TP
 EX_UNAVAILABLE
-Catchall meaning necessary resources 
+Catchall meaning necessary resources
 were not available.
 .TP
 EX_SYNTAX
 Syntax error in address.
 .TP
 EX_SOFTWARE
-Internal software error, 
+Internal software error,
 including bad arguments.
 .TP
 EX_OSERR
-Temporary operating system error, 
+Temporary operating system error,
 such as
 ``cannot fork''.
 .TP
@@ -661,7 +667,7 @@ EX_NOHOST
 Host name not recognized.
 .TP
 EX_TEMPFAIL
-Message could not be sent immediately, 
+Message could not be sent immediately,
 but was queued.
 .PP
 If invoked as
@@ -699,15 +705,14 @@ and your system security reduced by setting the
 .BR DontBlameSendmail
 option,
 the permission problems should be fixed.
-For more information, see:
-
-.I http://www.sendmail.org/tips/DontBlameSendmail.html
+For more information, see the
+.I "Sendmail Installation and Operation Guide"
 .SH FILES
 Except for the file
 .I /etc/mail/sendmail.cf
 itself the following pathnames are all specified in
 .IR /etc/mail/sendmail.cf .
-Thus, 
+Thus,
 these values are only approximations.
 .PP
 .TP
@@ -737,7 +742,7 @@ mail.local(8),
 rc(8),
 rmail(8)
 .PP
-DARPA 
+DARPA
 Internet Request For Comments
 .IR RFC819 ,
 .IR RFC821 ,
diff --git a/contrib/sendmail/src/sendmail.h b/contrib/sendmail/src/sendmail.h
index b51d4d0f076b..c0ede8f0a759 100644
--- a/contrib/sendmail/src/sendmail.h
+++ b/contrib/sendmail/src/sendmail.h
@@ -36,9 +36,6 @@
 #include 
 #include 
 #include 
-#ifdef EX_OK
-# undef EX_OK			/* for SVr4.2 SMP */
-#endif
 
 #include "sendmail/sendmail.h"
 
@@ -47,7 +44,20 @@
 # if _FFR_TLSA_DANE && !defined(DANE)
 #  define DANE _FFR_TLSA_DANE
 # endif
-#endif
+#else /* STARTTLS */
+# if DANE
+#  ERROR "DANE set but STARTTLS not defined"
+# endif
+# if _FFR_TLS_ALTNAMES
+#  ERROR "_FFR_TLS_ALTNAMES set but STARTTLS not defined"
+# endif
+# if _FFR_TLSFB2CLEAR
+#   ERROR "_FFR_TLSFB2CLEAR set but STARTTLS not defined"
+# endif
+# if _FFR_TLS_USE_CERTIFICATE_CHAIN_FILE
+#  ERROR "_FFR_TLS_USE_CERTIFICATE_CHAIN_FILE set but STARTTLS not defined"
+# endif
+#endif /* STARTTLS */
 
 /* profiling? */
 #if MONCONTROL
@@ -65,6 +75,7 @@ SM_UNUSED(static char SmailId[]) = "@(#)$Id: sendmail.h,v 8.1104 2013-11-22 20:5
 #include "bf.h"
 #include "timers.h"
 #include 
+#include 
 #include 
 #include 
 #include 
@@ -125,9 +136,42 @@ SM_UNUSED(static char SmailId[]) = "@(#)$Id: sendmail.h,v 8.1104 2013-11-22 20:5
 # endif
 #endif /* HESIOD */
 
-#if _FFR_EAI && !defined(ALLOW_255)
+#if USE_EAI && !defined(ALLOW_255)
 # define ALLOW_255 1
 #endif
+#if _FFR_EAI && _FFR_EIGHT_BIT_ADDR_OK
+# ERROR "Cannot enable both of these FFRs: _FFR_EAI _FFR_EIGHT_BIT_ADDR_OK"
+#endif
+
+#if _FFR_OCC && !SM_CONF_SHM
+# ERROR "_FFR_OCC requires SM_CONF_SHM"
+#endif
+
+#if _FFR_SM_LDAP_DBG && !(LDAPMAP && defined(LBER_OPT_LOG_PRINT_FN))
+# ERROR "_FFR_SM_LDAP_DBG requires LDAPMAP and LBER_OPT_LOG_PRINT_FN"
+#endif
+
+#if _FFR_LOG_MORE1 > 1 || _FFR_LOG_MORE2 > 1
+# if _FFR_LOG_MORE1 != _FFR_LOG_MORE2
+#  ERROR "_FFR_LOG_MORE1 != _FFR_LOG_MORE2"
+# endif
+#endif
+
+#if LDAP_NETWORK_TIMEOUT && !(LDAPMAP && defined(LDAP_OPT_NETWORK_TIMEOUT))
+# ERROR "LDAP_NETWORK_TIMEOUT requires LDAPMAP"
+#endif
+
+#if _FFR_VRFY_TRUSTED_FIRST && !defined(X509_V_FLAG_TRUSTED_FIRST)
+# ERROR "_FFR_VRFY_TRUSTED_FIRST set but X509_V_FLAG_TRUSTED_FIRST not defined"
+#endif
+
+#if _FFR_8BITENVADDR
+# define MAXNAME_I ((MAXNAME) * 2)
+#else
+# define MAXNAME_I MAXNAME
+#endif
+
+#define SM_IS_EMPTY(s)	(NULL == (s) || '\0' == *(s))
 
 #if STARTTLS
 # if DANE
@@ -147,7 +191,7 @@ struct dane_vrfy_ctx_S
 
 typedef struct dane_tlsa_S dane_tlsa_T, *dane_tlsa_P;
 typedef struct dane_vrfy_ctx_S dane_vrfy_ctx_T, *dane_vrfy_ctx_P;
-# endif
+# endif /* DANE */
 
 /* TLS information context */
 struct tlsi_ctx_S
@@ -163,8 +207,11 @@ typedef struct tlsi_ctx_S tlsi_ctx_T, *tlsi_ctx_P;
 /* TLS information context flags */
 #define TLSI_FL_CRLREQ	'R'	/* CRL required */
 #define TLSI_FL_FB2CLR	'C'	/* fall back to clear text is ok */
-#define TLSI_FL_NOFB2CLR	'c'	/* do not fall back to clear text */
+#define TLSI_FL_NOFB2CLR 'c'	/* do not fall back to clear text */
 #define TLSI_FL_NODANE	'd'	/* do not use/lookup DANE */
+#define TLSI_FL_NOSTS	'M'	/* do not use/lookup STS */
+/* internal */
+#define TLSI_FL_STS_NOFB2CLR	0x01	/* no clear text: STS is used */
 #define SM_TLSI_IS(tlsi_ctx, flag)	\
 	(((tlsi_ctx) != NULL) && bitnset((flag), (tlsi_ctx)->tlsi_flags))
 
@@ -172,12 +219,12 @@ typedef struct tlsi_ctx_S tlsi_ctx_T, *tlsi_ctx_P;
 # if _FFR_LOG_MORE1 > 1 || _FFR_LOG_MORE2 > 1
 #  define LOG_MORE_2(buf, bp)	\
 	p = macvalue(macid("{tls_version}"), e);	\
-	if (p == NULL || *p == '\0')	\
+	if (SM_IS_EMPTY(p))	\
 		p = "NONE";	\
 	(void) sm_snprintf(bp, SPACELEFT(buf, bp), ", tls_version=%.10s", p); \
 	bp += strlen(bp);	\
 	p = macvalue(macid("{cipher}"), e);	\
-	if (p == NULL || *p == '\0')	\
+	if (SM_IS_EMPTY(p))	\
 		p = "NONE";	\
 	(void) sm_snprintf(bp, SPACELEFT(buf, bp), ", cipher=%.20s", p); \
 	bp += strlen(bp);
@@ -187,7 +234,7 @@ typedef struct tlsi_ctx_S tlsi_ctx_T, *tlsi_ctx_P;
 
 # define LOG_MORE(buf, bp)	\
 	p = macvalue(macid("{verify}"), e);	\
-	if (p == NULL || *p == '\0')	\
+	if (SM_IS_EMPTY(p))	\
 		p = "NONE";	\
 	(void) sm_snprintf(bp, SPACELEFT(buf, bp), ", tls_verify=%.20s", p);	\
 	bp += strlen(bp);	\
@@ -218,14 +265,14 @@ typedef int (*sasl_callback_ft)(void);
 #   define SASL SASL_VERSION
 #  else /* SASL == 1 || SASL == 2 */
 #   if SASL != SASL_VERSION
-  ERROR README: -DSASL (SASL) does not agree with the version of the CYRUS_SASL library (SASL_VERSION)
-  ERROR README: see README!
+#    ERROR "README: -DSASL (SASL) does not agree with the version of the CYRUS_SASL library (SASL_VERSION)"
+#    ERROR "README: see README!"
 #   endif /* SASL != SASL_VERSION */
 #  endif /* SASL == 1 || SASL == 2 */
 # else /* defined(SASL_VERSION_MAJOR) && defined(SASL_VERSION_MINOR) && defined(SASL_VERSION_STEP) */
 #  if SASL == 1
-  ERROR README: please set -DSASL to the version of the CYRUS_SASL library
-  ERROR README: see README!
+#   ERROR "README: please set -DSASL to the version of the CYRUS_SASL library"
+#   ERROR "README: see README!"
 #  endif /* SASL == 1 */
 # endif /* defined(SASL_VERSION_MAJOR) && defined(SASL_VERSION_MINOR) && defined(SASL_VERSION_STEP) */
 #endif /* SASL */
@@ -358,6 +405,7 @@ typedef struct address ADDRESS;
 #define QDYNMAILER	0x00080000	/* "dynamic mailer" */
 #define QSECURE		0x00100000	/* DNSSEC ok */
 #define QQUEUED		0x00200000	/* queued */
+#define QINTREPLY	0x00400000	/* internally rejected (delivery) */
 #define QTHISPASS	0x40000000	/* temp: address set this pass */
 #define QRCPTOK		0x80000000	/* recipient() processed address */
 
@@ -423,6 +471,11 @@ typedef struct address ADDRESS;
 #define QS_IS_DEAD(s)		((s) >= QS_DONTSEND)
 #define QS_IS_TEMPFAIL(s)	((s) == QS_QUEUEUP || (s) == QS_RETRY)
 
+#define QUP_FL_NONE	0x0000
+#define QUP_FL_ANNOUNCE	0x0001
+#define QUP_FL_MSYNC	0x0002
+#define QUP_FL_UNLOCK	0x0004
+
 #define NULLADDR	((ADDRESS *) NULL)
 
 extern ADDRESS	NullAddress;	/* a null (template) address [main.c] */
@@ -509,9 +562,6 @@ struct mailer
 	short	m_qgrp;		/* queue group for this mailer */
 #if DANE
 	unsigned short	m_port;	/* port (if appropriate for mailer) */
-# define M_PORT(m)	((m)->m_port)
-#else
-# define M_PORT(m)	(-1)
 #endif
 };
 
@@ -633,8 +683,8 @@ struct queuegrp
 
 	char	*qg_qdir;	/* common component of queue directory */
 	short	qg_index;	/* queue number internally, index in Queue[] */
-	int	qg_maxqrun;	/* max # of jobs in 1 queuerun */
-	int	qg_numqueues;	/* number of queues in this queue */
+	int	qg_maxqrun;	/* max # of jobs in one queuerun */
+	int	qg_numqueues;	/* number of queues in this queue group */
 
 	/*
 	**  qg_queueintvl == 0 denotes that no individual value is used.
@@ -662,7 +712,7 @@ struct queuegrp
 #endif /* 0 */
 };
 
-/* bits for qg_flags (XXX: unused as of now) */
+/* bits for qg_flags */
 #define QD_DEFINED	((char) 1)	/* queue group has been defined */
 #define QD_FORK		'f'	/* fork queue runs */
 
@@ -717,7 +767,7 @@ extern bool	filesys_free __P((long));
 	(SASL_SEC_FORWARD_SECRECY & SASL_SEC_MASK) == 0 || \
 	(SASL_SEC_NOANONYMOUS & SASL_SEC_MASK) == 0 || \
 	(SASL_SEC_PASS_CREDENTIALS & SASL_SEC_MASK) == 0
-ERROR: change SASL_SEC_MASK_ notify sendmail.org!
+#   ERROR "change SASL_SEC_MASK notify sendmail.org!"
 #  endif /* SASL_SEC_NOPLAINTEXT & SASL_SEC_MASK) == 0 ... */
 # endif /* SASL >= 20101 */
 # define MAXOUTLEN 8192	/* length of output buffer, should be 2^n */
@@ -794,8 +844,8 @@ MCI
 	bool		mci_retryrcpt;	/* tempfail for at least one rcpt */
 	char		*mci_tolist;	/* list of valid recipients */
 	SM_RPOOL_T	*mci_rpool;	/* resource pool */
-#if PIPELINING
 	int		mci_okrcpts;	/* number of valid recipients */
+#if PIPELINING
 	ADDRESS		*mci_nextaddr;	/* next address for pipelined status */
 #endif
 #if SASL
@@ -856,7 +906,7 @@ MCI
 #else
 # define MCIF_NOTSTICKY	0
 #endif
-#if _FFR_EAI
+#if USE_EAI
 # define MCIF_EAI	0x40000000	/* SMTPUTF8 supported */
 #else
 # define MCIF_EAI	0x00000000	/* for MCIF_EXTENS */
@@ -949,6 +999,9 @@ extern struct hdrinfo	HdrInfo[];
 #define H_STRIPCOMM	0x00010000	/* header check: strip comments */
 #define H_BINDLATE	0x00020000	/* only expand macros at deliver */
 #define H_USER		0x00040000	/* header came from the user/SMTP */
+#if _FFR_MTA_MODE
+# define H_ASIS		0x10000000
+#endif
 
 /* bits for chompheader() */
 #define CHHDR_DEF	0x0001	/* default header */
@@ -1001,8 +1054,8 @@ struct envelope
 	ADDRESS		e_from;		/* the person it is from */
 	char		*e_sender;	/* e_from.q_paddr w comments stripped */
 	char		**e_fromdomain;	/* the domain part of the sender */
-#if _FFR_EAI
-	bool		e_smtputf8; /* whether the sender demanded SMTPUTF8 */
+#if USE_EAI
+	bool		e_smtputf8;	/* requires SMTPUTF8? */
 #endif
 	ADDRESS		*e_sendqueue;	/* list of message recipients */
 	ADDRESS		*e_errorqueue;	/* the queue for error responses */
@@ -1189,8 +1242,13 @@ struct rewrite
 **		cannot conflict.
 */
 
-/* "out of band" indicator */
-/* sm/sendmail.h #define METAQUOTE ((unsigned char)0377) quotes the next octet */
+/*
+**  "out of band" indicator
+**  sm/sendmail.h #define METAQUOTE ((unsigned char)0377)
+**  quotes the next octet
+**  range: ((ch) & 0340) == 0200
+**  see #define SM_MM_QUOTE(ch) in libsm/util.c
+*/
 
 /* left hand side items */
 #define MATCHZANY	((unsigned char)0220)	/* match zero or more tokens */
@@ -1432,6 +1490,7 @@ MAP
 #define MF_OPENBOGUS	0x00800000	/* open failed, don't call map_close */
 #define MF_CLOSING	0x01000000	/* map is being closed */
 #define MF_SECURE	0x02000000	/* DNSSEC result is "secure" */
+#define MF_KEEPXFMT	0x04000000	/* keep [x] format */
 
 #define DYNOPENMAP(map) \
 	do		\
@@ -1606,6 +1665,7 @@ extern bool	iptostring __P((SOCKADDR *, SOCKADDR_LEN_T, char *, unsigned));
 #define PROC_QUEUE_CHILD	3
 #define PROC_CONTROL		4
 #define PROC_CONTROL_CHILD	5
+#define PROC_QM			6
 
 /* functions */
 extern void	proc_list_add __P((pid_t, char *, int, int, int, SOCKADDR *));
@@ -1756,6 +1816,7 @@ extern void	stabapply __P((void (*)(STAB *, int), int));
 #define MD_PURGESTAT	'H'		/* purge persistent host stat info */
 #define MD_QUEUERUN	'q'		/* queue run */
 #define MD_CHECKCONFIG	'C'		/* check configuration file */
+#define MD_SHOWCONFIG	'O'		/* show cf options */
 
 #if _FFR_LOCAL_DAEMON
 EXTERN bool	LocalDaemon;
@@ -1779,12 +1840,18 @@ EXTERN bool	V6LoopbackAddrFound;	/* found an IPv6 loopback address */
 /* values for e_sendmode -- send modes */
 #define SM_DELIVER	'i'		/* interactive delivery */
 #if _FFR_PROXY
-#define SM_PROXY_REQ	's'		/* synchronous mode requested */
-#define SM_PROXY	'S'		/* synchronous mode activated */
+# define SM_PROXY_REQ	's'		/* synchronous mode requested */
+# define SM_PROXY	'S'		/* synchronous mode activated */
 #endif
 #define SM_FORK		'b'		/* deliver in background */
 #if _FFR_DM_ONE
-#define SM_DM_ONE	'o' /* deliver first TA in background, then queue */
+# define SM_DM_ONE	'o' /* deliver first TA in background, then queue */
+#endif
+#if _FFR_DMTRIGGER
+# define SM_TRIGGER	't'		/* queue and tell "queue manager" */
+# define IS_SM_TRIGGER(m)	((m) == SM_TRIGGER)
+#else
+# define IS_SM_TRIGGER(m)	false
 #endif
 #define SM_QUEUE	'q'		/* queue, don't deliver */
 #define SM_DEFER	'd'		/* defer map lookups as well as queue */
@@ -1796,7 +1863,7 @@ EXTERN bool	V6LoopbackAddrFound;	/* found an IPv6 loopback address */
 # define SM_IS_INTERACTIVE(m)	((m) == SM_DELIVER)
 #endif
 
-#define WILL_BE_QUEUED(m)	((m) == SM_QUEUE || (m) == SM_DEFER)
+#define WILL_BE_QUEUED(m)	((m) == SM_QUEUE || (m) == SM_DEFER || IS_SM_TRIGGER(m))
 
 /* used only as a parameter to sendall */
 #define SM_DEFAULT	'\0'		/* unspecified, use SendMode */
@@ -1854,9 +1921,10 @@ extern void	set_delivery_mode __P((int, ENVELOPE *));
 #define PRIV_NOBODYRETN		0x00100000	/* do not return bodies on bounces */
 #define PRIV_NORECEIPTS		0x00200000	/* disallow return receipts */
 #define PRIV_NOACTUALRECIPIENT	0x00400000 /* no X-Actual-Recipient in DSNs */
+#define PRIV_NOREFLECTION	0x00800000 /* do not show original command */
 
 /* don't give no info, anyway, anyhow (in the main SMTP transaction) */
-#define PRIV_GOAWAY		0x0000ffff
+#define PRIV_GOAWAY		(0x0000ffff|PRIV_NOREFLECTION)
 
 /* struct defining such things */
 struct prival
@@ -1881,6 +1949,7 @@ EXTERN unsigned long	PrivacyFlags;	/* privacy flags */
 #define RF_COPYALL		(RF_COPYPARSE|RF_COPYPADDR)
 #define RF_COPYNONE		0
 #define RF_RM_ADDR		0x040	/* address to be removed */
+#define RF_IS_EXT		0x100	/* address is in external format */
 
 /*
 **  Flags passed to rscheck
@@ -2038,15 +2107,14 @@ struct termescape
 #define D_CANONREQ	'c'	/* canonification required (cf) */
 #define D_IFNHELO	'h'	/* use if name for HELO */
 #define D_FQMAIL	'f'	/* fq sender address required (cf) */
-#if _FFR_EAI
-#define D_EAI		'I'	/* EAI supported */
-#endif
 #define D_FQRCPT	'r'	/* fq recipient address required (cf) */
 #define D_SMTPS		's'	/* SMTP over SSL (smtps) */
 #define D_UNQUALOK	'u'	/* unqualified address is ok (cf) */
 #define D_NOAUTH	'A'	/* no AUTH */
 #define D_NOCANON	'C'	/* no canonification (cf) */
+#define D_NODANE	'D'	/* no DANE (client) */
 #define D_NOETRN	'E'	/* no ETRN (MSA) */
+#define D_NOSTS		'M'	/* no MTA-STS (client) */
 #define D_NOTLS		'S'	/* don't use STARTTLS */
 #define D_ETRNONLY	((char)0x01)	/* allow only ETRN (disk low) */
 #define D_OPTIONAL	'O'	/* optional socket */
@@ -2160,7 +2228,7 @@ extern char	*qid_printname __P((ENVELOPE *));
 extern char	*qid_printqueue __P((int, int));
 extern void	quarantine_queue __P((char *, int));
 extern char	*queuename __P((ENVELOPE *, int));
-extern void	queueup __P((ENVELOPE *, bool, bool));
+extern void	queueup __P((ENVELOPE *, unsigned int));
 extern bool	runqueue __P((bool, bool, bool, bool));
 extern bool	run_work_group __P((int, int));
 extern void	set_def_queueval __P((QUEUEGRP *, bool));
@@ -2176,6 +2244,9 @@ extern void	sync_dir __P((char *, bool));
 #else
 # define SYNC_DIR(path, panic) ((void) 0)
 #endif
+#if _FFR_DMTRIGGER
+extern bool	qm __P((void));
+#endif
 
 /*
 **  Timeouts
@@ -2465,6 +2536,10 @@ EXTERN char	*MessageAccept; /* "Message accepted for delivery" reply text */
 #endif
 
 EXTERN int	MimeMode;	/* MIME processing mode */
+#if _FFR_MTA_STS
+EXTERN bool	MTASTS;
+EXTERN char	*STS_SNI;
+#endif
 EXTERN int	NoRecipientAction;
 
 #if SM_CONF_SHM
@@ -2484,6 +2559,11 @@ EXTERN long	RefuseLowMem;	/* low memory refusing connections */
 EXTERN char	*MemoryResource;/* memory resource to look up */
 #endif /* _FFR_MEMSTAT */
 EXTERN int	SuperSafe;	/* be extra careful, even if expensive */
+#if USE_EAI
+EXTERN int	SMTPUTF8;	/* enable SMTPUTF8 support */
+#else
+# define SMTPUTF8	false
+#endif
 EXTERN int	VendorCode;	/* vendor-specific operation enhancements */
 EXTERN int	Verbose;	/* set if blow-by-blow desired */
 EXTERN gid_t	DefGid;		/* default gid to run as */
@@ -2607,6 +2687,7 @@ extern void NR_PRINTFLIKE(2, 3)	usrerrenh __P((char *, const char *, ...));
 extern void NR_PRINTFLIKE(1, 2)	usrerr __P((const char *, ...));
 extern int	isenhsc __P((const char *, int));
 extern int	extenhsc __P((const char *, int, char *));
+extern int	skipaddrhost __P((const char *, bool));
 
 /* alias file */
 extern void	alias __P((ADDRESS *, ADDRESS **, int, ENVELOPE *));
@@ -2628,6 +2709,7 @@ extern void	smtp __P((char *volatile, BITMAP256, ENVELOPE *volatile));
 #if SASL
 extern int	smtpauth __P((MAILER *, MCI *, ENVELOPE *));
 #endif
+extern void	smtpclrse __P((ENVELOPE *));
 extern int	smtpdata __P((MAILER *, MCI *, ENVELOPE *, ADDRESS *, time_t));
 extern int	smtpgetstat __P((MAILER *, MCI *, ENVELOPE *));
 extern int	smtpmailfrom __P((MAILER *, MCI *, ENVELOPE *));
@@ -2769,7 +2851,8 @@ extern bool	lockfile __P((int, char *, char *, int));
 extern void	log_sendmail_pid __P((ENVELOPE *));
 extern void	logundelrcpts __P((ENVELOPE *, char *, int, bool));
 extern char	lower __P((int));
-extern void	makelower __P((char *));
+extern char	*makelower_a __P((char **, SM_RPOOL_T *));
+extern void	makelower_buf __P((char *, char *, int));
 extern int	makeconnection_ds __P((char *, MCI *));
 #if DANE
 extern int	makeconnection __P((char *, volatile unsigned int, MCI *, ENVELOPE *, time_t, unsigned long *));
@@ -2782,7 +2865,13 @@ extern void	mark_work_group_restart __P((int, int));
 extern MCI	*mci_new __P((SM_RPOOL_T *));
 extern char	*munchstring __P((char *, char **, int));
 extern struct hostent	*myhostname __P((char *, int));
+#if SM_HEAP_CHECK > 2
+extern char	*newstr_tagged __P((const char *, char *, int, int));
+# define newstr(str) newstr_tagged(str, "newstr:" __FILE__, __LINE__, SmHeapGroup)
+#else
 extern char	*newstr __P((const char *));
+# define newstr_tagged(str, file, line, grp) newstr(str)
+#endif
 #if NISPLUS
 extern char	*nisplus_default_domain __P((void));	/* extern for Sun */
 #endif
@@ -2829,6 +2918,7 @@ extern char	*sfgets __P((char *, int, SM_FILE_T *, time_t, char *));
 extern char	*shortenstring __P((const char *, size_t));
 extern char	*shorten_hostname __P((char []));
 extern bool	shorten_rfc822_string __P((char *, size_t));
+extern void	showcfopts __P((void));
 extern void	shutdown_daemon __P((void));
 extern void	sm_closefrom __P((int lowest, int highest));
 extern void	sm_close_on_exec __P((int lowest, int highest));
@@ -2889,13 +2979,35 @@ extern bool	xtextok __P((char *));
 extern int	xunlink __P((char *));
 extern char	*xuntextify __P((char *));
 
-#if _FFR_EAI
+#define ASSIGN_IFDIFF(old, new)		\
+	do				\
+	{				\
+		if ((new) != (old))	\
+		{			\
+			SM_FREE(old);	\
+			old = new;	\
+			new = NULL;	\
+		}			\
+	} while (0);
+
+#if USE_EAI
 extern bool	addr_is_ascii __P((const char *));
+extern const char	*hn2alabel __P((const char *));
 #endif
 
 #if _FFR_RCPTFLAGS
 extern bool	newmodmailer __P((ADDRESS *, int));
 #endif
 
+#define SM_CLOSE_FP(fp)			\
+	do				\
+	{				\
+		if ((fp) != NULL)	\
+		{			\
+			(void) sm_io_close((fp), SM_TIME_DEFAULT);	\
+			fp = NULL;	\
+		}			\
+	} while (0);
+
 #undef EXTERN
 #endif /* ! _SENDMAIL_H */
diff --git a/contrib/sendmail/src/sfsasl.c b/contrib/sendmail/src/sfsasl.c
index 3335608e450c..7e6b1d141532 100644
--- a/contrib/sendmail/src/sfsasl.c
+++ b/contrib/sendmail/src/sfsasl.c
@@ -154,11 +154,7 @@ sasl_close(fp)
 	so = (struct sasl_obj *) fp->f_cookie;
 	if (so == NULL)
 		return 0;
-	if (so->fp != NULL)
-	{
-		sm_io_close(so->fp, SM_TIME_DEFAULT);
-		so->fp = NULL;
-	}
+	SM_CLOSE_FP(so->fp);
 	sm_free(so);
 	so = NULL;
 	return 0;
@@ -544,11 +540,7 @@ tls_close(fp)
 	so = (struct tls_obj *) fp->f_cookie;
 	if (so == NULL)
 		return 0;
-	if (so->fp != NULL)
-	{
-		sm_io_close(so->fp, SM_TIME_DEFAULT);
-		so->fp = NULL;
-	}
+	SM_CLOSE_FP(so->fp);
 	sm_free(so);
 	so = NULL;
 	return 0;
@@ -672,11 +664,11 @@ tls_retry(ssl, rfd, wfd, tlsstart, timeout, err, where)
 }
 
 /* errno to force refill() etc to stop (see IS_IO_ERROR()) */
-#ifdef ETIMEDOUT
-# define SM_ERR_TIMEOUT	ETIMEDOUT
-#else
-# define SM_ERR_TIMEOUT	EIO
-#endif
+# ifdef ETIMEDOUT
+#  define SM_ERR_TIMEOUT	ETIMEDOUT
+# else
+#  define SM_ERR_TIMEOUT	EIO
+# endif
 
 /*
 **  SET_TLS_RD_TMO -- read secured information for the caller
@@ -770,10 +762,10 @@ tls_read(fp, buf, size)
 		err = "syscall error";
 		break;
 	  case SSL_ERROR_SSL:
-#if DEAL_WITH_ERROR_SSL
+# if DEAL_WITH_ERROR_SSL
 		if (r == 0 && errno == 0) /* out of protocol EOF found */
 			break;
-#endif
+# endif
 		err = "generic SSL error";
 
 		if (LogLevel > 9)
@@ -787,11 +779,11 @@ tls_read(fp, buf, size)
 			tlslogerr(pri, 9, "read");
 		}
 
-#if DEAL_WITH_ERROR_SSL
+# if DEAL_WITH_ERROR_SSL
 		/* avoid repeated calls? */
 		if (r == 0)
 			r = -1;
-#endif
+# endif
 		break;
 	}
 	if (err != NULL)
@@ -898,11 +890,11 @@ tls_write(fp, buf, size)
 */
 		tlslogerr(LOG_WARNING, 9, "write");
 
-#if DEAL_WITH_ERROR_SSL
+# if DEAL_WITH_ERROR_SSL
 		/* avoid repeated calls? */
 		if (r == 0)
 			r = -1;
-#endif
+# endif
 		break;
 	}
 	if (err != NULL)
diff --git a/contrib/sendmail/src/sm_resolve.c b/contrib/sendmail/src/sm_resolve.c
index 79e4168715a3..977b34103550 100644
--- a/contrib/sendmail/src/sm_resolve.c
+++ b/contrib/sendmail/src/sm_resolve.c
@@ -42,6 +42,7 @@
  */
 
 #include 
+#include 
 #if DNSMAP || DANE
 # if NAMED_BIND
 #  if NETINET
@@ -111,7 +112,7 @@ dns_string_to_type(name)
 	struct stot *p = stot;
 
 	for (p = stot; p->st_name != NULL; p++)
-		if (sm_strcasecmp(name, p->st_name) == 0)
+		if (SM_STRCASEEQ(name, p->st_name))
 			return p->st_type;
 	return -1;
 }
@@ -461,13 +462,16 @@ parse_dns_reply(data, len, flags)
 
 #  if DNSSEC_TEST
 
-#include 
+#   include 
+#   if _FFR_8BITENVADDR
+#    include 
+#   endif
 
 static int gen_dns_reply __P((unsigned char *, int, unsigned char *,
 		const char *, int, const char *, int, int, int, int,
 		const char *, int, int, int));
 static int dnscrtrr __P((const char *, const char *, int, char *, int,
-	unsigned int, int *, int *, unsigned char *, int,  unsigned char *));
+	unsigned int, int *, int *, unsigned char *, int, unsigned char *));
 
 /*
 **  HERRNO2TXT -- return error text for h_errno
@@ -548,6 +552,16 @@ gen_dns_reply(buf, buflen, bufpos, query, qtype, domain, class, type, ttl, size,
 	int n;
 	static unsigned char *dnptrs[20], **dpp, **lastdnptr;
 
+#define DN_COMP_CHK	do	\
+	{	\
+		if (n < 0)	\
+		{	\
+			if (tTd(8, 91))	\
+				sm_dprintf("gen_dns_reply: dn_comp=%d\n", n); \
+			return n;	\
+		}	\
+	} while (0)
+
 	SM_REQUIRE(NULL != buf);
 	SM_REQUIRE(buflen >= HFIXEDSZ);
 	SM_REQUIRE(query != NULL);
@@ -579,8 +593,7 @@ gen_dns_reply(buf, buflen, bufpos, query, qtype, domain, class, type, ttl, size,
 		hp->ancount = 0;
 
 		n = dn_comp(query, cp, ep - cp - QFIXEDSZ, dnptrs, lastdnptr);
-		if (n < 0)
-			return n;
+		DN_COMP_CHK;
 		cp += n;
 		PUTSHORT(qtype, cp);
 		PUTSHORT(class, cp);
@@ -588,10 +601,14 @@ gen_dns_reply(buf, buflen, bufpos, query, qtype, domain, class, type, ttl, size,
 	hp->ad = ad;
 
 	if (ep - cp < QFIXEDSZ)
+	{
+		if (tTd(8, 91))
+			sm_dprintf("gen_dns_reply: ep-cp=%ld\n",
+				(long) (ep - cp));
 		return (-1);
+	}
 	n = dn_comp(domain, cp, ep - cp - QFIXEDSZ, dnptrs, lastdnptr);
-	if (n < 0)
-		return n;
+	DN_COMP_CHK;
 	cp += n;
 	PUTSHORT(type, cp);
 	PUTSHORT(class, cp);
@@ -605,8 +622,7 @@ gen_dns_reply(buf, buflen, bufpos, query, qtype, domain, class, type, ttl, size,
 	{
 	  case T_MX:
 		n = dn_comp(data, cp + 4, ep - cp - QFIXEDSZ, dnptrs, lastdnptr);
-		if (n < 0)
-			return n;
+		DN_COMP_CHK;
 		PUTSHORT(n + 2, cp);
 		PUTSHORT(pref, cp);
 		cp += n;
@@ -622,8 +638,7 @@ gen_dns_reply(buf, buflen, bufpos, query, qtype, domain, class, type, ttl, size,
 
 	  case T_CNAME:
 		n = dn_comp(data, cp + 2, ep - cp - QFIXEDSZ, dnptrs, lastdnptr);
-		if (n < 0)
-			return n;
+		DN_COMP_CHK;
 		PUTSHORT(n, cp);
 		cp += n;
 		break;
@@ -672,7 +687,7 @@ setherrnofromstring(str, prc)
 	int *prc;
 {
 	SM_SET_H_ERRNO(0);
-	if (str == NULL || *str == '\0')
+	if (SM_IS_EMPTY(str))
 		return 0;
 	if (strstr(str, "herrno:") == NULL)
 		return 0;
@@ -710,7 +725,7 @@ int
 getttlfromstring(str)
 	const char *str;
 {
-	if (str == NULL || *str == '\0')
+	if (SM_IS_EMPTY(str))
 		return 0;
 #define TTL_PRE "ttl="
 	if (strstr(str, TTL_PRE) == NULL)
@@ -718,6 +733,33 @@ getttlfromstring(str)
 	return strtoul(str + strlen(TTL_PRE), NULL, 10);
 }
 
+/*
+**  DNS_SETNS -- set one NS in resolver context
+**
+**	Parameters:
+**		ns -- (IPv4 address of) nameserver
+**		port -- nameserver port
+**
+**	Returns:
+**		None.
+*/
+
+static void dns_setns __P((struct in_addr *, unsigned int));
+
+static void
+dns_setns(ns, port)
+	struct in_addr *ns;
+	unsigned int port;
+{
+	_res.nsaddr_list[0].sin_family = AF_INET;
+	_res.nsaddr_list[0].sin_addr = *ns;
+	if (port != 0)
+		_res.nsaddr_list[0].sin_port = htons(port);
+	_res.nscount = 1;
+	if (tTd(8, 61))
+		sm_dprintf("dns_setns(%s,%u)\n", inet_ntoa(*ns), port);
+}
+
 /*
 **  NSPORTIP -- parse port@IPv4 and set NS accordingly
 **
@@ -746,7 +788,7 @@ nsportip(p)
 	unsigned short port;
 	struct in_addr nsip;
 
-	if (p == NULL || *p == '\0')
+	if (SM_IS_EMPTY(p))
 		return -1;
 
 	port = 0;
@@ -778,31 +820,6 @@ nsportip(p)
 	return r > 0 ? 0 : -1;
 }
 
-/*
-**  DNS_SETNS -- set one NS in resolver context
-**
-**	Parameters:
-**		ns -- (IPv4 address of) nameserver
-**		port -- nameserver port
-**
-**	Returns:
-**		None.
-*/
-
-void
-dns_setns(ns, port)
-	struct in_addr *ns;
-	unsigned int port;
-{
-	_res.nsaddr_list[0].sin_family = AF_INET;
-	_res.nsaddr_list[0].sin_addr = *ns;
-	if (port != 0)
-		_res.nsaddr_list[0].sin_port = htons(port);
-	_res.nscount = 1;
-	if (tTd(8, 61))
-		sm_dprintf("dns_setns(%s,%u)\n", inet_ntoa(*ns), port);
-}
-
 #   if defined(T_TLSA)
 /*
 **  HEX2BIN -- convert hex string to binary TLSA RR
@@ -914,7 +931,7 @@ dnscrtrr(domain, query, qtype, value, rr_type, flags, herr, adp, answer, anslen,
 	char rhs[MAXLINE];
 
 	rlen = -1;
-	if (NULL == value || '\0' == *value)
+	if (SM_IS_EMPTY(value))
 		return rlen;
 	SM_REQUIRE(adp != NULL);
 	(void) sm_strlcpy(rhs, value, sizeof(rhs));
@@ -997,8 +1014,8 @@ dnscrtrr(domain, query, qtype, value, rr_type, flags, herr, adp, answer, anslen,
 				query, qtype, domain, C_IN, rr_type, ttl,
 				strlen(token) + 1, token, 0, pref, ad);
 			if (tTd(8, 50))
-				sm_dprintf("dnscrtrr: mx=%s, pref=%d\n",
-					token, pref);
+				sm_dprintf("dnscrtrr: mx=%s, pref=%d, rlen=%d\n",
+					token, pref, rlen);
 		}
 
 #   ifdef T_TLSA
@@ -1059,7 +1076,13 @@ tstdns_search(domain, class, type, answer, anslen)
 	const char *tag;
 	char *av[2];
 	STAB *map;
-	char key[MAXNAME + 16];
+#   if _FFR_8BITENVADDR
+	char qbuf[MAXNAME_I];
+	char *qdomain;
+#   else
+#    define qdomain domain
+#   endif
+	char key[MAXNAME_I + 16];
 	char rhs[MAXLINE];
 	unsigned char *anspos;
 
@@ -1067,7 +1090,7 @@ tstdns_search(domain, class, type, answer, anslen)
 	herr = 0;
 	if (class != C_IN)
 		goto error;
-	if (NULL == domain || '\0' == *domain)
+	if (SM_IS_EMPTY(domain))
 		goto error;
 	tag = rr_type2tag(type);
 	if (tag == NULL)
@@ -1075,7 +1098,17 @@ tstdns_search(domain, class, type, answer, anslen)
 	maprcode = EX_OK;
 	ad = -1;
 	flags = 0;
+#   if _FFR_8BITENVADDR
+	if (tTd(8, 62))
+		sm_dprintf("domain=%s\n", domain);
+	(void) dequote_internal_chars((char *)domain, qbuf, sizeof(qbuf));
+	query = qbuf;
+	qdomain = qbuf;
+	if (tTd(8, 63))
+		sm_dprintf("qdomain=%s\n", qdomain);
+#   else
 	query = domain;
+#   endif /* _FFR_8BITENVADDR */
 	anspos = NULL;
 
 	map = stab("access", ST_MAP, ST_FIND);
@@ -1100,17 +1133,17 @@ tstdns_search(domain, class, type, answer, anslen)
 	do {	\
 		int len;	\
 				\
-		len = strlen(domain);	\
+		len = strlen(qdomain);	\
 		av[0] = key;	\
 		av[1] = NULL;	\
-		snprintf(key, sizeof(key), "%s:%s", tag, domain); \
+		snprintf(key, sizeof(key), "%s:%s", tag, qdomain); \
 		p = (*map->s_map.map_class->map_lookup)(&map->s_map, key, av, \
 			&maprcode);	\
 		if (p != NULL)	\
 			break;	\
-		if (!tTd(8, 112) || (len > 0 && '.' == domain[len - 1])) \
+		if (!tTd(8, 112) || (len > 0 && '.' == qdomain[len - 1])) \
 			break;	\
-		snprintf(key, sizeof(key), "%s:%s.", tag, domain); \
+		snprintf(key, sizeof(key), "%s:%s.", tag, qdomain); \
 		p = (*map->s_map.map_class->map_lookup)(&map->s_map, key, av, \
 			&maprcode);	\
 	} while (0)
@@ -1138,7 +1171,7 @@ tstdns_search(domain, class, type, answer, anslen)
 			{
 				sm_dprintf("cname lookup key=%s, value=%s, ad=%d\n",
 					key, p, ad);
-				rlen = dnscrtrr(domain, query, type, p, T_CNAME,
+				rlen = dnscrtrr(qdomain, query, type, p, T_CNAME,
 						flags, &herr, &ad, answer,
 						anslen, anspos);
 				if (rlen < 0)
@@ -1156,9 +1189,9 @@ tstdns_search(domain, class, type, answer, anslen)
 
 		/* skip (leading) ad/ttl: look for last ' ' */
 		if ((last = strrchr(p, ' ')) != NULL && last[1] != '\0')
-			domain = last + 1;
+			qdomain = last + 1;
 		else
-			domain = p;
+			qdomain = p;
 		++cnt;
 	}
 	if (NULL == p)
@@ -1179,7 +1212,7 @@ tstdns_search(domain, class, type, answer, anslen)
 			if (p != NULL)
 			{
 				sm_dprintf("access map lookup failed key=%s:%s, but found key=%s\n",
-					tag, domain, key);
+					tag, qdomain, key);
 				herr = NO_DATA;
 				goto error;
 			}
@@ -1190,7 +1223,7 @@ tstdns_search(domain, class, type, answer, anslen)
 	}
 	if (found_cname && (flags & RR_ONLY_CNAME) != 0)
 		return rlen;
-	rlen = dnscrtrr(domain,  query, type, p, type, flags, &herr, &ad,
+	rlen = dnscrtrr(qdomain, query, type, p, type, flags, &herr, &ad,
 			answer, anslen, anspos);
 	if (rlen < 0)
 		goto error;
@@ -1229,12 +1262,12 @@ tstdns_querydomain(name, domain, class, type, answer, anslen)
 	unsigned char *answer;
 	int anslen;
 {
-	char query[MAXNAME];
+	char query[MAXNAME_I];
 	int len;
 
 	if (NULL == name)
 		goto error;
-	if (NULL == domain || '\0' == *domain)
+	if (SM_IS_EMPTY(domain))
 		return tstdns_search(name, class, type, answer, anslen);
 
 	len = snprintf(query, sizeof(query), "%s.%s", name, domain);
@@ -1458,10 +1491,9 @@ dns2he(dr, family)
 #    define IN_ADDRSZ INADDRSZ
 #   endif
 	static char he_addrs[SM_MAX_ADDRS * IN_ADDRSZ];
-	static char he_name[MAXNAME];
+	static char he_name[MAXNAME_I];
 	static bool he_init = false;
 	struct hostent *h;
-	struct in_addr ia;
 	int i;
 	size_t sz;
 #   if NETINET6 && DNSSEC_TEST
@@ -1573,6 +1605,8 @@ dns2he(dr, family)
 #   if DNSSEC_TEST
 	if (tTd(8, 16))
 	{
+		struct in_addr ia;
+
 		for (i = 0; h->h_addr_list[i] != NULL && i < SM_MAX_ADDRS; i++)
 		{
 			char *addr;
diff --git a/contrib/sendmail/src/sm_resolve.h b/contrib/sendmail/src/sm_resolve.h
index ffff41168944..3e4b90993cef 100644
--- a/contrib/sendmail/src/sm_resolve.h
+++ b/contrib/sendmail/src/sm_resolve.h
@@ -173,7 +173,6 @@ int tstdns_querydomain  __P((const char *, const char *, int, int, unsigned char
 #   endif
 SMR_EXTERN char *NameSearchList;
 #   undef SMR_EXTERN
-extern void	dns_setns __P((struct in_addr *, unsigned int));
 extern int	nsportip __P((char *));
 #  endif /* DNSSEC_TEST*/
 
diff --git a/contrib/sendmail/src/srvrsmtp.c b/contrib/sendmail/src/srvrsmtp.c
index b6263079a90e..bd416c3ebdaa 100644
--- a/contrib/sendmail/src/srvrsmtp.c
+++ b/contrib/sendmail/src/srvrsmtp.c
@@ -19,6 +19,10 @@
 
 SM_RCSID("@(#)$Id: srvrsmtp.c,v 8.1016 2013-11-22 20:51:56 ca Exp $")
 
+#include 
+#if _FFR_8BITENVADDR
+# include 
+#endif
 #include 
 #include 
 
@@ -32,7 +36,6 @@ static int saslmechs __P((sasl_conn_t *, char **));
 #endif
 #if STARTTLS
 # include 
-# include 
 
 static SSL_CTX	*srv_ctx = NULL;	/* TLS server context */
 static SSL	*srv_ssl = NULL;	/* per connection context */
@@ -67,9 +70,10 @@ static bool	NotFirstDelivery = false;
 #define SRV_REQ_AUTH	0x0400	/* require AUTH */
 #define SRV_REQ_SEC	0x0800	/* require security - equiv to AuthOptions=p */
 #define SRV_TMP_FAIL	0x1000	/* ruleset caused a temporary failure */
-#if _FFR_EAI
-# define SRV_OFFER_EAI	0x2000	/* offer SMTPUTF* */
+#if USE_EAI
+# define SRV_OFFER_EAI	0x2000	/* offer SMTPUTF8 */
 #endif
+#define SRV_NO_HTTP_CMD	0x4000	/* always reject HTTP commands */
 
 static unsigned int	srvfeatures __P((ENVELOPE *, char *, unsigned int));
 
@@ -81,32 +85,32 @@ static char	*skipword __P((char *volatile, char *));
 static void	setup_smtpd_io __P((void));
 
 #if SASL
-#  ifndef MAX_AUTH_USER_LEN
-#   define MAX_AUTH_USER_LEN 256
-#  endif
-#  ifndef MAX_AUTH_LOG_LEN
-#   define MAX_AUTH_LOG_LEN 64
-#  endif
+# ifndef MAX_AUTH_USER_LEN
+#  define MAX_AUTH_USER_LEN 256
+# endif
+# ifndef MAX_AUTH_LOG_LEN
+#  define MAX_AUTH_LOG_LEN 64
+# endif
 static void get_sasl_user __P((char *, unsigned int, const char *, char *out, size_t));
-#  define RESET_AUTH_FAIL_LOG_USER	\
+# define RESET_AUTH_FAIL_LOG_USER	\
 	do	\
 	{	\
 		(void) memset(auth_user, 0, sizeof(auth_user));	\
 		(void) memset(auth_user_tmp, 0, sizeof(auth_user_tmp));	\
 		auth_user_len = 0;	\
 	} while (0)
-#  define SET_AUTH_USER_TMP(s, len)	\
+# define SET_AUTH_USER_TMP(s, len)	\
 	do	\
 	{	\
 		auth_user_len = SM_MIN(len, MAX_AUTH_USER_LEN-1);	\
 		(void) memcpy(auth_user_tmp, s, auth_user_len);	\
 	} while (0)
-#  define SET_AUTH_USER	\
+# define SET_AUTH_USER	\
 	get_sasl_user(auth_user_tmp, auth_user_len, auth_type, auth_user, sizeof(auth_user))
-#  define SET_AUTH_USER_CONDITIONALLY	\
+# define SET_AUTH_USER_CONDITIONALLY	\
 		if ('\0' == auth_user[0])	\
 			SET_AUTH_USER;
-#  define LOG_AUTH_FAIL_USER ", user=", (int)MAX_AUTH_LOG_LEN, auth_user
+# define LOG_AUTH_FAIL_USER ", user=", (int)MAX_AUTH_LOG_LEN, auth_user
 # if SASL >= 20000
 static int reset_saslconn __P((sasl_conn_t **_conn, char *_hostname,
 				char *_remoteip, char *_localip,
@@ -159,7 +163,7 @@ extern ENVELOPE	BlankEnvelope;
 #define SKIP_SPACE(s)	while (SM_ISSPACE(*s))	\
 				(s)++
 
-#if _FFR_EAI
+#if USE_EAI
 /*
 **  ADDR_IS_ASCII -- check whether an address is 100% printable ASCII
 **
@@ -174,13 +178,32 @@ extern ENVELOPE	BlankEnvelope;
 
 bool
 addr_is_ascii(a)
-	const char * a;
+	const char *a;
 {
 	while (a != NULL && *a != '\0' && *a >= ' ' && (unsigned char)*a < 127)
 		a++;
 	return (a != NULL && *a == '\0');
 }
-#endif
+
+# define CHECK_UTF8_ADDR(a, q)	\
+	do	\
+	{	\
+		q = NULL;	\
+		if (addr_is_ascii(a))	\
+			break;	\
+		if (!SMTPUTF8)	\
+			break;	\
+		if (!e->e_smtputf8)	\
+			q = "553 5.6.7 Address requires SMTPUTF8";	\
+		else	\
+		{	\
+			char str[MAXNAME];	\
+			dequote_internal_chars(a, str, sizeof(str));	\
+			if (!utf8_valid(str, strlen(str)) && SMTPUTF8 <= 1) \
+				q = "553 5.6.7 Address not valid UTF8";	\
+		}	\
+	} while (0)
+#endif /* USE_EAI */
 
 /*
 **  PARSE_ESMTP_ARGS -- parse ESMTP arguments (for MAIL, RCPT)
@@ -270,7 +293,7 @@ parse_esmtp_args(e, addr_st, p, delimptr, which, args, esmtp_args)
 **  ADDRCPT -- Add a rcpt to sendq list
 **
 **	Parameters:
-**		rcpt -- rcpt
+**		rcpt -- rcpt [i]
 **		sendq -- a pointer to the head of a queue to put
 **			these people into.
 **		e -- the envelope in which to add these recipients.
@@ -300,6 +323,7 @@ addrcpt(rcpt, sendq, e)
 	SM_TRY
 	{
 		macdefine(&e->e_macro, A_PERM, macid("{addr_type}"), "e b");
+/* XXX rcpt must be [i] */
 		a = parseaddr(rcpt, NULLADDR, RF_COPYALL, ' ', NULL, e, true);
 		if (a == NULL)
 			return 0;
@@ -390,7 +414,7 @@ rcptmods(rcpt, e)
 	SM_REQUIRE(e != NULL);
 
 	fl = macvalue(macid("{rcpt_flags}"), e);
-	if (fl == NULL || *fl == '\0')
+	if (SM_IS_EMPTY(fl))
 		return;
 	if (tTd(25, 1))
 		sm_dprintf("rcptmods: rcpt=%s, flags=%s\n", rcpt->q_paddr, fl);
@@ -555,7 +579,7 @@ static char	*CurSmtpClient;		/* who's at the other end of channel */
 # define MAXSHIFT 8
 #endif
 #if MAXSHIFT > 31
- ERROR _MAXSHIFT > 31 is invalid
+# ERROR "MAXSHIFT > 31 is invalid"
 #endif
 
 
@@ -782,7 +806,7 @@ do								\
 #else
 # define auth_active	false
 #endif
-#if _FFR_EAI
+#if USE_EAI
 #define GET_PROTOCOL()					\
 	(e->e_smtputf8					\
 	    ? (auth_active				\
@@ -791,15 +815,25 @@ do								\
 	    : (auth_active				\
 		? (tls_active ? "ESMTPSA" : "ESMTPA")	\
 		: (tls_active ? "ESMTPS"  : "ESMTP")))
-#else /* _FFR_EAI */
+#else /* USE_EAI */
 #define GET_PROTOCOL()					\
 	(auth_active					\
 	    ? (tls_active ? "ESMTPSA" : "ESMTPA")	\
 	    : (tls_active ? "ESMTPS"  : "ESMTP"))
-#endif /* _FFR_EAI */
+#endif /* USE_EAI */
 
 static bool SevenBitInput_Saved;	/* saved version of SevenBitInput */
 
+#if _FFR_NOREFLECT
+# define SHOWCMDINREPLY(inp) (bitset(PRIV_NOREFLECTION, PrivacyFlags) ? \
+		"(suppressed)" : inp)
+# define SHOWSHRTCMDINREPLY(inp) (bitset(PRIV_NOREFLECTION, PrivacyFlags) ? \
+		"(suppressed)" : shortenstring(inp, MAXSHORTSTR))
+#else
+# define SHOWCMDINREPLY(inp) inp
+# define SHOWSHRTCMDINREPLY(inp) shortenstring(inp, MAXSHORTSTR)
+#endif
+
 void
 smtp(nullserver, d_flags, e)
 	char *volatile nullserver;
@@ -840,7 +874,7 @@ smtp(nullserver, d_flags, e)
 	char *args[MAXSMTPARGS];
 	char inp[MAXINPLINE];
 #if MAXINPLINE < MAXLINE
- ERROR _MAXINPLINE must NOT be less than _MAXLINE: MAXINPLINE < MAXLINE
+# ERROR "MAXINPLINE must NOT be less than MAXLINE"
 #endif
 	char cmdbuf[MAXLINE];
 #if SASL
@@ -892,7 +926,7 @@ smtp(nullserver, d_flags, e)
 # if _FFR_NO_PIPE
 	int np_log = 0;
 # endif
-#endif /* PIPELINING */
+#endif
 	volatile time_t log_delay = (time_t) 0;
 #if MILTER
 	volatile bool milter_cmd_done, milter_cmd_safe;
@@ -947,7 +981,7 @@ smtp(nullserver, d_flags, e)
 #if PIPELINING
 	/* auto-flush output when reading input */
 	(void) sm_io_autoflush(InChannel, OutChannel);
-#endif /* PIPELINING */
+#endif
 
 	sm_setproctitle(true, e, "server %s startup", CurSmtpClient);
 
@@ -974,9 +1008,9 @@ smtp(nullserver, d_flags, e)
 		| (bitset(TLS_I_NO_VRFY, TLS_Srv_Opts) ? SRV_NONE
 						       : SRV_VRFY_CLT)
 #endif
-#if _FFR_EAI
-		| SRV_OFFER_EAI
-#endif /* _FFR_EAI */
+#if USE_EAI
+		| (SMTPUTF8 ? SRV_OFFER_EAI : 0)
+#endif
 		;
 	if (nullserver == NULL)
 	{
@@ -1231,7 +1265,7 @@ smtp(nullserver, d_flags, e)
 		response = milter_connect(q, RealHostAddr, e, &state);
 		switch (state)
 		{
-#if _FFR_MILTER_CONNECT_REPLYCODE
+# if _FFR_MILTER_CONNECT_REPLYCODE
 		  case SMFIR_REPLYCODE:
 			if (*response == '5')
 			{
@@ -1295,9 +1329,9 @@ smtp(nullserver, d_flags, e)
 				break;
 			}
 
-#else /* _FFR_MILTER_CONNECT_REPLYCODE */
+# else /* _FFR_MILTER_CONNECT_REPLYCODE */
 		  case SMFIR_REPLYCODE:	/* REPLYCODE shouldn't happen */
-#endif /* _FFR_MILTER_CONNECT_REPLYCODE */
+# endif /* _FFR_MILTER_CONNECT_REPLYCODE */
 		  case SMFIR_REJECT:
 			if (MilterLogLevel > 3)
 				sm_syslog(LOG_INFO, e->e_id,
@@ -1567,7 +1601,7 @@ smtp(nullserver, d_flags, e)
 		}
 #endif /* SASL */
 
-		if (first)
+		if (first || bitset(SRV_NO_HTTP_CMD, features))
 		{
 			size_t cmdlen;
 			int idx;
@@ -1584,8 +1618,11 @@ smtp(nullserver, d_flags, e)
 				    SM_ISSPACE(inp[cmdlen]))
 				{
 					/* Open proxy, drop it */
-					message("421 4.7.0 %s Rejecting open proxy %s",
-						MyHostName, CurSmtpClient);
+					message("421 4.7.0 %s %s %s",
+						MyHostName,
+						first ? "Rejecting open proxy"
+							: "HTTP command",
+						CurSmtpClient);
 					sm_syslog(LOG_INFO, e->e_id,
 						  "%s: probable open proxy: command=%.40s",
 						  CurSmtpClient, inp);
@@ -1710,10 +1747,10 @@ smtp(nullserver, d_flags, e)
 						  xtextify(user, "<>\")"));
 				}
 
-# if 0
+#  if 0
 				/* get realm? */
 				sasl_getprop(conn, SASL_REALM, (void **) &data);
-# endif
+#  endif
 
 				/* get security strength (features) */
 				result = sasl_getprop(conn, SASL_SSF,
@@ -1871,7 +1908,7 @@ smtp(nullserver, d_flags, e)
 		/* decode command */
 		for (c = CmdTab; c->cmd_name != NULL; c++)
 		{
-			if (sm_strcasecmp(c->cmd_name, cmdbuf) == 0)
+			if (SM_STRCASEEQ(c->cmd_name, cmdbuf))
 				break;
 		}
 
@@ -2212,10 +2249,11 @@ smtp(nullserver, d_flags, e)
 				tlslogerr(LOG_WARNING, 8, "server");
 				goto tls_done;
 			}
-			if (get_tls_se_options(e, srv_ssl, &tlsi_ctx, true)
-			    != 0)
+			if (get_tls_se_features(e, srv_ssl, &tlsi_ctx, true)
+			    != EX_OK)
 			{
-				message("454 4.3.3 TLS not available: error setting options");
+				/* do not offer too much info to client */
+				message("454 4.3.3 TLS curently not available");
 				SMTLSFAILED;
 			}
 			r = SSL_set_ex_data(srv_ssl, TLSsslidx, &tlsi_ctx);
@@ -2341,6 +2379,7 @@ smtp(nullserver, d_flags, e)
 			SuprErrs = saveSuprErrs;
 
 			tls_ok_srv = false;	/* don't offer STARTTLS again */
+			first = true;
 			n_helo = 0;
 # if SASL
 			if (sasl_ok)
@@ -2450,7 +2489,7 @@ smtp(nullserver, d_flags, e)
 			}
 
 			/* check for long domain name (hides Received: info) */
-			if (strlen(p) > MAXNAME)
+			if (strlen(p) > MAXNAME) /* EAI:ok:EHLO name must be ASCII */
 			{
 				usrerr("501 Invalid domain name");
 				if (LogLevel > 9)
@@ -2635,10 +2674,10 @@ smtp(nullserver, d_flags, e)
 			if (SendMIMEErrors && bitset(SRV_OFFER_DSN, features))
 				message("250-DSN");
 #endif
-#if _FFR_EAI
+#if USE_EAI
 			if (bitset(SRV_OFFER_EAI, features))
 				message("250-SMTPUTF8");
-#endif /* _FFR_EAI */
+#endif
 			if (bitset(SRV_OFFER_ETRN, features))
 				message("250-ETRN");
 #if SASL
@@ -2751,15 +2790,35 @@ smtp(nullserver, d_flags, e)
 		    SM_TRY
 		    {
 			extern char *FullName;
+#if _FFR_8BITENVADDR
+			char *origp;
+			char iaddr[MAXLINE * 2];
+			int len;
+#else
+# define origp	p
+#endif
 
 			QuickAbort = true;
 			SM_FREE(FullName);
+#if _FFR_8BITENVADDR
+			len = sizeof(iaddr);
+			origp = p;
+
+			/* HACK!!!! p is more than the address! */
+			p = quote_internal_chars(p, iaddr, &len, NULL);
+#endif
 
 			/* must parse sender first */
 			delimptr = NULL;
 			setsender(p, e, &delimptr, ' ', false);
 			if (delimptr != NULL && *delimptr != '\0')
+			{
 				*delimptr++ = '\0';
+#if _FFR_8BITENVADDR
+				len = sizeof(iaddr) - (delimptr - iaddr);
+				(void) dequote_internal_chars(delimptr, delimptr, len);
+#endif
+			}
 			if (Errors > 0)
 				sm_exc_raisenew_x(&EtypeQuickAbort, 1);
 
@@ -2808,12 +2867,12 @@ smtp(nullserver, d_flags, e)
 			/* now parse ESMTP arguments */
 			e->e_msgsize = 0;
 			addr = p;
-			parse_esmtp_args(e, NULL, p, delimptr, "MAIL", args,
+			parse_esmtp_args(e, NULL, origp, delimptr, "MAIL", args,
 					mail_esmtp_args);
 			if (Errors > 0)
 				sm_exc_raisenew_x(&EtypeQuickAbort, 1);
 
-#if _FFR_EAI
+#if USE_EAI
 			if (e->e_smtputf8)
 			{
 				protocol = GET_PROTOCOL();
@@ -2821,9 +2880,11 @@ smtp(nullserver, d_flags, e)
 			}
 
 			/* UTF8 addresses are only legal with SMTPUTF8 */
-			if (!e->e_smtputf8 && !addr_is_ascii(e->e_from.q_paddr))
+/* XXX different error if SMTPUTF8 is not enabled? */
+			CHECK_UTF8_ADDR(e->e_from.q_paddr, q);
+			if (q != NULL)
 			{
-				usrerr("553 5.6.7 That address requires SMTPUTF8");
+				usrerr(q);
 				sm_exc_raisenew_x(&EtypeQuickAbort, 1);
 			}
 #endif
@@ -3011,6 +3072,12 @@ smtp(nullserver, d_flags, e)
 			SmtpPhase = "server RCPT";
 		    SM_TRY
 		    {
+#if _FFR_8BITENVADDR
+			char iaddr[MAXLINE * 2];
+			int len;
+			char *origp;
+#endif
+
 			QuickAbort = true;
 			LogUsrErrs = true;
 
@@ -3054,6 +3121,13 @@ smtp(nullserver, d_flags, e)
 				goto rcpt_done;
 			macdefine(&e->e_macro, A_PERM,
 				macid("{addr_type}"), "e r");
+#if _FFR_8BITENVADDR
+			len = sizeof(iaddr);
+			origp = p;
+
+			/* HACK!!!! p is more than the address! */
+			p = quote_internal_chars(p, iaddr, &len, NULL);
+#endif
 			a = parseaddr(p, NULLADDR, RF_COPYALL, ' ', &delimptr,
 				      e, true);
 			macdefine(&e->e_macro, A_PERM,
@@ -3065,16 +3139,23 @@ smtp(nullserver, d_flags, e)
 				usrerr("501 5.0.0 Missing recipient");
 				goto rcpt_done;
 			}
-#if _FFR_EAI
-			if (!e->e_smtputf8 && !addr_is_ascii(a->q_paddr))
+#if USE_EAI
+			CHECK_UTF8_ADDR(a->q_paddr, q);
+			if (q != NULL)
 			{
-				usrerr("553 5.6.7 Address requires SMTPUTF8");
+				usrerr(q);
 				goto rcpt_done;
 			}
 #endif
 
 			if (delimptr != NULL && *delimptr != '\0')
+			{
 				*delimptr++ = '\0';
+#if _FFR_8BITENVADDR
+				len = sizeof(iaddr) - (delimptr - iaddr);
+				(void) dequote_internal_chars(delimptr, delimptr, len);
+#endif
+			}
 
 			/* put resulting triple from parseaddr() into macros */
 			if (a->q_mailer != NULL)
@@ -3100,8 +3181,8 @@ smtp(nullserver, d_flags, e)
 				goto rcpt_done;
 
 			/* now parse ESMTP arguments */
-			addr = p;
-			parse_esmtp_args(e, a, p, delimptr, "RCPT", args,
+			addr = sm_rpool_strdup_x(e->e_rpool, p);
+			parse_esmtp_args(e, a, origp, delimptr, "RCPT", args,
 					rcpt_esmtp_args);
 			if (Errors > 0)
 				goto rcpt_done;
@@ -3533,10 +3614,31 @@ smtp(nullserver, d_flags, e)
 			help(p, e);
 			break;
 
+#define CHECK_OTHER(type) do	\
+	{							\
+		bool saveQuickAbort = QuickAbort;		\
+		extern char MsgBuf[];				\
+		int rsc;					\
+		QuickAbort = false;				\
+		if ((rsc = rscheck("check_other", inp, type, e,	\
+			    RSF_UNSTRUCTURED, 3, NULL, NOQID, NULL, NULL)) \
+			!= EX_OK ||				\
+		    Errors > 0)					\
+		{						\
+			if (strncmp(MsgBuf, "421 ", 4) == 0)	\
+			{					\
+				e->e_sendqueue = NULL;		\
+				goto doquit;			\
+			}					\
+		}						\
+		QuickAbort = saveQuickAbort;			\
+	} while (0)
+
 		  case CMDNOOP:		/* noop -- do nothing */
 			DELAY_CONN("NOOP");
 			STOP_IF_ATTACK(checksmtpattack(&n_noop, MaxNOOPCommands,
 							true, "NOOP", e));
+			CHECK_OTHER("2");
 			message("250 2.0.0 OK");
 			break;
 
@@ -3609,6 +3711,27 @@ smtp(nullserver, d_flags, e)
 			if (tTd(93, 100))
 			{
 				/* return to handle next connection */
+#if SM_HEAP_CHECK
+# define SM_HC_TRIGGER "heapdump"
+				if (sm_debug_active(&SmHeapCheck, 2)
+				    && access(SM_HC_TRIGGER, F_OK) == 0
+				   )
+				{
+					SM_FILE_T *out;
+
+					remove(SM_HC_TRIGGER);
+					out = sm_io_open(SmFtStdio,
+						SM_TIME_DEFAULT, SM_HC_TRIGGER ".heap",
+						SM_IO_APPEND, NULL);
+					if (out != NULL)
+					{
+						(void) sm_io_fprintf(out, SM_TIME_DEFAULT, "----------------------\n");
+						sm_heap_report(out,
+							sm_debug_level(&SmHeapCheck) - 1);
+						(void) sm_io_close(out, SM_TIME_DEFAULT);
+					}
+				}
+#endif /* SM_HEAP_CHECK */
 				return;
 			}
 			finis(true, true, ExitStat);
@@ -3628,6 +3751,7 @@ smtp(nullserver, d_flags, e)
 			}
 			STOP_IF_ATTACK(checksmtpattack(&n_noop, MaxNOOPCommands,
 							true, "VERB", e));
+			CHECK_OTHER("2");
 			Verbose = 1;
 			set_delivery_mode(SM_DELIVER, e);
 			message("250 2.0.0 Verbose mode");
@@ -3635,12 +3759,14 @@ smtp(nullserver, d_flags, e)
 
 #if SMTPDEBUG
 		  case CMDDBGQSHOW:	/* show queues */
+			CHECK_OTHER("2");
 			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
 					     "Send Queue=");
 			printaddr(smioout, e->e_sendqueue, true);
 			break;
 
 		  case CMDDBGDEBUG:	/* set debug mode */
+			CHECK_OTHER("2");
 			tTsetup(tTdvect, sizeof(tTdvect), "0-99.1");
 			tTflag(p);
 			message("200 2.0.0 Debug set");
@@ -3696,18 +3822,21 @@ smtp(nullserver, d_flags, e)
 			}
 #endif /* MILTER && SMFI_VERSION > 2 */
 
+			CHECK_OTHER("5");
 			usrerr("500 5.5.1 Command unrecognized: \"%s\"",
-			       shortenstring(inp, MAXSHORTSTR));
+			       SHOWSHRTCMDINREPLY(inp));
 			break;
 
 		  case CMDUNIMPL:
 			DELAY_CONN("Unimpl");
+			CHECK_OTHER("5");
 			usrerr("502 5.5.1 Command not implemented: \"%s\"",
-			       shortenstring(inp, MAXSHORTSTR));
+			       SHOWSHRTCMDINREPLY(inp));
 			break;
 
 		  default:
 			DELAY_CONN("default");
+			CHECK_OTHER("5");
 			errno = 0;
 			syserr("500 5.5.0 smtp: unknown code %d", c->cmd_code);
 			break;
@@ -3794,10 +3923,10 @@ smtp_data(smtp, e)
 					  response);
 				LogUsrErrs = false;
 			}
-#if _FFR_MILTER_ENHSC
+# if _FFR_MILTER_ENHSC
 			if (ISSMTPCODE(response))
 				(void) extenhsc(response + 4, ' ', e->e_enhsc);
-#endif
+# endif
 
 			/* Can't use ("%s", ...) due to usrerr() requirements */
 			usrerr(response);
@@ -3816,10 +3945,10 @@ smtp_data(smtp, e)
 					  "Milter: cmd=data, reject=550 5.7.1 Command rejected");
 				LogUsrErrs = false;
 			}
-#if _FFR_MILTER_ENHSC
+# if _FFR_MILTER_ENHSC
 			(void) sm_strlcpy(e->e_enhsc, "5.7.1",
 					 sizeof(e->e_enhsc));
-#endif
+# endif
 			usrerr("550 5.7.1 Command rejected");
 			return true;
 
@@ -3838,9 +3967,9 @@ smtp_data(smtp, e)
 					  MSG_TEMPFAIL);
 				LogUsrErrs = false;
 			}
-#if _FFR_MILTER_ENHSC
+# if _FFR_MILTER_ENHSC
 			(void) extenhsc(MSG_TEMPFAIL + 4, ' ', e->e_enhsc);
-#endif
+# endif
 			/* Can't use ("%s", ...) due to usrerr() requirements */
 			usrerr(MSG_TEMPFAIL);
 			return true;
@@ -3917,10 +4046,10 @@ smtp_data(smtp, e)
 					  "Milter: data, reject=%s",
 					  response);
 			milteraccept = false;
-#if _FFR_MILTER_ENHSC
+# if _FFR_MILTER_ENHSC
 			if (ISSMTPCODE(response))
 				(void) extenhsc(response + 4, ' ', e->e_enhsc);
-#endif
+# endif
 			/* Can't use ("%s", ...) due to usrerr() requirements */
 			usrerr(response);
 			if (strncmp(response, "421 ", 4) == 0
@@ -3950,9 +4079,9 @@ smtp_data(smtp, e)
 					  "Milter: data, reject=%s",
 					  MSG_TEMPFAIL);
 			milteraccept = false;
-#if _FFR_MILTER_ENHSC
+# if _FFR_MILTER_ENHSC
 			(void) extenhsc(MSG_TEMPFAIL + 4, ' ', e->e_enhsc);
-#endif
+# endif
 			/* Can't use ("%s", ...) due to usrerr() requirements */
 			usrerr(MSG_TEMPFAIL);
 			break;
@@ -4173,6 +4302,10 @@ smtp_data(smtp, e)
 	}
 #endif /* _FFR_PROXY */
 
+#if _FFR_DMTRIGGER
+	if (SM_TRIGGER == e->e_sendmode)
+		doublequeue = true;
+#endif
 	for (ee = e; ee != NULL; ee = ee->e_sibling)
 	{
 		/* make sure we actually do delivery */
@@ -4183,8 +4316,15 @@ smtp_data(smtp, e)
 
 		if (doublequeue)
 		{
+			unsigned int qup_flags;
+
+			qup_flags = QUP_FL_MSYNC;
+#if _FFR_DMTRIGGER
+			if (IS_SM_TRIGGER(ee->e_sendmode))
+				qup_flags |= QUP_FL_UNLOCK;
+#endif
 			/* make sure it is in the queue */
-			queueup(ee, false, true);
+			queueup(ee, qup_flags);
 		}
 		else
 		{
@@ -4305,6 +4445,7 @@ smtp_data(smtp, e)
 		}
 		else
 #endif /* _FFR_MSG_ACCEPT */
+		/* "else" in #if code above */
 		message("250 2.0.0 %s Message accepted for delivery", id);
 #if _FFR_PROXY
 	}
@@ -4319,6 +4460,20 @@ smtp_data(smtp, e)
 		sm_getla();
 		for (ee = e; ee != NULL; ee = ee->e_sibling)
 		{
+#if _FFR_DMTRIGGER
+			if (SM_TRIGGER == ee->e_sendmode)
+			{
+				sm_syslog(LOG_DEBUG, ee->e_id,
+					"smtp: doublequeue, mode=%c", ee->e_sendmode);
+				ee->e_sendmode = SM_DELIVER;
+
+				/* close all the queue files */
+				/* almost the same as below */
+				closexscript(ee);
+				SM_CLOSE_FP(ee->e_dfp);
+				continue;
+			}
+#endif /* _FFR_DMTRIGGER */
 			if (WILL_BE_QUEUED(ee->e_sendmode))
 				continue;
 			if (shouldqueue(ee->e_msgpriority, ee->e_ctime))
@@ -4336,11 +4491,7 @@ smtp_data(smtp, e)
 
 			/* close all the queue files */
 			closexscript(ee);
-			if (ee->e_dfp != NULL)
-			{
-				(void) sm_io_close(ee->e_dfp, SM_TIME_DEFAULT);
-				ee->e_dfp = NULL;
-			}
+			SM_CLOSE_FP(ee->e_dfp);
 			unlockqueue(ee);
 		}
 		if (anything_to_send)
@@ -4353,6 +4504,9 @@ smtp_data(smtp, e)
 
 			(void) sm_io_flush(OutChannel, SM_TIME_DEFAULT);
 #endif /* PIPELINING */
+#if _FFR_DMTRIGGER
+			sm_syslog(LOG_DEBUG, e->e_id, "smtp: doublequeue=send");
+#endif
 			(void) doworklist(e, true, true);
 		}
 	}
@@ -4453,6 +4607,10 @@ logundelrcpts(e, msg, level, all)
 #if _FFR_MILTER_ENHSC
 			    (a->q_status == NULL && e->e_enhsc[0] != '\0')
 			    ? e->e_enhsc :
+#endif
+/* not yet documented or tested */
+#if _FFR_USE_E_STATUS
+			    (NULL == a->q_status) ? e->e_status :
 #endif
 			    a->q_status,
 			    msg, NULL, (time_t) 0, e, a, EX_OK /* ??? */);
@@ -4635,7 +4793,7 @@ skipword(p, w)
 	{
 	  syntax:
 		usrerr("501 5.5.2 Syntax error in parameters scanning \"%s\"",
-			shortenstring(firstp, MAXSHORTSTR));
+			SHOWSHRTCMDINREPLY(firstp));
 		return NULL;
 	}
 	*p++ = '\0';
@@ -4714,7 +4872,7 @@ mail_esmtp_args(a, kp, vp, e)
 	char *vp;
 	ENVELOPE *e;
 {
-	if (sm_strcasecmp(kp, "size") == 0)
+	if (SM_STRCASEEQ(kp, "size"))
 	{
 		if (vp == NULL)
 		{
@@ -4735,29 +4893,30 @@ mail_esmtp_args(a, kp, vp, e)
 			/* NOTREACHED */
 		}
 	}
-	else if (sm_strcasecmp(kp, "body") == 0)
+	else if (SM_STRCASEEQ(kp, "body"))
 	{
 		if (vp == NULL)
 		{
 			usrerr("501 5.5.2 BODY requires a value");
 			/* NOTREACHED */
 		}
-		else if (sm_strcasecmp(vp, "8bitmime") == 0)
+		else if (SM_STRCASEEQ(vp, "8bitmime"))
 		{
 			SevenBitInput = false;
 		}
-		else if (sm_strcasecmp(vp, "7bit") == 0)
+		else if (SM_STRCASEEQ(vp, "7bit"))
 		{
 			SevenBitInput = true;
 		}
 		else
 		{
-			usrerr("501 5.5.4 Unknown BODY type %s", vp);
+			usrerr("501 5.5.4 Unknown BODY type %s",
+				SHOWCMDINREPLY(vp));
 			/* NOTREACHED */
 		}
 		e->e_bodytype = sm_rpool_strdup_x(e->e_rpool, vp);
 	}
-	else if (sm_strcasecmp(kp, "envid") == 0)
+	else if (SM_STRCASEEQ(kp, "envid"))
 	{
 		if (!bitset(SRV_OFFER_DSN, e->e_features))
 		{
@@ -4783,7 +4942,7 @@ mail_esmtp_args(a, kp, vp, e)
 		macdefine(&e->e_macro, A_PERM,
 			macid("{dsn_envid}"), e->e_envid);
 	}
-	else if (sm_strcasecmp(kp, "ret") == 0)
+	else if (SM_STRCASEEQ(kp, "ret"))
 	{
 		if (!bitset(SRV_OFFER_DSN, e->e_features))
 		{
@@ -4801,17 +4960,18 @@ mail_esmtp_args(a, kp, vp, e)
 			/* NOTREACHED */
 		}
 		e->e_flags |= EF_RET_PARAM;
-		if (sm_strcasecmp(vp, "hdrs") == 0)
+		if (SM_STRCASEEQ(vp, "hdrs"))
 			e->e_flags |= EF_NO_BODY_RETN;
 		else if (sm_strcasecmp(vp, "full") != 0)
 		{
-			usrerr("501 5.5.2 Bad argument \"%s\" to RET", vp);
+			usrerr("501 5.5.2 Bad argument \"%s\" to RET",
+				SHOWCMDINREPLY(vp));
 			/* NOTREACHED */
 		}
 		macdefine(&e->e_macro, A_TEMP, macid("{dsn_ret}"), vp);
 	}
 #if SASL
-	else if (sm_strcasecmp(kp, "auth") == 0)
+	else if (SM_STRCASEEQ(kp, "auth"))
 	{
 		int len;
 		char *q;
@@ -4897,7 +5057,7 @@ mail_esmtp_args(a, kp, vp, e)
 	**  We maybe could add this to the list of server_features.
 	*/
 
-	else if (sm_strcasecmp(kp, "by") == 0 && DeliverByMin >= 0)
+	else if (SM_STRCASEEQ(kp, "by") && DeliverByMin >= 0)
 	{
 		char *s;
 
@@ -4913,7 +5073,8 @@ mail_esmtp_args(a, kp, vp, e)
 		    e->e_deliver_by > 999999999l ||
 		    e->e_deliver_by < -999999999l)
 		{
-			usrerr("501 5.5.2 BY=%s out of range", vp);
+			usrerr("501 5.5.2 BY=%s out of range",
+				SHOWCMDINREPLY(vp));
 			/* NOTREACHED */
 		}
 		if (s == NULL || *s != ';')
@@ -4964,12 +5125,12 @@ mail_esmtp_args(a, kp, vp, e)
 
 		/* XXX: check whether more characters follow? */
 	}
-#if _FFR_EAI
-	else if (sm_strcasecmp(kp, "smtputf8") == 0)
+#if USE_EAI
+	else if (SM_STRCASEEQ(kp, "smtputf8"))
 	{
 		if (!bitset(SRV_OFFER_EAI, e->e_features))
 		{
-			usrerr("504 5.7.0 Sorry, SMTPUTF8 not supported/enabled");
+			usrerr("504 5.7.0 Sorry, SMTPUTF8 not supported");
 			/* NOTREACHED */
 		}
 		e->e_smtputf8 = true;
@@ -4977,7 +5138,8 @@ mail_esmtp_args(a, kp, vp, e)
 #endif
 	else
 	{
-		usrerr("555 5.5.4 %s parameter unrecognized", kp);
+		usrerr("555 5.5.4 %s parameter unrecognized",
+			SHOWCMDINREPLY(kp));
 		/* NOTREACHED */
 	}
 }
@@ -5002,7 +5164,7 @@ rcpt_esmtp_args(a, kp, vp, e)
 	char *vp;
 	ENVELOPE *e;
 {
-	if (sm_strcasecmp(kp, "notify") == 0)
+	if (SM_STRCASEEQ(kp, "notify"))
 	{
 		char *p;
 
@@ -5020,7 +5182,7 @@ rcpt_esmtp_args(a, kp, vp, e)
 		a->q_flags |= QHASNOTIFY;
 		macdefine(&e->e_macro, A_TEMP, macid("{dsn_notify}"), vp);
 
-		if (sm_strcasecmp(vp, "never") == 0)
+		if (SM_STRCASEEQ(vp, "never"))
 			return;
 		for (p = vp; p != NULL; vp = p)
 		{
@@ -5029,23 +5191,23 @@ rcpt_esmtp_args(a, kp, vp, e)
 			s = p = strchr(p, ',');
 			if (p != NULL)
 				*p++ = '\0';
-			if (sm_strcasecmp(vp, "success") == 0)
+			if (SM_STRCASEEQ(vp, "success"))
 				a->q_flags |= QPINGONSUCCESS;
-			else if (sm_strcasecmp(vp, "failure") == 0)
+			else if (SM_STRCASEEQ(vp, "failure"))
 				a->q_flags |= QPINGONFAILURE;
-			else if (sm_strcasecmp(vp, "delay") == 0)
+			else if (SM_STRCASEEQ(vp, "delay"))
 				a->q_flags |= QPINGONDELAY;
 			else
 			{
 				usrerr("501 5.5.4 Bad argument \"%s\"  to NOTIFY",
-					vp);
+					SHOWCMDINREPLY(vp));
 				/* NOTREACHED */
 			}
 			if (s != NULL)
 				*s = ',';
 		}
 	}
-	else if (sm_strcasecmp(kp, "orcpt") == 0)
+	else if (SM_STRCASEEQ(kp, "orcpt"))
 	{
 		char *p;
 
@@ -5071,6 +5233,26 @@ rcpt_esmtp_args(a, kp, vp, e)
 			/* NOTREACHED */
 		}
 		*p = '\0';
+#if USE_EAI
+		if (SM_STRCASEEQ(vp, "utf-8"))
+		{
+			/* XXX check syntax of p+1 ! */
+			if (!xtextok(p + 1) &&
+			    uxtext_unquote(p + 1, NULL, MAXNAME_I) <= 0)
+			{
+				*p = ';';
+				usrerr("501 5.5.4 Syntax error in UTF-8 ORCPT parameter value");
+				/* NOTREACHED */
+			}
+# if 0
+complicated... see grammar!
+RFC 6533 Internationalized Delivery Status and Disposition Notifications
+utf-8-enc-addr = utf-8-addr-xtext / utf-8-addr-unitext / utf-8-address
+# endif
+		}
+		else
+#endif /* USE_EAI */
+		/* "else" in #if code above */
 		if (!isatom(vp) || !xtextok(p + 1))
 		{
 			*p = ';';
@@ -5082,7 +5264,8 @@ rcpt_esmtp_args(a, kp, vp, e)
 	}
 	else
 	{
-		usrerr("555 5.5.4 %s parameter unrecognized", kp);
+		usrerr("555 5.5.4 %s parameter unrecognized",
+			SHOWCMDINREPLY(kp));
 		/* NOTREACHED */
 	}
 }
@@ -5121,7 +5304,7 @@ printvrfyaddr(a, last, vrfy)
 	{
 		if ((a->q_mailer == NULL ||
 		     a->q_mailer->m_addrtype == NULL ||
-		     sm_strcasecmp(a->q_mailer->m_addrtype, "rfc822") == 0) &&
+		     SM_STRCASEEQ(a->q_mailer->m_addrtype, "rfc822")) &&
 		    strchr(a->q_user, '@') == NULL)
 			(void) sm_strlcpy(&fmtbuf[OFFF], "<%s@%s>",
 				       sizeof(fmtbuf) - OFFF);
@@ -5134,7 +5317,7 @@ printvrfyaddr(a, last, vrfy)
 	{
 		if ((a->q_mailer == NULL ||
 		     a->q_mailer->m_addrtype == NULL ||
-		     sm_strcasecmp(a->q_mailer->m_addrtype, "rfc822") == 0) &&
+		     SM_STRCASEEQ(a->q_mailer->m_addrtype, "rfc822")) &&
 		    strchr(a->q_user, '@') == NULL)
 			(void) sm_strlcpy(&fmtbuf[OFFF], "%s <%s@%s>",
 				       sizeof(fmtbuf) - OFFF);
@@ -5329,7 +5512,8 @@ static struct
 	{ 'C',	SRV_REQ_SEC	},
 	{ 'D',	SRV_OFFER_DSN	},
 	{ 'E',	SRV_OFFER_ETRN	},
-#if _FFR_EAI
+	{ 'H',	SRV_NO_HTTP_CMD	},
+#if USE_EAI
 	{ 'I',	SRV_OFFER_EAI	},
 #endif
 	{ 'L',	SRV_REQ_AUTH	},
@@ -5429,6 +5613,7 @@ help(topic, e)
 {
 	register SM_FILE_T *hf;
 	register char *p;
+	char *lstr;
 	int len;
 	bool noinfo;
 	bool first = true;
@@ -5453,14 +5638,20 @@ help(topic, e)
 		return;
 	}
 
-	if (topic == NULL || *topic == '\0')
+	lstr = NULL;
+	if (SM_IS_EMPTY(topic))
 	{
 		topic = "smtp";
 		noinfo = false;
 	}
 	else
 	{
-		makelower(topic);
+
+		lstr = makelower_a(&topic, NULL);
+		if (lstr != topic)
+			topic = lstr;
+		else
+			lstr = NULL;
 		noinfo = true;
 	}
 
@@ -5530,6 +5721,7 @@ help(topic, e)
 	}
 
 	(void) sm_io_close(hf, SM_TIME_DEFAULT);
+	SM_FREE(lstr);
 }
 
 #if SASL
@@ -5545,6 +5737,7 @@ help(topic, e)
 **		SASL result
 */
 
+#ifdef __STDC__
 static int
 reset_saslconn(sasl_conn_t **conn, char *hostname,
 # if SASL >= 20000
@@ -5554,6 +5747,9 @@ reset_saslconn(sasl_conn_t **conn, char *hostname,
 	       struct sockaddr_in *saddr_r, struct sockaddr_in *saddr_l,
 	       sasl_external_properties_t * ext_ssf)
 # endif /* SASL >= 20000 */
+#else /* __STDC__ */
+# ERROR "SASL requires __STDC__"
+#endif /* __STDC__ */
 {
 	int result;
 
@@ -5645,14 +5841,14 @@ get_sasl_user(val, len, auth_type, user, user_len)
 	SM_ASSERT(user_len > 0);
 
 	*user = '\0';
-	if (NULL == auth_type || '\0' == *auth_type)
+	if (SM_IS_EMPTY(auth_type))
 		return;
 	if (0 == len)
 		return;
 
 # define DIGMD5U	"username=\""
 # define DIGMD5U_L	(sizeof(DIGMD5U) - 1)
-	if (sm_strcasecmp(auth_type, "digest-md5") == 0 &&
+	if (SM_STRCASEEQ(auth_type, "digest-md5") &&
 	    strncmp(val, DIGMD5U, DIGMD5U_L) == 0)
 	{
 		char *s;
@@ -5678,7 +5874,7 @@ get_sasl_user(val, len, auth_type, user, user_len)
 			}
 		}
 	}
-	else if (sm_strcasecmp(auth_type, "cram-md5") == 0)
+	else if (SM_STRCASEEQ(auth_type, "cram-md5"))
 	{
 		char *s;
 
@@ -5692,8 +5888,8 @@ get_sasl_user(val, len, auth_type, user, user_len)
 		}
 	}
 
-	else if (sm_strcasecmp(auth_type, "plain") == 0 ||
-		 sm_strcasecmp(auth_type, "login") == 0)
+	else if (SM_STRCASEEQ(auth_type, "plain") ||
+		 SM_STRCASEEQ(auth_type, "login"))
 	{
 		/*
 		**  RFC 4616: The PLAIN Simple Authentication and
diff --git a/contrib/sendmail/src/stab.c b/contrib/sendmail/src/stab.c
index 6835662ae855..3fe1c7d60a00 100644
--- a/contrib/sendmail/src/stab.c
+++ b/contrib/sendmail/src/stab.c
@@ -15,6 +15,10 @@
 
 SM_RCSID("@(#)$Id: stab.c,v 8.92 2013-11-22 20:51:56 ca Exp $")
 
+#include 
+#if USE_EAI
+# include 
+#endif
 #if DANE
 # include 
 #endif
@@ -62,8 +66,23 @@ stab(name, type, op)
 	*/
 
 	hfunc = type;
-	for (p = name; *p != '\0'; p++)
-		hfunc = ((hfunc << 1) ^ (SM_LOWER(*p) & 0377)) % STABSIZE;
+#if USE_EAI
+	if (!addr_is_ascii(name))
+	{
+		char *lower, *cstr;
+
+		lower = sm_lowercase(name);
+		for (cstr = lower; *cstr != '\0'; cstr++)
+			hfunc = ((hfunc << 1) ^ ((*cstr) & 0377)) % STABSIZE;
+	}
+	else
+#endif
+	/* "else" in #if code above */
+	{
+		for (p = name; *p != '\0'; p++)
+			hfunc = ((hfunc << 1) ^ (SM_LOWER(*p) & 0377)) %
+				STABSIZE;
+	}
 
 	if (tTd(36, 9))
 		sm_dprintf("(hfunc=%d) ", hfunc);
@@ -78,7 +97,7 @@ stab(name, type, op)
 	else
 	{
 		while ((s = *ps) != NULL &&
-		       (s->s_symtype != type || sm_strcasecmp(name, s->s_name)))
+		       (s->s_symtype != type || !SM_STRCASEEQ(name, s->s_name)))
 			ps = &s->s_next;
 	}
 
diff --git a/contrib/sendmail/src/stats.c b/contrib/sendmail/src/stats.c
index 382382d53de5..7abe49a5b4e2 100644
--- a/contrib/sendmail/src/stats.c
+++ b/contrib/sendmail/src/stats.c
@@ -136,7 +136,7 @@ poststats(sfile)
 	static bool entered = false;
 	long sff = SFF_REGONLY|SFF_OPENASROOT;
 	struct statistics stats;
-	extern off_t lseek();
+	extern off_t lseek __P((int, off_t, int));
 
 	if (sfile == NULL || *sfile == '\0' || !GotStats || entered)
 		return;
diff --git a/contrib/sendmail/src/tls.c b/contrib/sendmail/src/tls.c
index 696d32fbb6df..e2cac7632288 100644
--- a/contrib/sendmail/src/tls.c
+++ b/contrib/sendmail/src/tls.c
@@ -13,7 +13,6 @@
 SM_RCSID("@(#)$Id: tls.c,v 8.127 2013-11-27 02:51:11 gshapiro Exp $")
 
 #if STARTTLS
-# include 
 # include 
 # include 
 # include 
@@ -24,12 +23,19 @@ SM_RCSID("@(#)$Id: tls.c,v 8.127 2013-11-27 02:51:11 gshapiro Exp $")
 # if _FFR_TLS_ALTNAMES
 #  include 
 # endif
+# include 
 
 # if defined(OPENSSL_VERSION_NUMBER) && OPENSSL_VERSION_NUMBER <= 0x00907000L
-# ERROR: OpenSSL version OPENSSL_VERSION_NUMBER is unsupported.
+#  ERROR "OpenSSL version OPENSSL_VERSION_NUMBER is unsupported."
 # endif
 
-# if OPENSSL_VERSION_NUMBER >= 0x10100000L && OPENSSL_VERSION_NUMBER < 0x20000000L
+/*
+**  *SSL version numbers:
+**  OpenSSL 0.9 - 1.1 (so far), 3.0 (in alpha)
+**  LibreSSL 2.0 (0x20000000L - part of "These will never change")
+*/
+
+# if (OPENSSL_VERSION_NUMBER >= 0x10100000L && OPENSSL_VERSION_NUMBER < 0x20000000L) || OPENSSL_VERSION_NUMBER >= 0x30000000L
 #  define MTA_HAVE_DH_set0_pqg 1
 #  define MTA_HAVE_DSA_GENERATE_EX	1
 
@@ -228,7 +234,7 @@ tls_rand_init(randfile, logl)
 	/* set default values */
 	ok = false;
 	done = RI_FAIL;
-	randdef = (randfile == NULL || *randfile == '\0') ? RF_MISS : RF_OK;
+	randdef = (SM_IS_EMPTY(randfile)) ? RF_MISS : RF_OK;
 #  if EGD
 	if (randdef == RF_OK && sm_strncasecmp(randfile, "egd:", 4) == 0)
 	{
@@ -244,6 +250,7 @@ tls_rand_init(randfile, logl)
 	}
 	else
 #  endif /* EGD */
+	/* "else" in #if code above */
 	if (randdef == RF_OK && sm_strncasecmp(randfile, "file:", 5) == 0)
 	{
 		int fd;
@@ -1160,17 +1167,16 @@ inittls(ctx, req, options, srv, certfile, keyfile, cacertpath, cacertfile, dhpar
 	if (tTd(96, 101) || getenv("SSL_MODE_AUTO_RETRY") != NULL)
 			SSL_CTX_set_mode(*ctx, SSL_MODE_AUTO_RETRY);
 	else
-#  endif
+#  endif /* _FFR_TESTS */
+	/* "else" in #if code above */
 	SSL_CTX_clear_mode(*ctx, SSL_MODE_AUTO_RETRY);
 # endif /* defined(SSL_MODE_AUTO_RETRY) && OPENSSL_VERSION_NUMBER >= 0x10100000L && OPENSSL_VERSION_NUMBER < 0x20000000L */
 
-
 # if TLS_NO_RSA
 	/* turn off backward compatibility, required for no-rsa */
 	SSL_CTX_set_options(*ctx, SSL_OP_NO_SSLv2);
 # endif
 
-
 # if !TLS_NO_RSA && MTA_RSA_TMP_CB
 	/*
 	**  Create a temporary RSA key
@@ -1561,6 +1567,25 @@ inittls(ctx, req, options, srv, certfile, keyfile, cacertpath, cacertfile, dhpar
 		}
 	}
 
+# if MTA_HAVE_TLSv1_3
+	/* install our own cipher suites */
+	if (!SM_IS_EMPTY(CipherSuites))
+	{
+		if (SSL_CTX_set_ciphersuites(*ctx, CipherSuites) <= 0)
+		{
+			if (LogLevel > 7)
+			{
+				sm_syslog(LOG_WARNING, NOQID,
+					  "STARTTLS=%s, error: SSL_CTX_set_ciphersuites(%s) failed, suites ignored",
+					  who, CipherSuites);
+
+				tlslogerr(LOG_WARNING, 9, who);
+			}
+			/* failure if setting to this suites is required? */
+		}
+	}
+# endif /* MTA_HAVE_TLSv1_3 */
+
 	if (LogLevel > 12)
 		sm_syslog(LOG_INFO, NOQID, "STARTTLS=%s, init=%d", who, ok);
 
@@ -1796,7 +1821,7 @@ tls_get_info(ssl, srv, host, mac, certreq)
 	if (cert != NULL)
 	{
 		X509_NAME *subj, *issuer;
-		char buf[MAXNAME];
+		char buf[MAXNAME];	/* EAI: not affected */
 
 		subj = X509_get_subject_name(cert);
 		issuer = X509_get_issuer_name(cert);
@@ -1885,6 +1910,7 @@ tls_get_info(ssl, srv, host, mac, certreq)
 	}
 	else
 # endif /* if DANE */
+	/* "else" in #if code above */
 	switch (verifyok)
 	{
 	  case X509_V_OK:
@@ -2535,7 +2561,7 @@ x509_verify_cb(ok, ctx)
 	return ok;
 }
 
-# if !USE_OPENSSL_ENGINE
+# if !USE_OPENSSL_ENGINE && !defined(OPENSSL_NO_ENGINE)
 /*
 **  TLS_SET_ENGINE -- set up ENGINE if needed
 **
@@ -2568,8 +2594,14 @@ TLS_set_engine(id, isprefork)
 			TLSEngineInitialized);
 	if (TLSEngineInitialized)
 		return 1;
-	if (id == NULL || *id == '\0')
+	if (SM_IS_EMPTY(id))
 		return 1;
+#  if !defined(ENGINE_METHOD_ALL)
+	if (LogLevel > 9)
+		sm_syslog(LOG_NOTICE, NOQID,
+			"engine=%s, status=engines_not_support", id)
+	goto error;
+#  endif
 
 	/* is this the "right time" to initialize the engine? */
 	if (isprefork != SSLEngineprefork)
@@ -2654,5 +2686,5 @@ TLS_set_engine(id, isprefork)
 		ENGINE_free(e);
 	return 0;
 }
-# endif /* !USE_OPENSSL_ENGINE */
+# endif /* !USE_OPENSSL_ENGINE && !defined(OPENSSL_NO_ENGINE) */
 #endif /* STARTTLS */
diff --git a/contrib/sendmail/src/tls.h b/contrib/sendmail/src/tls.h
index 0e03b81933a9..5ca6d7eab734 100644
--- a/contrib/sendmail/src/tls.h
+++ b/contrib/sendmail/src/tls.h
@@ -11,7 +11,6 @@
 #ifndef _TLS_H
 # define _TLS_H 1
 
-
 #if STARTTLS
 # include 
 # if !TLS_NO_RSA
@@ -28,6 +27,16 @@
 #  define TLS_version_num SSLeay
 # endif
 
+#ifndef MTA_HAVE_TLSv1_3
+/*
+**  HACK: if openssl can disable TLSv1_3 then "assume" it supports all
+**   related functions!
+*/
+# ifdef SSL_OP_NO_TLSv1_3
+#  define MTA_HAVE_TLSv1_3 1
+# endif
+#endif
+
 #ifdef _DEFINE
 # define EXTERN
 #else
@@ -67,7 +76,7 @@ struct dane_tlsa_S
 	char		*dane_tlsa_sni;
 };
 
-# define TLSAFLNONE	0x00000000	/* currently unused */
+# define TLSAFLNONE	0x00000000
 /* Dane Mode */
 # define TLSAFLALWAYS	0x00000001
 # define TLSAFLSECURE	0x00000002
@@ -127,7 +136,10 @@ struct dane_tlsa_S
 #define TLS_I_RSA_TMP	0x00000100	/* RSA TMP must be generated */
 #define TLS_I_USE_KEY	0x00000200	/* private key must usable */
 #define TLS_I_USE_CERT	0x00000400	/* certificate must be usable */
-#define TLS_I_VRFY_PATH	0x00000800	/* load verify path must succeed */
+/*
+not "read" anywhere
+#define TLS_I_VRFY_PATH	0x00000800	* load verify path must succeed *
+*/
 #define TLS_I_VRFY_LOC	0x00001000	/* load verify default must succeed */
 #define TLS_I_CACHE	0x00002000	/* require cache */
 #define TLS_I_TRY_DH	0x00004000	/* try DH certificate */
@@ -150,7 +162,7 @@ struct dane_tlsa_S
 			  TLS_I_USE_KEY | TLS_I_USE_CERT | TLS_I_CACHE)
 
 /* server requirements */
-#define TLS_I_SRV	(TLS_I_SRV_CERT | TLS_I_RSA_TMP | TLS_I_VRFY_PATH | \
+#define TLS_I_SRV	(TLS_I_SRV_CERT | TLS_I_RSA_TMP | /*TLS_I_VRFY_PATH|*/ \
 			 TLS_I_VRFY_LOC | TLS_I_TRY_DH | TLS_I_CACHE)
 
 /* client requirements */
@@ -175,7 +187,7 @@ struct dane_tlsa_S
 
 /* functions */
 extern int	endtls __P((SSL **, const char *));
-extern int	get_tls_se_options __P((ENVELOPE *, SSL *, tlsi_ctx_T *, bool));
+extern int	get_tls_se_features __P((ENVELOPE *, SSL *, tlsi_ctx_T *, bool));
 extern int	init_tls_library __P((bool _fipsmode));
 extern bool	inittls __P((SSL_CTX **, unsigned long, unsigned long, bool, char *, char *, char *, char *, char *));
 extern bool	initclttls __P((bool));
@@ -201,6 +213,9 @@ EXTERN char	*CltCACertFile;	/* file with CA certificate */
 EXTERN char	*CltCertFile;	/* file with client certificate */
 EXTERN char	*CltKeyFile;	/* file with client private key */
 EXTERN char	*CipherList;	/* list of ciphers */
+#if MTA_HAVE_TLSv1_3
+EXTERN char	*CipherSuites;	/* cipher suites */
+#endif
 EXTERN char	*CertFingerprintAlgorithm;	/* name of fingerprint alg */
 EXTERN const EVP_MD	*EVP_digest;	/* digest for cert fp */
 EXTERN char	*DHParams;	/* file with DH parameters */
@@ -220,7 +235,11 @@ EXTERN bool	SSLEngineprefork;
 # if USE_OPENSSL_ENGINE
 #define TLS_set_engine(id, prefork) SSL_set_engine(id)
 # else
+#  if !defined(OPENSSL_NO_ENGINE)
 int TLS_set_engine __P((const char *, bool));
+#  else
+#define TLS_set_engine(id, prefork)	1
+#  endif
 # endif
 
 extern int	set_tls_rd_tmo __P((int));
diff --git a/contrib/sendmail/src/tlsh.c b/contrib/sendmail/src/tlsh.c
index 4b968268cca0..56dad3568e4f 100644
--- a/contrib/sendmail/src/tlsh.c
+++ b/contrib/sendmail/src/tlsh.c
@@ -57,6 +57,8 @@ data2hex(buf, blen, hex, hlen)
 	return h;
 }
 
+# if DANE
+
 /*
 **  TLS_DATA_MD -- calculate MD for data
 **
@@ -102,8 +104,6 @@ tls_data_md(buf, len, md)
 	return (int)md_len;
 }
 
-#if DANE
-
 /*
 **  PUBKEY_FP -- get public key fingerprint
 **
@@ -153,7 +153,10 @@ pubkey_fp(cert, mdalg, fp)
 
 	md = EVP_get_digestbyname(mdalg);
 	if (NULL == md)
+	{
+		SM_FREE(buf);
 		return DANE_VRFY_FAIL;
+	}
 	len = i2d_X509_PUBKEY(X509_get_X509_PUBKEY(cert), &end);
 	r = tls_data_md(buf, len, md);
 	if (r < 0)
@@ -196,7 +199,7 @@ dane_tlsa_chk(rr, len, host, log)
 	SM_ASSERT(rr != NULL);
 
 	alg = (int)rr[2];
-	if ((int)rr[0] == 3 && (int)rr[1] == 1 && (alg >= 0 || alg <= 2))
+	if ((int)rr[0] == 3 && (int)rr[1] == 1 && (alg >= 0 && alg <= 2))
 		return alg;
 	if (log && LogLevel > 9)
 		sm_syslog(LOG_NOTICE, NOQID,
@@ -258,6 +261,6 @@ dane_tlsa_free(dane_tlsa)
 	return 0;
 
 }
-#endif /* DANE */
+# endif /* DANE */
 
 #endif /* STARTTLS */
diff --git a/contrib/sendmail/src/trace.c b/contrib/sendmail/src/trace.c
index afc43a5cf4bd..7e4d18fb624c 100644
--- a/contrib/sendmail/src/trace.c
+++ b/contrib/sendmail/src/trace.c
@@ -204,7 +204,7 @@ void
 tTflag(s)
 	register char *s;
 {
-	if (s == NULL || *s == '\0')
+	if (SM_IS_EMPTY(s))
 		s = DefFlags;
 
 	for (;;)
diff --git a/contrib/sendmail/src/udb.c b/contrib/sendmail/src/udb.c
index 00ee857ffe40..0542952c7e28 100644
--- a/contrib/sendmail/src/udb.c
+++ b/contrib/sendmail/src/udb.c
@@ -160,7 +160,7 @@ udbexpand(a, sendq, aliaslevel, e)
 	}
 
 	/* short circuit the process if no chance of a match */
-	if (UdbSpec == NULL || UdbSpec[0] == '\0')
+	if (SM_IS_EMPTY(UdbSpec))
 		return EX_OK;
 
 	/* extract user to do userdb matching on */
@@ -192,7 +192,7 @@ udbexpand(a, sendq, aliaslevel, e)
 # endif
 		char userbuf[MEMCHUNKSIZE];
 # if HESIOD && HES_GETMAILHOST
-		char pobuf[MAXNAME];
+		char pobuf[MAXNAME];	/* EAI:should be ok, no UTF8? */
 # endif
 # if defined(NEWDB) && DB_VERSION_MAJOR > 1
 		DBC *dbc = NULL;
@@ -615,7 +615,7 @@ udbmatch(user, field, rpool)
 	}
 
 	/* short circuit if no spec */
-	if (UdbSpec == NULL || UdbSpec[0] == '\0')
+	if (SM_IS_EMPTY(UdbSpec))
 		return NULL;
 
 	/* short circuit name begins with '\\' since it can't possibly match */
@@ -845,15 +845,13 @@ udb_map_lookup(map, name, av, statp)
 	char *val;
 	char *key;
 	char *SM_NONVOLATILE result = NULL;
-	char keybuf[MAXNAME + 1];
+	char keybuf[MAXNAME + 1];	/* EAI:ok */
 
 	if (tTd(28, 20) || tTd(38, 20))
 		sm_dprintf("udb_map_lookup(%s, %s)\n", map->map_mname, name);
 
 	if (bitset(MF_NOFOLDCASE, map->map_mflags))
-	{
 		key = name;
-	}
 	else
 	{
 		int keysize = strlen(name);
@@ -862,7 +860,7 @@ udb_map_lookup(map, name, av, statp)
 			keysize = sizeof(keybuf) - 1;
 		memmove(keybuf, name, keysize);
 		keybuf[keysize] = '\0';
-		makelower(keybuf);
+		makelower_buf(keybuf, keybuf, sizeof(keybuf));
 		key = keybuf;
 	}
 	val = udbmatch(key, map->map_file, NULL);
@@ -876,6 +874,8 @@ udb_map_lookup(map, name, av, statp)
 	SM_FINALLY
 		sm_free(val);
 	SM_END_TRY
+	if (key != name && key != keybuf)
+		SM_FREE(key);
 	return result;
 }
 /*
@@ -968,7 +968,7 @@ _udbx_init(e)
 # if HESIOD
 		  case 'h':	/* use hesiod */
 		  case 'H':
-			if (sm_strcasecmp(spec, "hesiod") != 0)
+			if (!SM_STRCASEEQ(spec, "hesiod"))
 				goto badspec;
 			up->udb_type = UDB_HESIOD;
 			up->udb_pid = CurrentPid;
@@ -997,13 +997,13 @@ _udbx_init(e)
 #  else /* DB_VERSION_MAJOR < 2 */
 			{
 				int flags = DB_RDONLY;
-#  if DB_VERSION_MAJOR > 2
+#   if DB_VERSION_MAJOR > 2
 				int ret;
-#  endif /* DB_VERSION_MAJOR > 2 */
+#   endif /* DB_VERSION_MAJOR > 2 */
 
 				SM_DB_FLAG_ADD(flags);
 				up->udb_dbp = NULL;
-#  if DB_VERSION_MAJOR > 2
+#   if DB_VERSION_MAJOR > 2
 				ret = db_create(&up->udb_dbp, NULL, 0);
 				if (ret != 0)
 				{
@@ -1022,20 +1022,20 @@ _udbx_init(e)
 								0644);
 					if (ret != 0)
 					{
-#ifdef DB_OLD_VERSION
+#    ifdef DB_OLD_VERSION
 						if (ret == DB_OLD_VERSION)
 							ret = EINVAL;
-#endif
+#    endif
 						(void) up->udb_dbp->close(up->udb_dbp, 0);
 						up->udb_dbp = NULL;
 					}
 				}
 				errno = ret;
-#  else /* DB_VERSION_MAJOR > 2 */
+#   else /* DB_VERSION_MAJOR > 2 */
 				errno = db_open(up->udb_dbname, DB_BTREE,
 						flags, 0644, NULL,
 						NULL, &up->udb_dbp);
-#  endif /* DB_VERSION_MAJOR > 2 */
+#   endif /* DB_VERSION_MAJOR > 2 */
 			}
 #  endif /* DB_VERSION_MAJOR < 2 */
 			if (up->udb_dbp == NULL)
@@ -1274,7 +1274,7 @@ hes_udb_get(key, info)
 		return 1;
 	}
 #  else /* HESIOD_INIT */
-	if (hp == NULL || hp[0] == NULL)
+	if (SM_IS_EMPTY(hp))
 	{
 		/* network problem or timeout */
 		if (hes_error() == HES_ER_NET)
diff --git a/contrib/sendmail/src/usersmtp.c b/contrib/sendmail/src/usersmtp.c
index b4ff5ccbc80a..5fe079176eaf 100644
--- a/contrib/sendmail/src/usersmtp.c
+++ b/contrib/sendmail/src/usersmtp.c
@@ -15,8 +15,7 @@
 
 SM_RCSID("@(#)$Id: usersmtp.c,v 8.488 2013-11-22 20:51:57 ca Exp $")
 
-#include 
-
+#include 
 
 static void	esmtp_check __P((char *, bool, MAILER *, MCI *, ENVELOPE *));
 static void	helo_options __P((char *, bool, MAILER *, MCI *, ENVELOPE *));
@@ -43,6 +42,34 @@ extern void	sm_sasl_free __P((void *));
 static char	SmtpMsgBuffer[MAXLINE];		/* buffer for commands */
 static char	SmtpReplyBuffer[MAXLINE];	/* buffer for replies */
 static bool	SmtpNeedIntro;		/* need "while talking" in transcript */
+
+/*
+**  SMTPCCLRSE -- clear session related data in envelope
+**
+**	Parameters:
+**		e -- the envelope.
+**
+**	Returns:
+**		none.
+*/
+
+void
+smtpclrse(e)
+	ENVELOPE *e;
+{
+	SmtpError[0] = '\0';
+	e->e_rcode = 0;
+	e->e_renhsc[0] = '\0';
+	e->e_text = NULL;
+
+	/*
+	**  Reset to avoid access to potentially dangling pointer
+	**  via macvalue().
+	*/
+
+	e->e_mci = NULL;
+}
+
 /*
 **  SMTPINIT -- initialize SMTP.
 **
@@ -73,7 +100,7 @@ smtpinit(m, mci, e, onlyhelo)
 	register char *p;
 	register char *hn;
 #if _FFR_EXPAND_HELONAME
-	char hnbuf[MAXNAME + 1];
+	char hnbuf[MAXNAME + 1];	/* EAI:ok:EHLO name must be ASCII */
 #endif
 	char *enhsc;
 
@@ -225,7 +252,7 @@ smtpinit(m, mci, e, onlyhelo)
 		*p = '\0';
 	if (!bitnset(M_NOLOOPCHECK, m->m_flags) &&
 	    !bitnset(M_LMTP, m->m_flags) &&
-	    sm_strcasecmp(&SmtpReplyBuffer[4], MyHostName) == 0)
+	    SM_STRCASEEQ(&SmtpReplyBuffer[4], MyHostName))
 	{
 		syserr("553 5.3.5 %s config error: mail loops back to me (MX problem?)",
 			CurHostName);
@@ -340,9 +367,9 @@ str_union(s1, s2, rpool)
 	char *hr, *h1, *h, *res;
 	int l1, l2, rl;
 
-	if (s1 == NULL || *s1 == '\0')
+	if (SM_IS_EMPTY(s1))
 		return s2;
-	if (s2 == NULL || *s2 == '\0')
+	if (SM_IS_EMPTY(s2))
 		return s1;
 	l1 = strlen(s1);
 	l2 = strlen(s2);
@@ -455,43 +482,43 @@ helo_options(line, firstline, m, mci, e)
 	p = strpbrk(line, " =");
 	if (p != NULL)
 		*p++ = '\0';
-	if (sm_strcasecmp(line, "size") == 0)
+	if (SM_STRCASEEQ(line, "size"))
 	{
 		mci->mci_flags |= MCIF_SIZE;
 		if (p != NULL)
 			mci->mci_maxsize = atol(p);
 	}
-	else if (sm_strcasecmp(line, "8bitmime") == 0)
+	else if (SM_STRCASEEQ(line, "8bitmime"))
 	{
 		mci->mci_flags |= MCIF_8BITMIME;
 		mci->mci_flags &= ~MCIF_7BIT;
 	}
-	else if (sm_strcasecmp(line, "expn") == 0)
+	else if (SM_STRCASEEQ(line, "expn"))
 		mci->mci_flags |= MCIF_EXPN;
-	else if (sm_strcasecmp(line, "dsn") == 0)
+	else if (SM_STRCASEEQ(line, "dsn"))
 		mci->mci_flags |= MCIF_DSN;
-	else if (sm_strcasecmp(line, "enhancedstatuscodes") == 0)
+	else if (SM_STRCASEEQ(line, "enhancedstatuscodes"))
 		mci->mci_flags |= MCIF_ENHSTAT;
-	else if (sm_strcasecmp(line, "pipelining") == 0)
+	else if (SM_STRCASEEQ(line, "pipelining"))
 		mci->mci_flags |= MCIF_PIPELINED;
-	else if (sm_strcasecmp(line, "verb") == 0)
+	else if (SM_STRCASEEQ(line, "verb"))
 		mci->mci_flags |= MCIF_VERB;
-#if _FFR_EAI
-	else if (sm_strcasecmp(line, "smtputf8") == 0)
+#if USE_EAI
+	else if (SM_STRCASEEQ(line, "smtputf8"))
 		mci->mci_flags |= MCIF_EAI;
-#endif /* _FFR_EAI */
+#endif
 #if STARTTLS
-	else if (sm_strcasecmp(line, "starttls") == 0)
+	else if (SM_STRCASEEQ(line, "starttls"))
 		mci->mci_flags |= MCIF_TLS;
 #endif
-	else if (sm_strcasecmp(line, "deliverby") == 0)
+	else if (SM_STRCASEEQ(line, "deliverby"))
 	{
 		mci->mci_flags |= MCIF_DLVR_BY;
 		if (p != NULL)
 			mci->mci_min_by = atol(p);
 	}
 #if SASL
-	else if (sm_strcasecmp(line, "auth") == 0)
+	else if (SM_STRCASEEQ(line, "auth"))
 	{
 		if (p != NULL && *p != '\0' &&
 		    !bitset(MCIF_AUTH2, mci->mci_flags))
@@ -745,10 +772,10 @@ readauth(filename, safe, sai, rpool)
 	char *s;
 	char buf[MAXLINE];
 
-	if (filename == NULL || filename[0] == '\0')
+	if (SM_IS_EMPTY(filename))
 		return EX_UNAVAILABLE;
 
-#if !_FFR_ALLOW_SASLINFO
+# if !_FFR_ALLOW_SASLINFO
 	/*
 	**  make sure we don't use a program that is not
 	**  accessible to the user who specified a different authinfo file.
@@ -780,7 +807,8 @@ readauth(filename, safe, sai, rpool)
 				       (void *) &fd, SM_IO_RDONLY, NULL);
 	}
 	else
-#endif /* !_FFR_ALLOW_SASLINFO */
+# endif /* !_FFR_ALLOW_SASLINFO */
+	/* "else" in #if code above */
 	{
 		pid = -1;
 		sff = SFF_REGONLY|SFF_SAFEDIRPATH|SFF_NOWLINK
@@ -790,7 +818,7 @@ readauth(filename, safe, sai, rpool)
 		if (DontLockReadFiles)
 			sff |= SFF_NOLOCK;
 
-#if _FFR_ALLOW_SASLINFO
+# if _FFR_ALLOW_SASLINFO
 		/*
 		**  XXX: make sure we don't read or open files that are not
 		**  accessible to the user who specified a different authinfo
@@ -798,10 +826,10 @@ readauth(filename, safe, sai, rpool)
 		*/
 
 		sff |= SFF_MUSTOWN;
-#else /* _FFR_ALLOW_SASLINFO */
+# else /* _FFR_ALLOW_SASLINFO */
 		if (safe)
 			sff |= SFF_OPENASROOT;
-#endif /* _FFR_ALLOW_SASLINFO */
+# endif /* _FFR_ALLOW_SASLINFO */
 
 		f = safefopen(filename, O_RDONLY, 0, sff);
 	}
@@ -1138,9 +1166,9 @@ getsimple(context, id, result, len)
 	unsigned *len;
 {
 	char *h, *s;
-# if SASL > 10509
+#  if SASL > 10509
 	bool addrealm;
-# endif
+#  endif
 	size_t l;
 	SASL_AI_T *sai;
 	char *authid = NULL;
@@ -1189,10 +1217,10 @@ getsimple(context, id, result, len)
 
 	  case SASL_CB_AUTHNAME:
 		h = (*sai)[SASL_AUTHID];
-# if SASL > 10509
+#  if SASL > 10509
 		/* XXX maybe other mechanisms too?! */
 		addrealm = (*sai)[SASL_MECH] != NULL &&
-			   sm_strcasecmp((*sai)[SASL_MECH], "CRAM-MD5") == 0;
+			   SM_STRCASEEQ((*sai)[SASL_MECH], "cram-md5");
 
 		/*
 		**  Add realm to authentication id unless authid contains
@@ -1238,7 +1266,7 @@ getsimple(context, id, result, len)
 				authid = (*sai)[SASL_ID_REALM];
 		}
 		else
-# endif /* SASL > 10509 */
+#  endif /* SASL > 10509 */
 			authid = h;
 		l = strlen(authid) + 1;
 		s = sm_sasl_malloc(l);
@@ -1325,38 +1353,38 @@ getsecret(conn, context, id, psecret)
 */
 
 int
-#if SASL > 10515
+# if SASL > 10515
 safesaslfile(context, file, type)
-#else
+# else
 safesaslfile(context, file)
-#endif
+# endif
 	void *context;
 # if SASL >= 20000
 	const char *file;
 # else
 	char *file;
 # endif
-#if SASL > 10515
-# if SASL >= 20000
+# if SASL > 10515
+#  if SASL >= 20000
 	sasl_verify_type_t type;
-# else
+#  else
 	int type;
+#  endif
 # endif
-#endif
 {
 	long sff;
 	int r;
-#if SASL <= 10515
+# if SASL <= 10515
 	size_t len;
-#endif
+# endif
 	char *p;
 
-	if (file == NULL || *file == '\0')
+	if (SM_IS_EMPTY(file))
 		return SASL_OK;
 	if (tTd(95, 16))
 		sm_dprintf("safesaslfile=%s\n", file);
 	sff = SFF_SAFEDIRPATH|SFF_NOWLINK|SFF_NOWWFILES|SFF_ROOTOK;
-#if SASL <= 10515
+# if SASL <= 10515
 	if ((p = strrchr(file, '/')) == NULL)
 		p = file;
 	else
@@ -1372,7 +1400,7 @@ safesaslfile(context, file)
 		if (!bitnset(DBS_GROUPWRITABLESASLDBFILE, DontBlameSendmail))
 			sff |= SFF_NOGWFILES;
 	}
-#else /* SASL <= 10515 */
+# else /* SASL <= 10515 */
 	/* files containing passwords should be not readable */
 	if (type == SASL_VRFY_PASSWD)
 	{
@@ -1383,7 +1411,7 @@ safesaslfile(context, file)
 		if (!bitnset(DBS_GROUPWRITABLESASLDBFILE, DontBlameSendmail))
 			sff |= SFF_NOGWFILES;
 	}
-#endif /* SASL <= 10515 */
+# endif /* SASL <= 10515 */
 
 	p = (char *) file;
 	if ((r = safefile(p, RunAsUid, RunAsGid, RunAsUserName, sff,
@@ -1429,7 +1457,7 @@ saslgetrealm(context, id, availrealms, result)
 		sm_syslog(LOG_INFO, NOQID,
 			  "AUTH=client, realm=%s, available realms=%s",
 			  r == NULL ? "" : r,
-			  (availrealms == NULL || *availrealms == NULL)
+			  (NULL == availrealms || SM_IS_EMPTY(*availrealms))
 				? "" : *availrealms);
 
 	/* check whether context is in list */
@@ -1474,9 +1502,9 @@ iteminlist(item, list, delim)
 	char *s;
 	int len;
 
-	if (list == NULL || *list == '\0')
+	if (SM_IS_EMPTY(list))
 		return NULL;
-	if (item == NULL || *item == '\0')
+	if (SM_IS_EMPTY(item))
 		return NULL;
 	s = list;
 	len = strlen(item);
@@ -1516,7 +1544,7 @@ removemech(rem, list, rpool)
 
 	if (list == NULL)
 		return NULL;
-	if (rem == NULL || *rem == '\0')
+	if (SM_IS_EMPTY(rem))
 	{
 		/* take out what? */
 		return NULL;
@@ -1596,9 +1624,9 @@ attemptauth(m, mci, e, sai)
 
 	/* MUST NOT be a multiple of 4: bug in some sasl_encode64() versions */
 	char in64[MAXOUTLEN + 1];
-#if NETINET || (NETINET6 && SASL >= 20000)
+# if NETINET || (NETINET6 && SASL >= 20000)
 	extern SOCKADDR CurHostAddr;
-#endif
+# endif
 
 	/* no mechanism selected (yet) */
 	(*sai)[SASL_MECH] = NULL;
@@ -1633,9 +1661,9 @@ attemptauth(m, mci, e, sai)
 	/* XXX should these be options settable via .cf ? */
 	ssp.max_ssf = MaxSLBits;
 	ssp.maxbufsize = MAXOUTLEN;
-#  if 0
+# if 0
 	ssp.security_flags = SASL_SEC_NOPLAINTEXT;
-#  endif
+# endif
 	saslresult = sasl_setprop(mci->mci_conn, SASL_SEC_PROPS, &ssp);
 	if (saslresult != SASL_OK)
 		return EX_TEMPFAIL;
@@ -1776,8 +1804,8 @@ attemptauth(m, mci, e, sai)
 	if (out == NULL
 		/* login and digest-md5 up to 1.5.28 set out="" */
 	    || (outlen == 0 &&
-		(sm_strcasecmp(mechusing, "LOGIN") == 0 ||
-		 sm_strcasecmp(mechusing, "DIGEST-MD5") == 0))
+		(SM_STRCASEEQ(mechusing, "login") ||
+		 SM_STRCASEEQ(mechusing, "digest-md5")))
 	   )
 	{
 		/* no initial response */
@@ -1962,9 +1990,9 @@ smtpauth(m, mci, e)
 	callbacks[CB_USER_IDX].context = (void *) &mci->mci_sai;
 	callbacks[CB_AUTHNAME_IDX].context = (void *) &mci->mci_sai;
 	callbacks[CB_GETREALM_IDX].context = (void *) &mci->mci_sai;
-#if 0
+# if 0
 	callbacks[CB_SAFESASL_IDX].context = (void *) &mci->mci_sai;
-#endif
+# endif
 
 	/* set default value for realm */
 	if ((mci->mci_sai)[SASL_DEFREALM] == NULL)
@@ -2013,6 +2041,9 @@ smtpauth(m, mci, e)
 **		m -- the mailer.
 **		mci -- the mailer connection structure.
 **		e -- the envelope (including the sender to specify).
+**
+**	Returns:
+**		exit status corresponding to mail status.
 */
 
 int
@@ -2025,8 +2056,11 @@ smtpmailfrom(m, mci, e)
 	char *bufp;
 	char *bodytype;
 	char *enhsc;
-	char buf[MAXNAME + 1];
+	char buf[MAXNAME_I + 1];
 	char optbuf[MAXLINE];
+#if _FFR_8BITENVADDR
+	int len, nlen;
+#endif
 
 	if (tTd(18, 2))
 		sm_dprintf("smtpmailfrom: CurHost=%s\n", CurHostName);
@@ -2044,18 +2078,42 @@ smtpmailfrom(m, mci, e)
 		return EX_TEMPFAIL;
 	}
 
-#if _FFR_EAI
-	/*
-	**  Abort right away if the message needs SMTPUTF8 and the
-	**  server does not advertise SMTPUTF8.
-	*/
+#if USE_EAI
+	if (bitset(EF_RESPONSE, e->e_flags) &&
+	    !bitnset(M_NO_NULL_FROM, m->m_flags))
+		buf[0] = '\0';
+	else
+	{
+		expand("\201g", buf, sizeof(buf), e);
+		if (!addr_is_ascii(buf) && !e->e_smtputf8)
+			e->e_smtputf8 = true;
+	}
 
-	if (e->e_smtputf8 && !bitset(MCIF_EAI, mci->mci_flags)) {
-		usrerrenh("5.6.7", "%s does not support SMTPUTF8", CurHostName);
-		mci_setstat(mci, EX_NOTSTICKY, "5.6.7", NULL);
+	if (e->e_smtputf8 && !SMTPUTF8)
+	{
+		extern char MsgBuf[];
+
+		/* XREF: format must be coordinated with giveresponse() */
+		usrerrenh("5.6.7", "504 SMTPUTF8 required but not enabled");
+		mci_setstat(mci, EX_NOTSTICKY, "5.6.7", MsgBuf);
 		return EX_DATAERR;
 	}
-#endif /* _FFR_EAI */
+
+	/*
+	**  Abort right away if the message needs SMTPUTF8
+	**  but the server does not advertise it.
+	*/
+
+	if (e->e_smtputf8 && !bitset(MCIF_EAI, mci->mci_flags))
+	{
+		extern char MsgBuf[];
+
+		/* XREF: format must be coordinated with giveresponse() */
+		usrerrenh("5.6.7", "504 SMTPUTF8 required but not offered");
+		mci_setstat(mci, EX_NOTSTICKY, "5.6.7", MsgBuf);
+		return EX_DATAERR;
+	}
+#endif /* USE_EAI */
 
 	/* set up appropriate options to include */
 	if (bitset(MCIF_SIZE, mci->mci_flags) && e->e_msgsize > 0)
@@ -2070,13 +2128,14 @@ smtpmailfrom(m, mci, e)
 		bufp = optbuf;
 	}
 
-#if _FFR_EAI
-	if (e->e_smtputf8) {
+#if USE_EAI
+	if (e->e_smtputf8)
+	{
 		(void) sm_snprintf(bufp, SPACELEFT(optbuf, bufp),
 				 " SMTPUTF8");
 		bufp += strlen(bufp);
 	}
-#endif /* _FFR_EAI */
+#endif /* USE_EAI */
 
 	bodytype = e->e_bodytype;
 	if (bitset(MCIF_8BITMIME, mci->mci_flags))
@@ -2191,11 +2250,24 @@ smtpmailfrom(m, mci, e)
 
 	mci->mci_state = MCIS_MAIL;
 
+#if !USE_EAI
 	if (bitset(EF_RESPONSE, e->e_flags) &&
 	    !bitnset(M_NO_NULL_FROM, m->m_flags))
 		buf[0] = '\0';
 	else
 		expand("\201g", buf, sizeof(buf), e);
+#endif /* !USE_EAI */
+#if _FFR_8BITENVADDR
+	if (tTd(18, 11))
+		sm_dprintf("mail_expand=%s\n", buf);
+	len = sizeof(buf);
+	nlen = dequote_internal_chars(buf, buf, len);
+	/* check length! but that's a bit late... */
+	if (nlen > MAXNAME)
+		sm_syslog(LOG_ERR, e->e_id, "MAIL too long: %d", nlen);
+	if (tTd(18, 11))
+		sm_dprintf("mail2=%s\n", buf);
+#endif /* _FFR_8BITENVADDR */
 	if (buf[0] == '<')
 	{
 		/* strip off  (put back on below) */
@@ -2313,6 +2385,11 @@ smtprcpt(to, m, mci, e, ctladdr, xstart)
 {
 	char *bufp;
 	char optbuf[MAXLINE];
+	char *rcpt;
+#if _FFR_8BITENVADDR
+	char buf[MAXNAME + 1];	/* EAI:ok */
+	int len, nlen;
+#endif
 
 #if PIPELINING
 	/*
@@ -2329,7 +2406,7 @@ smtprcpt(to, m, mci, e, ctladdr, xstart)
 		if (r != EX_OK)
 		{
 			markfailure(e, mci->mci_nextaddr, mci, r, false);
-			giveresponse(r, mci->mci_nextaddr->q_status,  m, mci,
+			giveresponse(r, mci->mci_nextaddr->q_status, m, mci,
 				     ctladdr, xstart, e, to);
 		}
 		mci->mci_nextaddr = mci->mci_nextaddr->q_pchain;
@@ -2414,7 +2491,21 @@ smtprcpt(to, m, mci, e, ctladdr, xstart)
 		}
 	}
 
-	smtpmessage("RCPT To:<%s>%s", m, mci, to->q_user, optbuf);
+	rcpt = to->q_user;
+#if _FFR_8BITENVADDR
+	if (tTd(18, 11))
+		sm_dprintf("rcpt=%s\n", rcpt);
+	len = sizeof(buf);
+	nlen = dequote_internal_chars(rcpt, buf, len);
+	rcpt = buf;
+	/* check length! but that's a bit late... */
+	if (nlen > MAXNAME)
+		sm_syslog(LOG_ERR, e->e_id, "RCPT too long: %d", nlen);
+	if (tTd(18, 11))
+		sm_dprintf("rcpt2=%s\n", rcpt);
+#endif /* _FFR_8BITENVADDR */
+
+	smtpmessage("RCPT To:<%s>%s", m, mci, rcpt, optbuf);
 	mci->mci_state = MCIS_RCPT;
 
 	SmtpPhase = mci->mci_phase = "client RCPT";
@@ -2497,9 +2588,7 @@ smtprcptstat(to, m, mci, e)
 			*t = '\0';
 			mci->mci_tolist = t;
 		}
-#if PIPELINING
 		mci->mci_okrcpts++;
-#endif
 		return EX_OK;
 	}
 	else if (r == 550)
@@ -2641,11 +2730,8 @@ smtpdata(m, mci, e, ctladdr, xstart)
 	else if (REPLYTYPE(r) == 5)
 	{
 		smtprset(m, mci, e);
-#if PIPELINING
-		if (mci->mci_okrcpts <= 0)
-			return mci->mci_retryrcpt ? EX_TEMPFAIL
-						  : EX_UNAVAILABLE;
-#endif
+		if (mci->mci_okrcpts <= 0 && mci->mci_retryrcpt)
+			return EX_TEMPFAIL;
 		return EX_UNAVAILABLE;
 	}
 	else if (REPLYTYPE(r) != 3)
@@ -2660,59 +2746,49 @@ smtpdata(m, mci, e, ctladdr, xstart)
 		smtprset(m, mci, e);
 		mci_setstat(mci, EX_PROTOCOL, ENHSCN(enhsc, "5.5.1"),
 			    SmtpReplyBuffer);
-#if PIPELINING
-		if (mci->mci_okrcpts <= 0)
-			return mci->mci_retryrcpt ? EX_TEMPFAIL
-						  : EX_PROTOCOL;
-#endif
+		if (mci->mci_okrcpts <= 0 && mci->mci_retryrcpt)
+			return EX_TEMPFAIL;
 		return EX_PROTOCOL;
 	}
 
-#if PIPELINING
 	if (mci->mci_okrcpts > 0)
 	{
-#endif
+		/*
+		**  Set timeout around data writes.  Make it at least
+		**  large enough for DNS timeouts on all recipients
+		**  plus some fudge factor.  The main thing is
+		**  that it should not be infinite.
+		*/
 
-	/*
-	**  Set timeout around data writes.  Make it at least large
-	**  enough for DNS timeouts on all recipients plus some fudge
-	**  factor.  The main thing is that it should not be infinite.
-	*/
+		if (tTd(18, 101))
+		{
+			/* simulate a DATA timeout */
+			timeout = 10;
+		}
+		else
+			timeout = DATA_PROGRESS_TIMEOUT * 1000;
+		sm_io_setinfo(mci->mci_out, SM_IO_WHAT_TIMEOUT, &timeout);
 
-	if (tTd(18, 101))
-	{
-		/* simulate a DATA timeout */
-		timeout = 10;
+		/*
+		**  Output the actual message.
+		*/
+
+		if (!(*e->e_puthdr)(mci, e->e_header, e, M87F_OUTER))
+			goto writeerr;
+
+		if (tTd(18, 101))
+		{
+			/* simulate a DATA timeout */
+			(void) sleep(2);
+		}
+
+		if (!(*e->e_putbody)(mci, e, NULL))
+			goto writeerr;
+
+		/*
+		**  Cleanup after sending message.
+		*/
 	}
-	else
-		timeout = DATA_PROGRESS_TIMEOUT * 1000;
-	sm_io_setinfo(mci->mci_out, SM_IO_WHAT_TIMEOUT, &timeout);
-
-
-	/*
-	**  Output the actual message.
-	*/
-
-	if (!(*e->e_puthdr)(mci, e->e_header, e, M87F_OUTER))
-		goto writeerr;
-
-	if (tTd(18, 101))
-	{
-		/* simulate a DATA timeout */
-		(void) sleep(2);
-	}
-
-	if (!(*e->e_putbody)(mci, e, NULL))
-		goto writeerr;
-
-	/*
-	**  Cleanup after sending message.
-	*/
-
-
-#if PIPELINING
-	}
-#endif
 
 #if _FFR_CATCH_BROKEN_MTAS
 	if (sm_io_getinfo(mci->mci_in, SM_IO_IS_READABLE, NULL) > 0)
@@ -2918,10 +2994,6 @@ smtpquit(m, mci, e)
 	if (CurHostName == NULL)
 		CurHostName = MyHostName;
 
-#if PIPELINING
-	mci->mci_okrcpts = 0;
-#endif
-
 	/*
 	**	Suppress errors here -- we may be processing a different
 	**	job when we do the quit connection, and we don't want the
@@ -2995,10 +3067,6 @@ smtprset(m, mci, e)
 	if (CurHostName == NULL)
 		CurHostName = MyHostName;
 
-#if PIPELINING
-	mci->mci_okrcpts = 0;
-#endif
-
 	/*
 	**  Check if connection is gone, if so
 	**  it's a tempfail and we use mci_errno
@@ -3299,17 +3367,16 @@ reply(m, mci, e, timeout, pfunc, enhstat, rtype)
 			continue;
 		}
 		if (REPLYTYPE(r) > 3 && firstline
-# if _FFR_PROXY
+#if _FFR_PROXY
 		    &&
 		    (e->e_sendmode != SM_PROXY
 		     || (e->e_sendmode == SM_PROXY
 			 && (e->e_rcode == 0 || REPLYTYPE(e->e_rcode) < 5))
 		    )
-# endif
+#endif
 		   )
 		{
 			int o = -1;
-# if PIPELINING
 			/*
 			**  ignore error iff: DATA, 5xy error, but we had
 			**  "retryable" recipients. XREF: smtpdata()
@@ -3317,7 +3384,6 @@ reply(m, mci, e, timeout, pfunc, enhstat, rtype)
 
 			if (!(rtype == XS_DATA && REPLYTYPE(r) == 5 &&
 			      mci->mci_okrcpts <= 0 && mci->mci_retryrcpt))
-# endif /* PIPELINING */
 			{
 				o = extenhsc(bufp + 4, ' ', enhstatcode);
 				if (o > 0)
@@ -3352,6 +3418,31 @@ reply(m, mci, e, timeout, pfunc, enhstat, rtype)
 					, rtype, e->e_text);
 			}
 		}
+#if _FFR_REPLY_MULTILINE
+# if _FFR_REPLY_MULTILINE > 200
+#  define MLLIMIT _FFR_REPLY_MULTILINE
+# else
+#  define MLLIMIT 1024
+# endif
+		if ((REPLYTYPE(r) > 3 && !firstline && e->e_text != NULL &&
+		    rtype != XS_QUIT) || tTd(87, 101))
+		{
+			int len;
+			char *new;
+
+			/* skip the same stuff or use o? */
+			/* but o is a local variable in the block above */
+			len = strlen(e->e_text) + strlen(bufp) + 3;
+			if (len < MLLIMIT &&
+			    (new = (char *) sm_rpool_malloc(e->e_rpool, len))
+				!= NULL)
+			{
+				sm_strlcpyn(new, len, 3, e->e_text, "; ",
+					bufp /* + o */);
+				e->e_text = new;
+			}
+		}
+#endif /* _FFR_REPLY_MULTILINE */
 
 		firstline = false;
 
diff --git a/contrib/sendmail/src/util.c b/contrib/sendmail/src/util.c
index 1cf6ddd811e7..c923298a576c 100644
--- a/contrib/sendmail/src/util.c
+++ b/contrib/sendmail/src/util.c
@@ -16,32 +16,50 @@
 SM_RCSID("@(#)$Id: util.c,v 8.427 2013-11-22 20:51:57 ca Exp $")
 
 #include 
-#include 
 #include 
+#if USE_EAI
+# include 
+#endif
 
 /*
 **  NEWSTR -- Create a copy of a C string
 **
 **	Parameters:
-**		s -- the string to copy.
+**		s -- the string to copy. [A]
 **
 **	Returns:
 **		pointer to newly allocated string.
 */
 
 char *
+#if SM_HEAP_CHECK > 2
+newstr_tagged(s, tag, line, group)
+	const char *s;
+	char *tag;
+	int line;
+	int group;
+#else
 newstr(s)
 	const char *s;
+# define tag  "newstr"
+# define line 0
+# define group 0
+#endif
 {
 	size_t l;
 	char *n;
 
 	l = strlen(s);
 	SM_ASSERT(l + 1 > l);
-	n = xalloc(l + 1);
+	n = sm_malloc_tagged_x(l + 1, tag, line, group);
 	sm_strlcpy(n, s, l + 1);
 	return n;
 }
+#if SM_HEAP_CHECK <= 2
+# undef tag
+# undef line
+# undef group
+#endif
 
 /*
 **  ADDQUOTES -- Adds quotes & quote bits to a string.
@@ -49,7 +67,7 @@ newstr(s)
 **	Runs through a string and adds backslashes and quote bits.
 **
 **	Parameters:
-**		s -- the string to modify.
+**		s -- the string to modify. [A]
 **		rpool -- resource pool from which to allocate result
 **
 **	Returns:
@@ -96,9 +114,10 @@ addquotes(s, rpool)
 /*
 **  STRIPBACKSLASH -- Strip all leading backslashes from a string, provided
 **	the following character is alpha-numerical.
-**
 **	This is done in place.
 **
+**	XXX: This may be a problem for EAI?
+**
 **	Parameters:
 **		s -- the string to strip.
 **
@@ -112,7 +131,7 @@ stripbackslash(s)
 {
 	char *p, *q, c;
 
-	if (s == NULL || *s == '\0')
+	if (SM_IS_EMPTY(s))
 		return;
 	p = q = s;
 	while (*p == '\\' && (p[1] == '\\' || (isascii(p[1]) && isalnum(p[1]))))
@@ -130,8 +149,11 @@ stripbackslash(s)
 **	are only found inside comments, quoted strings, or backslash
 **	escaped.  Also verified balanced quotes and parenthesis.
 **
+**	XXX: This may be a problem for EAI? MustQuoteChars is used.
+**	If this returns false, current callers just invoke addquotes().
+**
 **	Parameters:
-**		s -- the string to modify.
+**		s -- the string to modify. [A]
 **
 **	Returns:
 **		true iff the string is RFC822 compliant, false otherwise.
@@ -189,7 +211,7 @@ rfc822_string(s)
 **	comments and quotes.
 **
 **	Parameters:
-**		string -- the string to shorten
+**		string -- the string to shorten [A]
 **		length -- the maximum size, 0 if no maximum
 **
 **	Returns:
@@ -295,11 +317,10 @@ shorten_rfc822_string(string, length)
 **  FIND_CHARACTER -- find an unquoted character in an RFC822 string
 **
 **	Find an unquoted, non-commented character in an RFC822
-**	string and return a pointer to its location in the
-**	string.
+**	string and return a pointer to its location in the string.
 **
 **	Parameters:
-**		string -- the string to search
+**		string -- the string to search [A]
 **		character -- the character to find
 **
 **	Returns:
@@ -379,9 +400,9 @@ check_bodytype(bodytype)
 	/* check body type for legality */
 	if (bodytype == NULL)
 		return BODYTYPE_NONE;
-	if (sm_strcasecmp(bodytype, "7BIT") == 0)
+	if (SM_STRCASEEQ(bodytype, "7bit"))
 		return BODYTYPE_7BIT;
-	if (sm_strcasecmp(bodytype, "8BITMIME") == 0)
+	if (SM_STRCASEEQ(bodytype, "8bitmime"))
 		return BODYTYPE_8BITMIME;
 	return BODYTYPE_ILLEGAL;
 }
@@ -390,7 +411,7 @@ check_bodytype(bodytype)
 **  TRUNCATE_AT_DELIM -- truncate string at a delimiter and append "..."
 **
 **	Parameters:
-**		str -- string to truncate
+**		str -- string to truncate [A]
 **		len -- maximum length (including '\0') (0 for unlimited)
 **		delim -- delimiter character
 **
@@ -507,13 +528,21 @@ copyplist(list, copycont, rpool)
 
 	vp++;
 
-	newvp = (char **) sm_rpool_malloc_x(rpool, (vp - list) * sizeof(*vp));
+	/*
+	**  Hack: rpool is NULL if invoked from readcf(),
+	**  so "ignore" the allocation by setting group to 0.
+	*/
+
+	newvp = (char **) sm_rpool_malloc_tagged_x(rpool,
+				(vp - list) * sizeof(*vp), "copyplist", 0,
+				NULL == rpool ? 0 : 1);
 	memmove((char *) newvp, (char *) list, (int) (vp - list) * sizeof(*vp));
 
 	if (copycont)
 	{
 		for (vp = newvp; *vp != NULL; vp++)
-			*vp = sm_rpool_strdup_x(rpool, *vp);
+			*vp = sm_rpool_strdup_tagged_x(rpool, *vp,
+					"copyplist", 0, NULL == rpool ? 0 : 1);
 	}
 
 	return newvp;
@@ -720,6 +749,27 @@ printav(fp, av)
 {
 	while (*av != NULL)
 	{
+#if _FFR_8BITENVADDR
+		if (tTd(0, 9))
+		{
+			char *cp;
+			unsigned char v;
+
+			for (cp = *av++; *cp != '\0'; cp++) {
+				v = (unsigned char)(*cp & 0x00ff);
+				sm_dprintf("%c", v);
+# if 0
+				if (isascii(v) && isprint(v))
+					sm_dprintf("%c", v);
+				else
+					sm_dprintf("\\x%hhx", v);
+# endif
+			}
+			if (*av != NULL)
+				sm_dprintf(" ");
+			continue;
+		}
+#endif /* _FFR_8BITENVADDR */
 		if (tTd(0, 44))
 			sm_dprintf("\n\t%08lx=", (unsigned long) *av);
 		else
@@ -729,6 +779,8 @@ printav(fp, av)
 		else
 			xputs(fp, *av++);
 	}
+
+	/* don't print this if invoked directly (not via xputs())? */
 	(void) sm_io_putc(fp, SM_TIME_DEFAULT, '\n');
 }
 
@@ -737,7 +789,7 @@ printav(fp, av)
 **
 **	Parameters:
 **		fp -- output file pointer.
-**		s -- string to put.
+**		s -- string to put. [A]
 **
 **	Returns:
 **		none.
@@ -757,6 +809,17 @@ xputs(fp, s)
 	extern struct metamac MetaMacros[];
 	static SM_DEBUG_T DebugANSI = SM_DEBUG_INITIALIZER("ANSI",
 		"@(#)$Debug: ANSI - enable reverse video in debug output $");
+#if _FFR_8BITENVADDR
+	if (tTd(0, 9))
+	{
+		char *av[2];
+
+		av[0] = (char *) s;
+		av[1] = NULL;
+		printav(fp, av);
+		return;
+	}
+#endif
 
 	/*
 	**  TermEscape is set here, rather than in main(),
@@ -917,42 +980,123 @@ xputs(fp, s)
 }
 
 /*
-**  MAKELOWER -- Translate a line into lower case
+**  MAKELOWER_A -- Translate a line into lower case
 **
 **	Parameters:
-**		p -- the string to translate.  If NULL, return is
-**			immediate.
+**		pp -- pointer to the string to translate (modified in place if possible). [A]
+**		rpool -- rpool to use to reallocate string if needed
+**			(if NULL: uses sm_malloc() internally)
 **
 **	Returns:
-**		none.
+**		lower cased string
 **
 **	Side Effects:
-**		String pointed to by p is translated to lower case.
+**		String pointed to by pp is translated to lower case if possible.
 */
 
-void
-makelower(p)
-	register char *p;
+char *
+makelower_a(pp, rpool)
+	char **pp;
+	SM_RPOOL_T *rpool;
 {
-	register char c;
+	char c;
+	char *orig, *p;
 
+	SM_REQUIRE(pp != NULL);
+	p = *pp;
 	if (p == NULL)
-		return;
+		return p;
+	orig = p;
+
+#if USE_EAI
+	if (!addr_is_ascii(p))
+	{
+		char *new;
+
+		new = sm_lowercase(p);
+		if (new == p)
+			return p;
+		*pp = sm_rpool_strdup_tagged_x(rpool, new, "makelower", 0, 1);
+		return *pp;
+	}
+#endif /* USE_EAI */
 	for (; (c = *p) != '\0'; p++)
 		if (isascii(c) && isupper(c))
 			*p = tolower(c);
+	return orig;
+}
+
+
+#if 0
+makelower: Optimization for EAI case?
+
+	unsigned char ch;
+
+	while ((ch = (unsigned char)*str) != '\0' && ch < 127)
+	{
+		if (isascii(c) && isupper(c))
+			*str = tolower(ch);
+		++str;
+	}
+	if ('\0' == ch)
+		return orig;
+	handle UTF-8 case: invoke sm_lowercase() etc
+#endif /* 0 */
+
+/*
+**  MAKELOWER_BUF -- Translate a line into lower case
+**
+**	Parameters:
+**		str -- string to translate. [A]
+**		buf -- where to place lower case version.
+**		buflen -- size of buf
+**
+**	Returns:
+**		nothing
+**
+**	Side Effects:
+**		String pointed to by str is translated to lower case if possible.
+**
+**	Note:
+**		if str is lower cased in place and str == buf (same pointer),
+**		then it is not explicitly copied.
+*/
+
+void
+makelower_buf(str, buf, buflen)
+	char *str;
+	char *buf;
+	int buflen;
+{
+	char *lower;
+
+	SM_REQUIRE(buf != NULL);
+	if (str == NULL)
+		return;
+	lower = makelower_a(&str, NULL);
+	if (lower != str || str != buf)
+	{
+		sm_strlcpy(buf, lower, buflen);
+		if (lower != str)
+			SM_FREE(lower);
+	}
+	return;
 }
 
 /*
 **  FIXCRLF -- fix  in line.
 **
+**	XXX: Could this be a problem for EAI? That is, can there
+**		be a string with \n and the previous octet is \n
+**		but is part of a UTF8 "char"?
+**
 **	Looks for the  combination and turns it into the
 **	UNIX canonical  character.  It only takes one line,
 **	i.e., it is assumed that the first  found is the end
 **	of the line.
 **
 **	Parameters:
-**		line -- the line to fix.
+**		line -- the line to fix. [A]
 **		stripnl -- if true, strip the newline also.
 **
 **	Returns:
@@ -986,7 +1130,7 @@ fixcrlf(line, stripnl)
 **	as appropriate) at the end of the string.
 **
 **	Parameters:
-**		l -- line to put.
+**		l -- line to put. (should be [x])
 **		mci -- the mailer connection information.
 **
 **	Returns:
@@ -1011,7 +1155,7 @@ putline(l, mci)
 **	as appropriate) at the end of the string.
 **
 **	Parameters:
-**		l -- line to put.
+**		l -- line to put. (should be [x])
 **		len -- the length of the line.
 **		mci -- the mailer connection information.
 **		pxflags -- flag bits:
@@ -1264,6 +1408,7 @@ xunlink(f)
 **
 **	Parameters:
 **		buf -- place to put the input line.
+**			(can be [A], but should be [x])
 **		siz -- size of buf.
 **		fp -- file to read from.
 **		timeout -- the timeout before error occurs.
@@ -1372,6 +1517,7 @@ sfgets(buf, siz, fp, timeout, during)
 **
 **	Parameters:
 **		buf -- place to put result.
+**			(can be [A], but should be [x])
 **		np -- pointer to bytes available; will be updated with
 **			the actual buffer size (not number of bytes filled)
 **			on return.
@@ -1500,7 +1646,7 @@ bool
 atobool(s)
 	register char *s;
 {
-	if (s == NULL || *s == '\0' || strchr("tTyY", *s) != NULL)
+	if (SM_IS_EMPTY(s) || strchr("tTyY", *s) != NULL)
 		return true;
 	return false;
 }
@@ -1583,8 +1729,9 @@ bitzerop(map)
 **
 **	Parameters:
 **		icase -- ignore case?
-**		a -- possible substring.
-**		b -- possible superstring.
+**		a -- possible substring. [A]
+**		b -- possible superstring. [A]
+**		(both must be the same format: X or Q)
 **
 **	Returns:
 **		true if a is contained in b (case insensitive).
@@ -1961,6 +2108,8 @@ dumpfd(fd, printclosed, logit)
 **
 **	Parameters:
 **		host -- the host to shorten (stripped in place).
+**		[EAI: matched against $m: must be same format;
+**		conversion needed?]
 **
 **	Returns:
 **		place where string was truncated, NULL if not truncated.
@@ -2192,7 +2341,7 @@ prog_open(argv, pfd, e)
 **  GET_COLUMN -- look up a Column in a line buffer
 **
 **	Parameters:
-**		line -- the raw text line to search.
+**		line -- the raw text line to search. [A]
 **		col -- the column number to fetch.
 **		delim -- the delimiter between columns.  If null,
 **			use white space.
@@ -2264,10 +2413,11 @@ get_column(line, col, delim, buf, buflen)
 
 /*
 **  CLEANSTRCPY -- copy string keeping out bogus characters
+**	XXX: This may be a problem for EAI?
 **
 **	Parameters:
 **		t -- "to" string.
-**		f -- "from" string.
+**		f -- "from" string. [A]
 **		l -- length of space available in "to" string.
 **
 **	Returns:
@@ -2304,7 +2454,7 @@ cleanstrcpy(t, f, l)
 **  DENLSTRING -- convert newlines in a string to spaces
 **
 **	Parameters:
-**		s -- the input string
+**		s -- the input string [A]
 **		strict -- if set, don't permit continuation lines.
 **		logattacks -- if set, log attempted attacks.
 **
@@ -2361,8 +2511,8 @@ denlstring(s, strict, logattacks)
 **  STRREPLNONPRT -- replace "unprintable" characters in a string with subst
 **
 **	Parameters:
-**		s -- string to manipulate (in place)
-**		subst -- character to use as replacement
+**		s -- string to manipulate (in place) [A]
+**		c -- character to use as replacement
 **
 **	Returns:
 **		true iff string did not contain "unprintable" characters
@@ -2398,7 +2548,7 @@ strreplnonprt(s, c)
 **	support.
 **
 **	Parameters:
-**		pathname -- pathname to check for directory-ness.
+**		pathname -- pathname to check for directory-ness. [x]
 **		createflag -- if set, create directory if needed.
 **
 **	Returns:
@@ -2984,8 +3134,8 @@ xconnect(inchannel)
 	if (i == 0)
 		return D_XCNCT;
 	delim = *p;
-	if (i > MAXNAME)
-		b[MAXNAME] = '\0';
+	if (i > MAXNAME)	/* EAI:ok */
+		b[MAXNAME] = '\0';	/* EAI:ok */
 	else
 		b[i] = '\0';
 	SM_FREE(RealHostName);
diff --git a/contrib/sendmail/src/version.c b/contrib/sendmail/src/version.c
index 1cb5140b0521..8619c58ee355 100644
--- a/contrib/sendmail/src/version.c
+++ b/contrib/sendmail/src/version.c
@@ -15,4 +15,4 @@
 
 SM_RCSID("@(#)$Id: version.c,v 8.250 2014-01-27 12:55:16 ca Exp $")
 
-char	Version[] = "8.16.1";
+char	Version[] = "8.17.1";
diff --git a/contrib/sendmail/test/t_dropgid.c b/contrib/sendmail/test/t_dropgid.c
index 28fdfeed90f5..7f583c6619ce 100644
--- a/contrib/sendmail/test/t_dropgid.c
+++ b/contrib/sendmail/test/t_dropgid.c
@@ -20,10 +20,11 @@
 #include 
 #include 
 #include 
+#include 
 
 #ifndef lint
 static char id[] = "@(#)$Id: t_dropgid.c,v 1.7 2013-11-22 20:52:01 ca Exp $";
-#endif /* ! lint */
+#endif
 
 static void
 printgids(str, r, e)
@@ -37,17 +38,17 @@ printgids(str, r, e)
 /* define only one of these */
 #if HASSETEGID
 # define SETGIDCALL	"setegid"
-#endif /* HASSETEGID */
+#endif
 #if HASSETREGID
 # define SETGIDCALL	"setregid"
-#endif /* HASSETREGID */
+#endif
 #if HASSETRESGID
 # define SETGIDCALL	"setresgid"
-#endif /* HASSETRESGID */
+#endif
 
 #ifndef SETGIDCALL
 #  define SETGIDCALL	"setgid"
-#endif /* ! SETGIDCALL */
+#endif
 
 int
 main(argc, argv)
@@ -139,13 +140,13 @@ main(argc, argv)
 		       SETGIDCALL);
 #if !HASSETEGID
 		printf("Maybe compile with -DHASSETEGID and try again\n");
-#endif /* !HASSETEGID */
+#endif
 #if !HASSETREGID
 		printf("Maybe compile with -DHASSETREGID and try again\n");
-#endif /* !HASSETREGID */
+#endif
 #if !HASSETRESGID
 		printf("Maybe compile with -DHASSETRESGID and try again\n");
-#endif /* !HASSETRESGID */
+#endif
 		exit(1);
 	}
 
diff --git a/contrib/sendmail/test/t_exclopen.c b/contrib/sendmail/test/t_exclopen.c
index 978508fa8b57..57fdd2a80cad 100644
--- a/contrib/sendmail/test/t_exclopen.c
+++ b/contrib/sendmail/test/t_exclopen.c
@@ -40,14 +40,17 @@
 
 #include 
 #include 
+#include 
+#include 
 #include 
 #include 
 #include 
+#include 
 #include 
 
 #ifndef lint
 static char id[] = "@(#)$Id: t_exclopen.c,v 8.7 2013-11-22 20:52:01 ca Exp $";
-#endif /* ! lint */
+#endif
 
 static char Attacker[128];
 static char Attackee[128];
diff --git a/contrib/sendmail/test/t_pathconf.c b/contrib/sendmail/test/t_pathconf.c
index f03875391acc..72dd7e4ed5f1 100644
--- a/contrib/sendmail/test/t_pathconf.c
+++ b/contrib/sendmail/test/t_pathconf.c
@@ -19,15 +19,17 @@
 #include 
 #include 
 #include 
+#include 
 #include 
+#include 
 #ifdef EX_OK
 # undef EX_OK		/* unistd.h may have another use for this */
-#endif /* EX_OK */
+#endif
 #include 
 
 #ifndef lint
 static char id[] = "@(#)$Id: t_pathconf.c,v 8.7 2013-11-22 20:52:01 ca Exp $";
-#endif /* ! lint */
+#endif
 
 int
 main(argc, argv)
diff --git a/contrib/sendmail/test/t_seteuid.c b/contrib/sendmail/test/t_seteuid.c
index be4ab17fe8cb..06b02ef7f97b 100644
--- a/contrib/sendmail/test/t_seteuid.c
+++ b/contrib/sendmail/test/t_seteuid.c
@@ -24,14 +24,15 @@
 #include 
 #include 
 #include 
+#include 
 
 #ifndef lint
 static char id[] = "@(#)$Id: t_seteuid.c,v 8.9 2013-11-22 20:52:01 ca Exp $";
-#endif /* ! lint */
+#endif
 
 #ifdef __hpux
 # define seteuid(e)	setresuid(-1, e, -1)
-#endif /* __hpux */
+#endif
 
 static void
 printuids(str, r, e)
diff --git a/contrib/sendmail/test/t_setgid.c b/contrib/sendmail/test/t_setgid.c
index 642907440712..ff704a38e3c5 100644
--- a/contrib/sendmail/test/t_setgid.c
+++ b/contrib/sendmail/test/t_setgid.c
@@ -20,10 +20,11 @@
 #include 
 #include 
 #include 
+#include 
 
 #ifndef lint
 static char id[] = "@(#)$Id: t_setgid.c,v 1.7 2013-11-22 20:52:01 ca Exp $";
-#endif /* ! lint */
+#endif
 
 static void
 printgids(str, r, e)
@@ -54,17 +55,17 @@ main(argc, argv)
 
 #if SM_CONF_SETREGID
 	res = setregid(effgid, effgid);
-#else /* SM_CONF_SETREGID */
+#else
 	res = setgid(effgid);
-#endif /* SM_CONF_SETREGID */
+#endif
 
 	printf("setgid(%d)=%d %s\n", (int) effgid, res,
 		res < 0 ? "failure" : "ok");
 #if SM_CONF_SETREGID
 	printgids("after setregid()", effgid, effgid);
-#else /* SM_CONF_SETREGID */
+#else
 	printgids("after setgid()", effgid, effgid);
-#endif /* SM_CONF_SETREGID */
+#endif
 
 	if (getegid() != effgid)
 	{
@@ -107,9 +108,9 @@ main(argc, argv)
 		printf("\nThis system cannot use %s to set the real gid to the effective gid\nand clear the saved gid.\n",
 #if SM_CONF_SETREGID
 			"setregid"
-#else /* SM_CONF_SETREGID */
+#else
 			"setgid"
-#endif /* SM_CONF_SETREGID */
+#endif
 			);
 		exit(1);
 	}
diff --git a/contrib/sendmail/test/t_setreuid.c b/contrib/sendmail/test/t_setreuid.c
index 46fcb5f3ee97..fab01e65841c 100644
--- a/contrib/sendmail/test/t_setreuid.c
+++ b/contrib/sendmail/test/t_setreuid.c
@@ -21,14 +21,15 @@
 #include 
 #include 
 #include 
+#include 
 
 #ifndef lint
 static char id[] = "@(#)$Id: t_setreuid.c,v 8.10 2013-11-22 20:52:01 ca Exp $";
-#endif /* ! lint */
+#endif
 
 #ifdef __hpux
 # define setreuid(r, e)	setresuid(r, e, -1)
-#endif /* __hpux */
+#endif
 
 static void
 printuids(str, r, e)
diff --git a/contrib/sendmail/test/t_setuid.c b/contrib/sendmail/test/t_setuid.c
index 627db2d0d0d0..672739a14c9d 100644
--- a/contrib/sendmail/test/t_setuid.c
+++ b/contrib/sendmail/test/t_setuid.c
@@ -23,10 +23,11 @@
 #include 
 #include 
 #include 
+#include 
 
 #ifndef lint
 static char id[] = "@(#)$Id: t_setuid.c,v 8.8 2013-11-22 20:52:01 ca Exp $";
-#endif /* ! lint */
+#endif
 
 static void
 printuids(str, r, e)
diff --git a/contrib/sendmail/test/t_snprintf.c b/contrib/sendmail/test/t_snprintf.c
index f690f611a162..bcf7f038a3ad 100644
--- a/contrib/sendmail/test/t_snprintf.c
+++ b/contrib/sendmail/test/t_snprintf.c
@@ -13,7 +13,7 @@
 
 #ifndef lint
 static char id[] = "@(#)$Id: t_snprintf.c,v 8.5 2013-11-22 20:52:01 ca Exp $";
-#endif /* ! lint */
+#endif
 
 #define TEST_STRING	"1234567890"
 
diff --git a/contrib/sendmail/vacation/vacation.1 b/contrib/sendmail/vacation/vacation.1
index 237368c9840c..158fab1a7366 100644
--- a/contrib/sendmail/vacation/vacation.1
+++ b/contrib/sendmail/vacation/vacation.1
@@ -30,9 +30,9 @@ vacation
 .RB [ \-l ]
 .RB [ \-m
 .IR message ]
-.RB [ \-R 
+.RB [ \-R
 .IR returnaddr ]
-.RB [ \-r 
+.RB [ \-r
 .IR interval ]
 .RB [ \-s
 .IR address ]
@@ -168,7 +168,7 @@ above).
 Use
 .I address
 instead of the incoming message sender address on the
-.I From 
+.I From
 line as the recipient for the vacation message.
 .TP
 .BI \-t " time"
@@ -176,7 +176,7 @@ Ignored, available only for compatibility with Sun's
 vacation program.
 .TP
 .B \-U
-Do not attempt to lookup 
+Do not attempt to lookup
 .I login
 in the password file.
 The -f and -m options must be used to specify the database and message file
@@ -264,7 +264,7 @@ Precedence: bulk
 I am on vacation until July 22.  If you have something urgent,
 please contact Keith Bostic .
 --eric
-.fi 
+.fi
 .SH FILES
 .TP 1.8i
 ~/.vacation.db
diff --git a/contrib/sendmail/vacation/vacation.c b/contrib/sendmail/vacation/vacation.c
index 72032748d624..4ddd1a0e9bc2 100644
--- a/contrib/sendmail/vacation/vacation.c
+++ b/contrib/sendmail/vacation/vacation.c
@@ -28,16 +28,14 @@ SM_IDSTR(id, "@(#)$Id: vacation.c,v 8.148 2013-11-22 20:52:02 ca Exp $")
 #include 
 #include 
 #include 
-#ifdef EX_OK
-# undef EX_OK		/* unistd.h may have another use for this */
-#endif
+#include 
 #include 
 
 #include 
 #include 
-#include "sendmail/sendmail.h"
+#include 
 #include 
-#include "libsmdb/smdb.h"
+#include 
 
 #define ONLY_ONCE	((time_t) 0)	/* send at most one reply */
 #define INTERVAL_UNDEF	((time_t) (-1))	/* no value given */
@@ -176,11 +174,11 @@ main(argc, argv)
 			    "Unknown UID %d", (int) RealUid);
 	RunAsUserName = RealUserName = rnamebuf;
 
-# ifdef LOG_MAIL
+#ifdef LOG_MAIL
 	openlog("vacation", LOG_PID, LOG_MAIL);
-# else
+#else
 	openlog("vacation", LOG_PID);
-# endif
+#endif
 
 	opterr = 0;
 	initdb = false;
@@ -650,8 +648,7 @@ nsearch(name, str)
 		**  matching "eric" to "".
 		*/
 
-		if (tolower(*s) == tolower(*name) &&
-		    strncasecmp(name, s, len) == 0 &&
+		if (SM_STRNCASEEQ(name, s, len) &&
 		    (s == str || !isascii(*(s - 1)) || !isalnum(*(s - 1))) &&
 		    (!isascii(*(s + len)) || !isalnum(*(s + len))))
 			return true;