aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.builds/freebsd.yml23
-rw-r--r--.builds/openbsd.yml16
-rw-r--r--.github/workflows/alpine.yml4
-rw-r--r--.github/workflows/arch.yml4
-rw-r--r--.github/workflows/fedora-gcc10.yml15
-rw-r--r--.github/workflows/fedora.yml15
-rw-r--r--.github/workflows/macos-latest.yml23
-rw-r--r--.github/workflows/ubuntu-gcc10.yml15
-rw-r--r--.github/workflows/ubuntu.yml4
-rw-r--r--CHANGES.md14
-rw-r--r--README.md25
-rw-r--r--ci/docker/Dockerfile.alpine13
-rw-r--r--ci/docker/Dockerfile.archlinux1
-rw-r--r--ci/docker/Dockerfile.fedora51
-rw-r--r--ci/docker/Dockerfile.fedora-gcc1054
-rw-r--r--ci/docker/Dockerfile.ubuntu1
-rw-r--r--ci/docker/Dockerfile.ubuntu-gcc1054
-rw-r--r--configure.ac103
-rw-r--r--contrib/CVS/Entries4
-rw-r--r--contrib/CVS/Repository1
-rw-r--r--contrib/CVS/Root1
-rw-r--r--contrib/libexec/CVS/Entries3
-rw-r--r--contrib/libexec/CVS/Repository1
-rw-r--r--contrib/libexec/CVS/Root1
-rw-r--r--contrib/libexec/Makefile.am2
-rw-r--r--contrib/libexec/encrypt/Makefile.am2
-rw-r--r--contrib/libexec/lockspool/Makefile.am20
-rw-r--r--contrib/libexec/lockspool/locking.c181
-rw-r--r--contrib/libexec/lockspool/lockspool.177
-rw-r--r--contrib/libexec/lockspool/lockspool.c124
-rw-r--r--contrib/libexec/lockspool/mail.local.h42
-rw-r--r--contrib/libexec/lockspool/pathnames.h38
-rw-r--r--contrib/libexec/mail.local/CVS/Entries7
-rw-r--r--contrib/libexec/mail.local/CVS/Repository1
-rw-r--r--contrib/libexec/mail.local/CVS/Root1
-rw-r--r--contrib/libexec/mail.local/Makefile.am4
-rw-r--r--contrib/libexec/mail.local/locking.c47
-rw-r--r--contrib/libexec/mail.local/mail.local.826
-rw-r--r--contrib/libexec/mail.local/mail.local.c233
-rw-r--r--contrib/libexec/mail.local/mail.local.h10
-rw-r--r--contrib/libexec/mail.local/pathnames.h2
-rw-r--r--mk/mail/mail.lmtp/Makefile.am4
-rw-r--r--mk/mail/mail.maildir/Makefile.am4
-rw-r--r--mk/mail/mail.mboxfile/Makefile.am4
-rw-r--r--mk/mail/mail.mda/Makefile.am4
-rw-r--r--mk/pathnames5
-rw-r--r--mk/smtp/Makefile.am18
-rw-r--r--mk/smtpctl/Makefile.am61
-rw-r--r--mk/smtpd/Makefile.am195
-rw-r--r--openbsd-compat/Makefile.am41
-rw-r--r--openbsd-compat/defines.h18
-rw-r--r--openbsd-compat/err_h/err.h3
-rw-r--r--openbsd-compat/explicit_bzero.c2
-rw-r--r--openbsd-compat/fgetln.c2
-rw-r--r--openbsd-compat/freezero.c1
-rw-r--r--openbsd-compat/libasr/asr.c869
-rw-r--r--openbsd-compat/libasr/asr.h95
-rw-r--r--openbsd-compat/libasr/asr_compat.c102
-rw-r--r--openbsd-compat/libasr/asr_compat.h80
-rw-r--r--openbsd-compat/libasr/asr_debug.c362
-rw-r--r--openbsd-compat/libasr/asr_private.h359
-rw-r--r--openbsd-compat/libasr/asr_run.3316
-rw-r--r--openbsd-compat/libasr/asr_utils.c574
-rw-r--r--openbsd-compat/libasr/getaddrinfo.c55
-rw-r--r--openbsd-compat/libasr/getaddrinfo_async.c756
-rw-r--r--openbsd-compat/libasr/gethostnamadr.c200
-rw-r--r--openbsd-compat/libasr/gethostnamadr_async.c676
-rw-r--r--openbsd-compat/libasr/getnameinfo.c205
-rw-r--r--openbsd-compat/libasr/getnameinfo_async.c300
-rw-r--r--openbsd-compat/libasr/getnetnamadr.c134
-rw-r--r--openbsd-compat/libasr/getnetnamadr_async.c52
-rw-r--r--openbsd-compat/libasr/getrrsetbyname.c83
-rw-r--r--openbsd-compat/libasr/getrrsetbyname_async.c590
-rw-r--r--openbsd-compat/libasr/libasr.la41
-rw-r--r--openbsd-compat/libasr/res_debug.c2
-rw-r--r--openbsd-compat/libasr/res_init.c103
-rw-r--r--openbsd-compat/libasr/res_mkquery.c119
-rw-r--r--openbsd-compat/libasr/res_query.c112
-rw-r--r--openbsd-compat/libasr/res_search_async.c327
-rw-r--r--openbsd-compat/libasr/res_send.c61
-rw-r--r--openbsd-compat/libasr/res_send_async.c806
-rw-r--r--openbsd-compat/libasr/sethostent.c36
-rw-r--r--openbsd-compat/libasr/thread_private.h8
-rw-r--r--openbsd-compat/openbsd-compat.h14
-rw-r--r--openbsd-compat/pipe2.c46
-rw-r--r--openbsd-compat/res_hnok.c169
-rw-r--r--openbsd-compat/res_randomid.c13
-rw-r--r--openbsd-compat/setproctitle.c5
-rw-r--r--regress/config/Makefile17
-rw-r--r--regress/config/include.conf1
-rw-r--r--regress/config/test0.conf3
-rw-r--r--regress/config/test1.conf5
-rw-r--r--regress/config/test10.conf6
-rw-r--r--regress/config/test11.conf25
-rw-r--r--regress/config/test2.conf5
-rw-r--r--regress/config/test3.conf5
-rw-r--r--regress/config/test4.conf4
-rw-r--r--regress/config/test5.conf9
-rw-r--r--regress/config/test6.conf5
-rw-r--r--regress/config/test7.conf12
-rw-r--r--regress/config/test8.conf13
-rw-r--r--regress/config/test9.conf8
-rw-r--r--regress/expand/Makefile6
-rw-r--r--regress/expand/expand.c129
-rw-r--r--regress/expand/test07
-rw-r--r--regress/filters/Makefile9
-rwxr-xr-xregress/smtp/test.base56
-rw-r--r--regress/smtp/test.mailfrom110
-rw-r--r--regress/smtp/test.rcptto112
-rwxr-xr-xregress/smtp/test.smtp089
-rwxr-xr-xregress/smtp/test.smtp188
-rwxr-xr-xregress/smtp/test.smtp250
-rwxr-xr-xregress/smtp/test.smtp4155
-rw-r--r--regress/smtp/test.tls10
-rw-r--r--smtpd/lka_proc.c190
-rw-r--r--smtpd/lka_report.c511
-rw-r--r--smtpd/smtpctl/CVS/Entries2
-rw-r--r--smtpd/smtpctl/CVS/Repository1
-rw-r--r--smtpd/smtpctl/CVS/Root1
-rw-r--r--smtpd/smtpd/CVS/Repository1
-rw-r--r--smtpd/smtpd/CVS/Root1
-rw-r--r--tests/certificate_test/smtpd.conf13
-rwxr-xr-xtests/certificate_test/test.sh20
-rwxr-xr-xtests/test_all.sh6
-rw-r--r--tests/test_email.txt13
-rw-r--r--usr.sbin/smtpd/Makefile (renamed from smtpd/Makefile)0
-rw-r--r--usr.sbin/smtpd/aliases.5 (renamed from smtpd/aliases.5)6
-rw-r--r--usr.sbin/smtpd/aliases.c (renamed from smtpd/aliases.c)6
-rw-r--r--usr.sbin/smtpd/bounce.c (renamed from smtpd/bounce.c)104
-rw-r--r--usr.sbin/smtpd/ca.c (renamed from smtpd/ca.c)188
-rw-r--r--usr.sbin/smtpd/cert.c (renamed from smtpd/cert.c)0
-rw-r--r--usr.sbin/smtpd/compress_backend.c (renamed from smtpd/compress_backend.c)0
-rw-r--r--usr.sbin/smtpd/compress_gzip.c (renamed from smtpd/compress_gzip.c)0
-rw-r--r--usr.sbin/smtpd/config.c (renamed from smtpd/config.c)10
-rw-r--r--usr.sbin/smtpd/control.c (renamed from smtpd/control.c)2
-rw-r--r--usr.sbin/smtpd/crypto.c (renamed from smtpd/crypto.c)0
-rw-r--r--usr.sbin/smtpd/dict.c (renamed from smtpd/dict.c)0
-rw-r--r--usr.sbin/smtpd/dict.h (renamed from smtpd/dict.h)0
-rw-r--r--usr.sbin/smtpd/dns.c (renamed from smtpd/dns.c)0
-rw-r--r--usr.sbin/smtpd/enqueue.c (renamed from smtpd/enqueue.c)106
-rw-r--r--usr.sbin/smtpd/envelope.c (renamed from smtpd/envelope.c)12
-rw-r--r--usr.sbin/smtpd/esc.c (renamed from smtpd/esc.c)0
-rw-r--r--usr.sbin/smtpd/expand.c (renamed from smtpd/expand.c)0
-rw-r--r--usr.sbin/smtpd/filter.c (renamed from smtpd/filter.c)0
-rw-r--r--usr.sbin/smtpd/forward.5 (renamed from smtpd/forward.5)0
-rw-r--r--usr.sbin/smtpd/forward.c (renamed from smtpd/forward.c)0
-rw-r--r--usr.sbin/smtpd/iobuf.c (renamed from smtpd/iobuf.c)7
-rw-r--r--usr.sbin/smtpd/iobuf.h (renamed from smtpd/iobuf.h)0
-rw-r--r--usr.sbin/smtpd/ioev.c (renamed from smtpd/ioev.c)0
-rw-r--r--usr.sbin/smtpd/ioev.h (renamed from smtpd/ioev.h)0
-rw-r--r--usr.sbin/smtpd/libressl.c (renamed from smtpd/libressl.c)0
-rw-r--r--usr.sbin/smtpd/limit.c (renamed from smtpd/limit.c)0
-rw-r--r--usr.sbin/smtpd/lka.c (renamed from smtpd/lka.c)19
-rw-r--r--usr.sbin/smtpd/lka_filter.c (renamed from smtpd/lka_filter.c)760
-rw-r--r--usr.sbin/smtpd/lka_session.c (renamed from smtpd/lka_session.c)0
-rw-r--r--usr.sbin/smtpd/log.c (renamed from smtpd/log.c)0
-rw-r--r--usr.sbin/smtpd/log.h (renamed from smtpd/log.h)0
-rw-r--r--usr.sbin/smtpd/mail.lmtp.8 (renamed from smtpd/mail.lmtp.8)0
-rw-r--r--usr.sbin/smtpd/mail.lmtp.c (renamed from smtpd/mail.lmtp.c)84
-rw-r--r--usr.sbin/smtpd/mail.maildir.8 (renamed from smtpd/mail.maildir.8)0
-rw-r--r--usr.sbin/smtpd/mail.maildir.c (renamed from smtpd/mail.maildir.c)0
-rw-r--r--usr.sbin/smtpd/mail.mboxfile.8 (renamed from smtpd/mail.mboxfile.8)0
-rw-r--r--usr.sbin/smtpd/mail.mboxfile.c (renamed from smtpd/mail.mboxfile.c)2
-rw-r--r--usr.sbin/smtpd/mail.mda.8 (renamed from smtpd/mail.mda.8)0
-rw-r--r--usr.sbin/smtpd/mail.mda.c (renamed from smtpd/mail.mda.c)0
-rw-r--r--usr.sbin/smtpd/mail/Makefile (renamed from smtpd/mail/Makefile)0
-rw-r--r--usr.sbin/smtpd/mailaddr.c (renamed from smtpd/mailaddr.c)14
-rw-r--r--usr.sbin/smtpd/makemap.8 (renamed from smtpd/makemap.8)0
-rw-r--r--usr.sbin/smtpd/makemap.c (renamed from smtpd/makemap.c)14
-rw-r--r--usr.sbin/smtpd/mda.c (renamed from smtpd/mda.c)0
-rw-r--r--usr.sbin/smtpd/mda_mbox.c94
-rw-r--r--usr.sbin/smtpd/mda_unpriv.c (renamed from smtpd/mda_unpriv.c)10
-rw-r--r--usr.sbin/smtpd/mda_variables.c (renamed from smtpd/mda_variables.c)0
-rw-r--r--usr.sbin/smtpd/mproc.c (renamed from smtpd/mproc.c)7
-rw-r--r--usr.sbin/smtpd/mta.c (renamed from smtpd/mta.c)7
-rw-r--r--usr.sbin/smtpd/mta_session.c (renamed from smtpd/mta_session.c)424
-rw-r--r--usr.sbin/smtpd/newaliases.8 (renamed from smtpd/newaliases.8)0
-rw-r--r--usr.sbin/smtpd/parse.y (renamed from smtpd/parse.y)326
-rw-r--r--usr.sbin/smtpd/parser.c (renamed from smtpd/parser.c)7
-rw-r--r--usr.sbin/smtpd/parser.h (renamed from smtpd/parser.h)0
-rw-r--r--usr.sbin/smtpd/pony.c (renamed from smtpd/pony.c)0
-rw-r--r--usr.sbin/smtpd/proxy.c (renamed from smtpd/proxy.c)0
-rw-r--r--usr.sbin/smtpd/queue.c (renamed from smtpd/queue.c)11
-rw-r--r--usr.sbin/smtpd/queue_backend.c (renamed from smtpd/queue_backend.c)5
-rw-r--r--usr.sbin/smtpd/queue_fs.c (renamed from smtpd/queue_fs.c)5
-rw-r--r--usr.sbin/smtpd/queue_null.c (renamed from smtpd/queue_null.c)0
-rw-r--r--usr.sbin/smtpd/queue_proc.c (renamed from smtpd/queue_proc.c)0
-rw-r--r--usr.sbin/smtpd/queue_ram.c (renamed from smtpd/queue_ram.c)0
-rw-r--r--usr.sbin/smtpd/report_smtp.c (renamed from smtpd/report_smtp.c)24
-rw-r--r--usr.sbin/smtpd/resolver.c (renamed from smtpd/resolver.c)0
-rw-r--r--usr.sbin/smtpd/rfc5322.c (renamed from smtpd/rfc5322.c)0
-rw-r--r--usr.sbin/smtpd/rfc5322.h (renamed from smtpd/rfc5322.h)0
-rw-r--r--usr.sbin/smtpd/ruleset.c (renamed from smtpd/ruleset.c)18
-rw-r--r--usr.sbin/smtpd/runq.c (renamed from smtpd/runq.c)0
-rw-r--r--usr.sbin/smtpd/scheduler.c (renamed from smtpd/scheduler.c)0
-rw-r--r--usr.sbin/smtpd/scheduler_backend.c (renamed from smtpd/scheduler_backend.c)0
-rw-r--r--usr.sbin/smtpd/scheduler_null.c (renamed from smtpd/scheduler_null.c)0
-rw-r--r--usr.sbin/smtpd/scheduler_proc.c (renamed from smtpd/scheduler_proc.c)0
-rw-r--r--usr.sbin/smtpd/scheduler_ramqueue.c (renamed from smtpd/scheduler_ramqueue.c)0
-rw-r--r--usr.sbin/smtpd/sendmail.8 (renamed from smtpd/sendmail.8)0
-rw-r--r--usr.sbin/smtpd/smtp.1 (renamed from smtpd/smtp.1)0
-rw-r--r--usr.sbin/smtpd/smtp.c (renamed from smtpd/smtp.c)0
-rw-r--r--usr.sbin/smtpd/smtp.h (renamed from smtpd/smtp.h)0
-rw-r--r--usr.sbin/smtpd/smtp/Makefile (renamed from smtpd/smtp/Makefile)0
-rw-r--r--usr.sbin/smtpd/smtp_client.c (renamed from smtpd/smtp_client.c)13
-rw-r--r--usr.sbin/smtpd/smtp_session.c (renamed from smtpd/smtp_session.c)332
-rw-r--r--usr.sbin/smtpd/smtpc.c (renamed from smtpd/smtpc.c)4
-rw-r--r--usr.sbin/smtpd/smtpctl.8 (renamed from smtpd/smtpctl.8)11
-rw-r--r--usr.sbin/smtpd/smtpctl.c (renamed from smtpd/smtpctl.c)15
-rw-r--r--usr.sbin/smtpd/smtpctl/Makefile (renamed from smtpd/smtpctl/Makefile)0
-rw-r--r--usr.sbin/smtpd/smtpd-api.h (renamed from smtpd/smtpd-api.h)0
-rw-r--r--usr.sbin/smtpd/smtpd-defines.h (renamed from smtpd/smtpd-defines.h)10
-rw-r--r--usr.sbin/smtpd/smtpd-filters.7653
-rw-r--r--usr.sbin/smtpd/smtpd.8 (renamed from smtpd/smtpd.8)0
-rw-r--r--usr.sbin/smtpd/smtpd.c (renamed from smtpd/smtpd.c)115
-rw-r--r--usr.sbin/smtpd/smtpd.conf (renamed from smtpd/smtpd.conf)2
-rw-r--r--usr.sbin/smtpd/smtpd.conf.5 (renamed from smtpd/smtpd.conf.5)278
-rw-r--r--usr.sbin/smtpd/smtpd.h (renamed from smtpd/smtpd.h)60
-rw-r--r--usr.sbin/smtpd/smtpd/Makefile (renamed from smtpd/smtpd/Makefile)5
-rw-r--r--usr.sbin/smtpd/spfwalk.c (renamed from smtpd/spfwalk.c)166
-rw-r--r--usr.sbin/smtpd/srs.c (renamed from smtpd/srs.c)0
-rw-r--r--usr.sbin/smtpd/ssl.c (renamed from smtpd/ssl.c)5
-rw-r--r--usr.sbin/smtpd/ssl.h (renamed from smtpd/ssl.h)0
-rw-r--r--usr.sbin/smtpd/ssl_smtpd.c (renamed from smtpd/ssl_smtpd.c)0
-rw-r--r--usr.sbin/smtpd/ssl_verify.c (renamed from smtpd/ssl_verify.c)0
-rw-r--r--usr.sbin/smtpd/stat_backend.c (renamed from smtpd/stat_backend.c)0
-rw-r--r--usr.sbin/smtpd/stat_ramstat.c (renamed from smtpd/stat_ramstat.c)0
-rw-r--r--usr.sbin/smtpd/table.5 (renamed from smtpd/table.5)0
-rw-r--r--usr.sbin/smtpd/table.c (renamed from smtpd/table.c)0
-rw-r--r--usr.sbin/smtpd/table_db.c (renamed from smtpd/table_db.c)0
-rw-r--r--usr.sbin/smtpd/table_getpwnam.c (renamed from smtpd/table_getpwnam.c)0
-rw-r--r--usr.sbin/smtpd/table_proc.c (renamed from smtpd/table_proc.c)0
-rw-r--r--usr.sbin/smtpd/table_static.c (renamed from smtpd/table_static.c)0
-rw-r--r--usr.sbin/smtpd/to.c (renamed from smtpd/to.c)0
-rw-r--r--usr.sbin/smtpd/tree.c (renamed from smtpd/tree.c)0
-rw-r--r--usr.sbin/smtpd/tree.h (renamed from smtpd/tree.h)0
-rw-r--r--usr.sbin/smtpd/unpack_dns.c (renamed from smtpd/unpack_dns.c)0
-rw-r--r--usr.sbin/smtpd/unpack_dns.h (renamed from smtpd/unpack_dns.h)0
-rw-r--r--usr.sbin/smtpd/util.c (renamed from smtpd/util.c)8
-rw-r--r--usr.sbin/smtpd/waitq.c (renamed from smtpd/waitq.c)0
240 files changed, 12271 insertions, 2671 deletions
diff --git a/.builds/freebsd.yml b/.builds/freebsd.yml
new file mode 100644
index 00000000..51993106
--- /dev/null
+++ b/.builds/freebsd.yml
@@ -0,0 +1,23 @@
+image: freebsd/latest
+packages:
+- autoconf
+- automake
+- bison
+- libevent
+- libtool
+- libressl
+- libasr
+- py37-ansible
+- db6
+- python3
+- python37
+- python
+sources:
+- https://github.com/OpenSMTPD/OpenSMTPD
+- https://github.com/OpenSMTPD/ci
+tasks:
+- ansible: |
+ ls -lah
+ BUILD_DIR=$(pwd)/OpenSMTPD
+ cd ci/ansible
+ ansible-playbook test.yml --inventory inventory/freebsd --skip-tags checkout --extra-vars "build_dir=$BUILD_DIR"
diff --git a/.builds/openbsd.yml b/.builds/openbsd.yml
new file mode 100644
index 00000000..97b9634c
--- /dev/null
+++ b/.builds/openbsd.yml
@@ -0,0 +1,16 @@
+image: openbsd/6.8
+packages:
+- autoconf-2.69p3
+- automake-1.14.1p1
+- libtool
+sources:
+- https://github.com/OpenSMTPD/OpenSMTPD
+tasks:
+- configure: |
+ cd OpenSMTPD
+ export AUTOCONF_VERSION=2.69
+ ./bootstrap
+ ./configure
+- build: |
+ cd OpenSMTPD
+ make
diff --git a/.github/workflows/alpine.yml b/.github/workflows/alpine.yml
index 6f2d29f6..23816eab 100644
--- a/.github/workflows/alpine.yml
+++ b/.github/workflows/alpine.yml
@@ -2,10 +2,10 @@ name: Alpine Linux (amd64 musl openssl)
on:
push:
branches:
- - portable
+ - master
pull_request:
branches:
- - portable
+ - master
jobs:
build:
runs-on: ubuntu-latest
diff --git a/.github/workflows/arch.yml b/.github/workflows/arch.yml
index a3528152..2ee226a2 100644
--- a/.github/workflows/arch.yml
+++ b/.github/workflows/arch.yml
@@ -2,10 +2,10 @@ name: Archlinux (amd64 glibc libressl)
on:
push:
branches:
- - portable
+ - master
pull_request:
branches:
- - portable
+ - master
jobs:
build:
runs-on: ubuntu-latest
diff --git a/.github/workflows/fedora-gcc10.yml b/.github/workflows/fedora-gcc10.yml
new file mode 100644
index 00000000..698d9d5b
--- /dev/null
+++ b/.github/workflows/fedora-gcc10.yml
@@ -0,0 +1,15 @@
+name: Fedora (amd64 glibc openssl gcc10)
+on:
+ push:
+ branches:
+ - master
+ pull_request:
+ branches:
+ - master
+jobs:
+ build:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v1
+ - name: Fedora (amd64 glibc openssl gcc10)
+ run: docker build . --file ci/docker/Dockerfile.fedora-gcc10 --tag opensmtpd:fedora-gcc10
diff --git a/.github/workflows/fedora.yml b/.github/workflows/fedora.yml
new file mode 100644
index 00000000..448a6eda
--- /dev/null
+++ b/.github/workflows/fedora.yml
@@ -0,0 +1,15 @@
+name: Fedora (amd64 glibc openssl)
+on:
+ push:
+ branches:
+ - master
+ pull_request:
+ branches:
+ - master
+jobs:
+ build:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v1
+ - name: Fedora (amd64 glibc openssl)
+ run: docker build . --file ci/docker/Dockerfile.fedora --tag opensmtpd:fedora
diff --git a/.github/workflows/macos-latest.yml b/.github/workflows/macos-latest.yml
new file mode 100644
index 00000000..1960a39a
--- /dev/null
+++ b/.github/workflows/macos-latest.yml
@@ -0,0 +1,23 @@
+name: macOS Catalina 10.15
+on:
+ push:
+ branches:
+ - master
+ pull_request:
+ branches:
+ - master
+jobs:
+ build:
+ runs-on: macos-latest
+ steps:
+ - uses: actions/checkout@v1
+ - name: macOS Catalina 10.15
+ run: |
+ brew install automake libevent openssl@1.1 bison
+ export PATH="/usr/local/opt/bison/bin:$PATH"
+ export LDFLAGS="-L/usr/local/opt/openssl@1.1/lib -L/usr/local/opt/bison/lib"
+ export CFLAGS=-I/usr/local/opt/openssl@1.1/include
+ ./bootstrap
+ ./configure --with-gnu-ld --sysconfdir=/etc/mail --with-auth-pam
+ make
+ sudo make install
diff --git a/.github/workflows/ubuntu-gcc10.yml b/.github/workflows/ubuntu-gcc10.yml
new file mode 100644
index 00000000..e593df39
--- /dev/null
+++ b/.github/workflows/ubuntu-gcc10.yml
@@ -0,0 +1,15 @@
+name: Ubuntu (amd64 glibc openssl gcc10)
+on:
+ push:
+ branches:
+ - master
+ pull_request:
+ branches:
+ - master
+jobs:
+ build:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v1
+ - name: Ubuntu (amd64 glibc openssl gcc10)
+ run: docker build . --file ci/docker/Dockerfile.ubuntu-gcc10 --tag opensmtpd:ubuntu-gcc10
diff --git a/.github/workflows/ubuntu.yml b/.github/workflows/ubuntu.yml
index c068c6d5..c4bfd437 100644
--- a/.github/workflows/ubuntu.yml
+++ b/.github/workflows/ubuntu.yml
@@ -2,10 +2,10 @@ name: Ubuntu (amd64 glibc openssl)
on:
push:
branches:
- - portable
+ - master
pull_request:
branches:
- - portable
+ - master
jobs:
build:
runs-on: ubuntu-latest
diff --git a/CHANGES.md b/CHANGES.md
index 20bccca9..db25a4db 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -1,3 +1,17 @@
+# Release 6.6.3p1 (2020-02-10)
+
+Following the 6.6.2p1 release, various improvements were done in OpenBSD -current to mitigate the risk of similar bugs.
+
+This release back-ports them to the portable version of OpenSMTPD.
+
+# Release 6.6.2p1 (2020-01-28)
+
+This is CRITICAL security bugfix for
+[CVE-2020-7247](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-7247)
+
+Read more details in
+[this blog post](https://poolp.org/posts/2020-01-30/opensmtpd-advisory-dissected/)
+
# Release 6.6.1p1 (2019-11-06)
## Changes in this release (since 6.6.0p1)
diff --git a/README.md b/README.md
index 9de67dc6..bc35c16b 100644
--- a/README.md
+++ b/README.md
@@ -1,10 +1,10 @@
# OpenSMTPD
-[![Version](https://img.shields.io/badge/Version-6.6.1p1-brihtgreen.svg)](https://github.com/OpenSMTPD/OpenSMTPD/releases/tag/6.6.1p1)
+[![Version](https://img.shields.io/badge/Version-6.7.1p1-brihtgreen.svg)](https://github.com/OpenSMTPD/OpenSMTPD/releases/tag/6.7.1p1)
[![Coverity Scan analysis](https://scan.coverity.com/projects/278/badge.svg)](https://scan.coverity.com/projects/opensmtpd-opensmtpd)
[![Packaging status](https://repology.org/badge/tiny-repos/opensmtpd.svg)](https://repology.org/project/opensmtpd/versions)
[![License: ISC](https://img.shields.io/badge/License-ISC-blue.svg)](https://www.isc.org/licenses/)
-[![Clang Analysis](https://opensmtpd.email/reports/clang/badge.svg)](https://opensmtpd.email/reports/clang/index.html)
+
OpenSMTPD is a FREE implementation of the server-side SMTP protocol as
@@ -29,9 +29,8 @@ mailing list: http://www.opensmtpd.org/list.html
and to join the IRC channel: #OpenSMTPD @ irc.freenode.net
-Also note that we have a wiki at
-https://github.com/OpenSMTPD/OpenSMTPD/wiki that you are encouraged to
-contribute to.
+The manual pages are available online at https://www.opensmtpd.org/manual.html,
+which you are encouraged to contribute to.
Cheers!
@@ -49,14 +48,16 @@ Portable OpenSMTPD relies on:
* libtool (http://www.gnu.org/software/libtool/)
* libressl (https://www.libressl.org/)
or OpenSSL (https://www.openssl.org/)
- * libasr (https://opensmtpd.org/archives/libasr-1.0.3.tar.gz)
-By default OpenSMTPD expects latest versions of all dependencies unless noted otherwise
+By default OpenSMTPD expects latest versions of all dependencies unless noted otherwise.
+
+Note that some distributions have different packages for a same library, you should always use the `-dev` or `-devel` package (for example, `libevent-dev` or `libevent-devel`) if you're going to build OpenSMTPD yourself.
+
## Get the source
- git clone -b portable git://github.com/OpenSMTPD/OpenSMTPD.git opensmtpd
+ git clone git://github.com/OpenSMTPD/OpenSMTPD.git opensmtpd
## Build
@@ -76,10 +77,12 @@ libasr directory:
./configure --with-libasr=/usr/local
-### Mac OS X:
+### MacOS:
- ./configure --with-libevent=/opt/local --with-libasr=/opt/local
+ ./configure --with-libevent=/opt/local
+Though MacOS includes a copy of bison in the bases system, you will
+need to install a more recent version from, e.g., MacPorts.
## Install
@@ -143,7 +146,7 @@ script allows overriding these using the options:
### NetBSD, Linux (Debian, Arch Linux, ...)
- mkdir /var/empty
+ mkdir /var/empty
useradd -c "SMTP Daemon" -d /var/empty -s /sbin/nologin _smtpd
useradd -c "SMTPD Queue" -d /var/empty -s /sbin/nologin _smtpq
diff --git a/ci/docker/Dockerfile.alpine b/ci/docker/Dockerfile.alpine
index 01069644..2c7c66fc 100644
--- a/ci/docker/Dockerfile.alpine
+++ b/ci/docker/Dockerfile.alpine
@@ -1,10 +1,4 @@
-FROM alpine:3.10 as build
-
-# Allow container to expose ports at runtime, if necessary
-# https://docs.docker.com/engine/reference/#expose
-EXPOSE 25
-EXPOSE 465
-EXPOSE 587
+FROM alpine:3.11 as build
# creates /opensmtpd dir and makes all following commands to run in it
# https://docs.docker.com/engine/reference/builder/#workdir
@@ -19,15 +13,14 @@ RUN apk add --no-cache \
fts-dev \
gcc \
fts \
- libasr-dev \
libevent-dev \
libtool \
libtool \
linux-pam-dev \
make \
musl-dev \
- openssl \
- openssl-dev \
+ libressl \
+ libressl-dev \
zlib-dev
# create users and directories
diff --git a/ci/docker/Dockerfile.archlinux b/ci/docker/Dockerfile.archlinux
index 73e0d24d..dcd46684 100644
--- a/ci/docker/Dockerfile.archlinux
+++ b/ci/docker/Dockerfile.archlinux
@@ -22,7 +22,6 @@ RUN pacman -Suy --noconfirm \
libtool \
bison \
gettext \
- libasr \
libevent \
libressl \
pam \
diff --git a/ci/docker/Dockerfile.fedora b/ci/docker/Dockerfile.fedora
new file mode 100644
index 00000000..d6c01bdf
--- /dev/null
+++ b/ci/docker/Dockerfile.fedora
@@ -0,0 +1,51 @@
+FROM fedora:latest
+
+# Allow container to expose ports at runtime, if necessary
+# https://docs.docker.com/engine/reference/#expose
+EXPOSE 25
+EXPOSE 465
+EXPOSE 587
+
+# creates /opensmtpd dir and makes all following commands to run in it
+# https://docs.docker.com/engine/reference/builder/#workdir
+WORKDIR /opensmtpd
+
+# install necessary packages
+RUN dnf -y install \
+ autoconf \
+ automake \
+ bison \
+ @development-tools \
+ libevent-devel \
+ openssl-devel \
+ libtool \
+ pam-devel \
+ zlib-devel
+
+# create users and directories
+RUN mkdir -p /var/lib/opensmtpd/empty \
+ && useradd _smtpd \
+ --home-dir /var/lib/opensmtpd/empty \
+ --no-create-home \
+ --shell /bin/false \
+ && useradd _smtpq \
+ --home-dir /var/lib/opensmtpd/empty \
+ --no-create-home \
+ --shell /bin/false \
+ && mkdir -p /var/spool/smtpd \
+ && mkdir -p /var/mail \
+ && mkdir -p /etc/mail \
+ && chmod 711 /var/spool/smtpd
+
+# Copy contentes of the repo inside the container
+# https://docs.docker.com/engine/reference/builder/#copy
+COPY . /opensmtpd
+
+RUN ./bootstrap \
+ && ./configure \
+ --with-gnu-ld \
+ --sysconfdir=/etc/mail \
+ --with-auth-pam \
+ && make \
+ && make install \
+ && cp etc/aliases /etc/mail/aliases
diff --git a/ci/docker/Dockerfile.fedora-gcc10 b/ci/docker/Dockerfile.fedora-gcc10
new file mode 100644
index 00000000..1ab3bcbd
--- /dev/null
+++ b/ci/docker/Dockerfile.fedora-gcc10
@@ -0,0 +1,54 @@
+FROM fedora:32
+
+# Allow container to expose ports at runtime, if necessary
+# https://docs.docker.com/engine/reference/#expose
+EXPOSE 25
+EXPOSE 465
+EXPOSE 587
+
+# creates /opensmtpd dir and makes all following commands to run in it
+# https://docs.docker.com/engine/reference/builder/#workdir
+WORKDIR /opensmtpd
+
+# install necessary packages
+RUN dnf -y install \
+ autoconf \
+ automake \
+ bison \
+ @development-tools \
+ libevent-devel \
+ openssl-devel \
+ libtool \
+ pam-devel \
+ zlib-devel \
+ libdb-devel
+
+# create users and directories
+RUN mkdir -p /var/lib/opensmtpd/empty \
+ && useradd _smtpd \
+ --home-dir /var/lib/opensmtpd/empty \
+ --no-create-home \
+ --shell /bin/false \
+ && useradd _smtpq \
+ --home-dir /var/lib/opensmtpd/empty \
+ --no-create-home \
+ --shell /bin/false \
+ && mkdir -p /var/spool/smtpd \
+ && mkdir -p /var/mail \
+ && mkdir -p /etc/mail \
+ && chmod 711 /var/spool/smtpd
+
+# Copy contentes of the repo inside the container
+# https://docs.docker.com/engine/reference/builder/#copy
+COPY . /opensmtpd
+
+RUN export CC=gcc-10 CXX=g++-10
+RUN ./bootstrap \
+ && ./configure \
+ --with-gnu-ld \
+ --sysconfdir=/etc/mail \
+ --with-auth-pam \
+ --with-table-db \
+ && make \
+ && make install \
+ && cp etc/aliases /etc/mail/aliases
diff --git a/ci/docker/Dockerfile.ubuntu b/ci/docker/Dockerfile.ubuntu
index a5301135..6626033d 100644
--- a/ci/docker/Dockerfile.ubuntu
+++ b/ci/docker/Dockerfile.ubuntu
@@ -17,7 +17,6 @@ RUN apt update \
automake \
bison \
build-essential \
- libasr-dev \
libevent-dev \
libssl-dev \
libtool \
diff --git a/ci/docker/Dockerfile.ubuntu-gcc10 b/ci/docker/Dockerfile.ubuntu-gcc10
new file mode 100644
index 00000000..2ebbdf58
--- /dev/null
+++ b/ci/docker/Dockerfile.ubuntu-gcc10
@@ -0,0 +1,54 @@
+FROM ubuntu:latest
+
+# Allow container to expose ports at runtime, if necessary
+# https://docs.docker.com/engine/reference/#expose
+EXPOSE 25
+EXPOSE 465
+EXPOSE 587
+
+# creates /opensmtpd dir and makes all following commands to run in it
+# https://docs.docker.com/engine/reference/builder/#workdir
+WORKDIR /opensmtpd
+
+# install necessary packages
+RUN apt update \
+ && apt install -y --no-install-recommends \
+ autoconf \
+ automake \
+ bison \
+ build-essential \
+ libevent-dev \
+ libssl-dev \
+ libtool \
+ libpam0g-dev \
+ zlib1g-dev \
+ gcc-10
+
+# create users and directories
+RUN mkdir -p /var/lib/opensmtpd/empty \
+ && useradd _smtpd \
+ --home-dir /var/lib/opensmtpd/empty \
+ --no-create-home \
+ --shell /bin/false \
+ && useradd _smtpq \
+ --home-dir /var/lib/opensmtpd/empty \
+ --no-create-home \
+ --shell /bin/false \
+ && mkdir -p /var/spool/smtpd \
+ && mkdir -p /var/mail \
+ && mkdir -p /etc/mail \
+ && chmod 711 /var/spool/smtpd
+
+# Copy contentes of the repo inside the container
+# https://docs.docker.com/engine/reference/builder/#copy
+COPY . /opensmtpd
+
+RUN export CC=gcc-10 CXX=g++-10
+RUN ./bootstrap \
+ && ./configure \
+ --with-gnu-ld \
+ --sysconfdir=/etc/mail \
+ --with-auth-pam \
+ && make \
+ && make install \
+ && cp etc/aliases /etc/mail/aliases
diff --git a/configure.ac b/configure.ac
index 354a80d0..d46aeae7 100644
--- a/configure.ac
+++ b/configure.ac
@@ -173,6 +173,16 @@ AC_CHECK_HEADER([fts.h],
#include <sys/stat.h>
])
+need_libasr=no
+AC_CHECK_HEADER([asr.h],
+ [],
+ [need_libasr=yes],
+ [
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netdb.h>
+])
+AM_CONDITIONAL([NEED_LIBASR], [test x"$need_libasr" = x"yes"])
#
# CHECKS FOR TYPES
@@ -466,6 +476,20 @@ AC_SEARCH_LIBS([fparseln],
[Define if you have the fparseln() function.])
])
+AC_SEARCH_LIBS([res_hnok],
+ [resolv],
+ [
+ AC_DEFINE([HAVE_RES_HNOK], [1],
+ [Define if you have the res_hnok() function.])
+ ])
+
+AC_SEARCH_LIBS([res_randomid],
+ [resolv],
+ [
+ AC_DEFINE([HAVE_RES_RANDOMID], [1],
+ [Define if you have the res_randomid() function.])
+ ])
+
AC_SEARCH_LIBS([res_9_b64_ntop],
[resolv],
[
@@ -526,7 +550,7 @@ AC_SEARCH_LIBS([event_asr_run],
[event],
[
AC_DEFINE([HAVE_EVENT_ASR_RUN], [1],
- [Define if you have the basename() function.])
+ [Define if you have the event_asr_run() function.])
])
AC_CHECK_FUNCS([ \
@@ -563,11 +587,13 @@ AC_CHECK_FUNCS([ \
memmove \
nanosleep \
nsleep \
+ pipe2 \
pidfile \
pledge \
reallocarray \
recallocarray \
res_hnok \
+ res_randomid \
setenv \
seteuid \
setegid \
@@ -864,7 +890,6 @@ fi
#l432 (customized)
# Check for some target-specific stuff
-ASR_LIB=-lasr
case "$host" in
*-*-darwin*)
@@ -894,6 +919,7 @@ main() { if (NSVersionOfRunTimeLibrary("System") >= (60 << 16))
supported by bsd-setproctitle.c])
AC_DEFINE([BROKEN_STRNVIS], [1],
[OSX strnvis argument order is swapped compared to OpenBSD])
+ BROKEN_STRNVIS=1
;;
*-*-dragonfly*)
;;
@@ -908,11 +934,13 @@ main() { if (NSVersionOfRunTimeLibrary("System") >= (60 << 16))
fi
AC_DEFINE([BROKEN_STRNVIS], [1],
[NetBSD strnvis argument order is swapped compared to OpenBSD])
+ BROKEN_STRNVIS=1
;;
*-*-freebsd*)
AC_DEFINE([BROKEN_GLOB], [1], [FreeBSD glob does not do what we need])
AC_DEFINE([BROKEN_STRNVIS], [1],
[FreeBSD strnvis argument order is swapped compared to OpenBSD])
+ BROKEN_STRNVIS=1
;;
*-*-openbsd*)
use_pie=auto
@@ -921,6 +949,7 @@ main() { if (NSVersionOfRunTimeLibrary("System") >= (60 << 16))
AC_DEFINE([BROKEN_STRNVIS], [0],
[FreeBSD strnvis argument order is swapped compared to OpenBSD])
+ BROKEN_STRNVIS=0
YACC='yacc'
ASR_LIB=
AC_DEFINE([NOOP_ASR_FREEADDRINFO], [0], [OpenBSD doesn't need ASR_FREEADDRINFO])
@@ -1615,61 +1644,6 @@ AC_EXEEXT
#l4757
-# Search for asr (based on zlib checks)
-dnl asr is required
-AC_ARG_WITH([libasr],
- [ --with-libasr=PATH Specify path to libasr installation],
- [ if test "x$withval" = "xno"; then
- AC_MSG_ERROR([*** asr is required ***])
- elif test "x$withval" != "xyes"; then
- if test -d "$withval/lib"; then
- if test -n "${need_dash_r}"; then
- LDFLAGS="-L${withval}/lib -R${withval}/lib ${LDFLAGS}"
- else
- LDFLAGS="-L${withval}/lib ${LDFLAGS}"
- fi
- else
- if test -n "${need_dash_r}"; then
- LDFLAGS="-L${withval} -R${withval} ${LDFLAGS}"
- else
- LDFLAGS="-L${withval} ${LDFLAGS}"
- fi
- fi
- if test -d "$withval/include"; then
- CPPFLAGS="-I${withval}/include ${CPPFLAGS}"
- else
- CPPFLAGS="-I${withval} ${CPPFLAGS}"
- fi
- fi ]
-)
-
-AC_CHECK_HEADER([asr.h], ,[AC_MSG_ERROR([*** asr.h missing - please install libasr ***])],
-[#include <sys/types.h>
-#include <sys/socket.h>
-#include <netinet/in.h>])
-AC_CHECK_LIB([asr], [asr_run], ,
- [
- saved_CPPFLAGS="$CPPFLAGS"
- saved_LDFLAGS="$LDFLAGS"
- save_LIBS="$LIBS"
- dnl Check default asr install dir
- if test -n "${need_dash_r}"; then
- LDFLAGS="-L/usr/local/lib -R/usr/local/lib ${saved_LDFLAGS}"
- else
- LDFLAGS="-L/usr/local/lib ${saved_LDFLAGS}"
- fi
- CPPFLAGS="-I/usr/local/include ${saved_CPPFLAGS}"
-# don't add -lasr to LIBS, it's only added when needed
-# LIBS="$LIBS -lasr"
- AC_TRY_LINK_FUNC([asr_run], ,
- [
- AC_MSG_ERROR([*** libasr missing - please install first or check config.log ***])
- ]
- )
- ]
-)
-
-
# Search for fts
AC_ARG_WITH([libfts],
[ --with-libfts=PATH Specify path to libfts installation (default: none, part of libc)],
@@ -2007,6 +1981,12 @@ AM_CONDITIONAL([HAVE_DB_API], [test "x$use_db_api" = "x1"])
AM_COND_IF([HAVE_DB_API], [AC_DEFINE([HAVE_DB_API], [1], [Define to 1 if HAVE_DB_API])])
+
+if test "$need_libasr" = "no" -a "x$ac_cv_search_event_asr_run" = "xno"; then
+ LIBS="$LIBS -lasr"
+fi
+
+
LIBS="$LIBS ${SMTPDLIBS}"
##end of chl
@@ -2035,6 +2015,7 @@ AM_CONDITIONAL([NEED_GETOPT], [test "x$ac_cv_func_getopt" != "xyes"])
AM_CONDITIONAL([NEED_GETPEEREID], [test "x$ac_cv_func_getpeereid" != "xyes"])
AM_CONDITIONAL([NEED_NANOSLEEP], [test "x$ac_cv_func_nanosleep" != "xyes"])
AM_CONDITIONAL([NEED_PIDFILE], [test "x$ac_cv_func_pidfile" != "xyes"])
+AM_CONDITIONAL([NEED_PIPE2], [test "x$ac_cv_func_pipe2" != "xyes"])
AM_CONDITIONAL([NEED_REALLOCARRAY], [test "x$ac_cv_func_reallocarray" != "xyes"])
AM_CONDITIONAL([NEED_RECALLOCARRAY], [test "x$ac_cv_func_recallocarray" != "xyes"])
AM_CONDITIONAL([NEED_SETPROCTITLE], [test "x$ac_cv_func_setproctitle" != "xyes"])
@@ -2055,6 +2036,9 @@ AM_CONDITIONAL([NEED_WAITPID], [test "x$ac_cv_func_waitpid" != "xyes"])
AM_CONDITIONAL([NEED_VIS], [test "x$ac_cv_func_strnvis" != "xyes" -o "x$BROKEN_STRNVIS" = "x1"])
AM_CONDITIONAL([NEED_USLEEP], [test "x$ac_cv_func_usleep" != "xyes"])
+AM_CONDITIONAL([NEED_RES_HNOK], [test "x$ac_cv_search_res_hnok" = "xno" -a x"$ac_cv_func_res_hnok" != "xyes" -a x"$need_libasr" = x"yes"])
+AM_CONDITIONAL([NEED_RES_RANDOMID], [test "x$ac_cv_search_res_randomid" = "xno" -a x"$ac_cv_func_res_randomid" != "xyes" -a x"$need_libasr" = x"yes"])
+
AM_CONDITIONAL([NEED_ARC4RANDOM], [test "x$ac_cv_func_arc4random" != "xyes" -a "x$ac_cv_have_decl_LIBRESSL_VERSION_NUMBER" != "xyes"])
AM_CONDITIONAL([NEED_SSL_CTX_USE_CERTIFICATE_CHAIN_MEM], [test "x$ac_cv_have_decl_LIBRESSL_VERSION_NUMBER" != "xyes"])
@@ -2062,6 +2046,10 @@ AM_CONDITIONAL([NEED_PROGNAME], [test "x$ac_cv_libc_defines___progname" != "xyes
##
+AM_COND_IF([NEED_PROGNAME], [AC_DEFINE([NEED_PROGNAME], [1], [Define to 1 if NEED_PROGNAME])])
+AM_COND_IF([NEED_SETPROCTITLE], [AC_DEFINE([NEED_SETPROCTITLE], [1], [Define to 1 if NEED_SETPROCTITLE])])
+
+
AC_CONFIG_FILES([Makefile
openbsd-compat/Makefile
mk/Makefile
@@ -2076,6 +2064,7 @@ AC_CONFIG_FILES([Makefile
contrib/Makefile
contrib/libexec/Makefile
contrib/libexec/mail.local/Makefile
+ contrib/libexec/lockspool/Makefile
contrib/libexec/encrypt/Makefile
])
diff --git a/contrib/CVS/Entries b/contrib/CVS/Entries
deleted file mode 100644
index 82a9d101..00000000
--- a/contrib/CVS/Entries
+++ /dev/null
@@ -1,4 +0,0 @@
-D/lib////
-D/libexec////
-/Makefile/-1.50/Mon Mar 5 11:15:41 2012//
-/Makefile.inc/-1.2/Sun Jan 28 19:34:26 2001//
diff --git a/contrib/CVS/Repository b/contrib/CVS/Repository
deleted file mode 100644
index 31ac4266..00000000
--- a/contrib/CVS/Repository
+++ /dev/null
@@ -1 +0,0 @@
-src/libexec
diff --git a/contrib/CVS/Root b/contrib/CVS/Root
deleted file mode 100644
index 7040dfb5..00000000
--- a/contrib/CVS/Root
+++ /dev/null
@@ -1 +0,0 @@
-anoncvs@anoncvs.spacehopper.org:/cvs
diff --git a/contrib/libexec/CVS/Entries b/contrib/libexec/CVS/Entries
deleted file mode 100644
index cc09ac02..00000000
--- a/contrib/libexec/CVS/Entries
+++ /dev/null
@@ -1,3 +0,0 @@
-D/mail.local////
-/Makefile/1.50/Mon Jul 9 11:41:26 2012//
-/Makefile.inc/1.2/Mon Jul 9 11:41:26 2012//
diff --git a/contrib/libexec/CVS/Repository b/contrib/libexec/CVS/Repository
deleted file mode 100644
index 31ac4266..00000000
--- a/contrib/libexec/CVS/Repository
+++ /dev/null
@@ -1 +0,0 @@
-src/libexec
diff --git a/contrib/libexec/CVS/Root b/contrib/libexec/CVS/Root
deleted file mode 100644
index 7040dfb5..00000000
--- a/contrib/libexec/CVS/Root
+++ /dev/null
@@ -1 +0,0 @@
-anoncvs@anoncvs.spacehopper.org:/cvs
diff --git a/contrib/libexec/Makefile.am b/contrib/libexec/Makefile.am
index 6079ae6a..0e3a271f 100644
--- a/contrib/libexec/Makefile.am
+++ b/contrib/libexec/Makefile.am
@@ -1 +1 @@
-SUBDIRS = mail.local encrypt
+SUBDIRS = mail.local lockspool encrypt
diff --git a/contrib/libexec/encrypt/Makefile.am b/contrib/libexec/encrypt/Makefile.am
index 6ad7b82d..2f96e60d 100644
--- a/contrib/libexec/encrypt/Makefile.am
+++ b/contrib/libexec/encrypt/Makefile.am
@@ -1,7 +1,7 @@
pkglibexec_PROGRAMS = encrypt
encrypt_SOURCES = encrypt.c
-encrypt_SOURCES += $(top_srcdir)/smtpd/log.c
+encrypt_SOURCES += $(top_srcdir)/usr.sbin/smtpd/log.c
AM_CPPFLAGS = -I$(top_srcdir)/openbsd-compat
diff --git a/contrib/libexec/lockspool/Makefile.am b/contrib/libexec/lockspool/Makefile.am
new file mode 100644
index 00000000..2801c101
--- /dev/null
+++ b/contrib/libexec/lockspool/Makefile.am
@@ -0,0 +1,20 @@
+pkglibexec_PROGRAMS = lockspool
+
+lockspool_SOURCES = lockspool.c
+lockspool_SOURCES += locking.c
+lockspool_SOURCES += $(top_srcdir)/usr.sbin/smtpd/log.c
+
+EXTRA_DIST = mail.local.h pathnames.h
+
+AM_CPPFLAGS = -I$(top_srcdir)/openbsd-compat -I$(top_srcdir)/mail.local
+
+LIBCOMPAT = $(top_builddir)/openbsd-compat/libopenbsd.a
+
+LDADD = $(LIBCOMPAT)
+
+install-exec-hook: $(CONFIGFILES) $(MANPAGES)
+ chown root $(DESTDIR)$(pkglibexecdir)/lockspool || true
+ chmod 4555 $(DESTDIR)$(pkglibexecdir)/lockspool || true
+
+uninstall-hook:
+ rmdir $(DESTDIR)$(pkglibexecdir) 2> /dev/null || /bin/true
diff --git a/contrib/libexec/lockspool/locking.c b/contrib/libexec/lockspool/locking.c
new file mode 100644
index 00000000..e4922dd6
--- /dev/null
+++ b/contrib/libexec/lockspool/locking.c
@@ -0,0 +1,181 @@
+/* $OpenBSD: locking.c,v 1.14 2020/02/09 14:59:20 millert Exp $ */
+
+/*
+ * Copyright (c) 1996-1998 Theo de Raadt <deraadt@theos.com>
+ * Copyright (c) 1996-1998 David Mazieres <dm@lcs.mit.edu>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the authors may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "includes.h"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <pwd.h>
+#include <syslog.h>
+#include <time.h>
+#include <unistd.h>
+#include <limits.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include "pathnames.h"
+#include "mail.local.h"
+
+static char lpath[PATH_MAX];
+
+void
+rellock(void)
+{
+
+ if (lpath[0])
+ unlink(lpath);
+}
+
+int
+getlock(const char *name, struct passwd *pw)
+{
+ struct stat sb, fsb;
+ int lfd=-1;
+ char buf[8*1024];
+ int tries = 0;
+
+ (void)snprintf(lpath, sizeof lpath, "%s/%s.lock",
+ _PATH_MAILDIR, name);
+
+ if (stat(_PATH_MAILDIR, &sb) != -1 &&
+ (sb.st_mode & S_IWOTH) == S_IWOTH) {
+ /*
+ * We have a writeable spool, deal with it as
+ * securely as possible.
+ */
+ time_t ctim = -1;
+
+ seteuid(pw->pw_uid);
+ if (lstat(lpath, &sb) != -1)
+ ctim = sb.st_ctime;
+ while (1) {
+ /*
+ * Deal with existing user.lock files
+ * or directories or symbolic links that
+ * should not be here.
+ */
+ if (readlink(lpath, buf, sizeof buf-1) != -1) {
+ if (lstat(lpath, &sb) != -1 &&
+ S_ISLNK(sb.st_mode)) {
+ seteuid(sb.st_uid);
+ unlink(lpath);
+ seteuid(pw->pw_uid);
+ }
+ goto again;
+ }
+ if ((lfd = open(lpath, O_CREAT|O_WRONLY|O_EXCL|O_EXLOCK,
+ S_IRUSR|S_IWUSR)) != -1)
+ break;
+again:
+ if (tries > 10) {
+ mwarn("%s: %s", lpath, strerror(errno));
+ seteuid(0);
+ return(-1);
+ }
+ if (tries > 9 &&
+ (lfd = open(lpath, O_WRONLY|O_EXLOCK, 0)) != -1) {
+ if (fstat(lfd, &fsb) != -1 &&
+ lstat(lpath, &sb) != -1) {
+ if (fsb.st_dev == sb.st_dev &&
+ fsb.st_ino == sb.st_ino &&
+ ctim == fsb.st_ctime ) {
+ seteuid(fsb.st_uid);
+ baditem(lpath);
+ seteuid(pw->pw_uid);
+ }
+ }
+ close(lfd);
+ }
+ sleep(1U << tries);
+ tries++;
+ continue;
+ }
+ seteuid(0);
+ } else {
+ /*
+ * Only root can write the spool directory.
+ */
+ while (1) {
+ if ((lfd = open(lpath, O_CREAT|O_WRONLY|O_EXCL,
+ S_IRUSR|S_IWUSR)) != -1)
+ break;
+ if (tries > 9) {
+ mwarn("%s: %s", lpath, strerror(errno));
+ return(-1);
+ }
+ sleep(1U << tries);
+ tries++;
+ }
+ }
+ return(lfd);
+}
+
+void
+baditem(char *path)
+{
+ char npath[PATH_MAX];
+ int fd;
+
+ if (unlink(path) == 0)
+ return;
+ snprintf(npath, sizeof npath, "%s/mailXXXXXXXXXX", _PATH_MAILDIR);
+ if ((fd = mkstemp(npath)) == -1)
+ return;
+ close(fd);
+ if (rename(path, npath) == -1)
+ unlink(npath);
+ else
+ mwarn("nasty spool item %s renamed to %s", path, npath);
+ /* XXX if we fail to rename, another attempt will happen later */
+}
+
+void
+mwarn(const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ vsyslog(LOG_ERR, fmt, ap);
+ va_end(ap);
+}
+
+void
+merr(int eval, const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ vsyslog(LOG_ERR, fmt, ap);
+ va_end(ap);
+ exit(eval);
+}
diff --git a/contrib/libexec/lockspool/lockspool.1 b/contrib/libexec/lockspool/lockspool.1
new file mode 100644
index 00000000..ea5524bf
--- /dev/null
+++ b/contrib/libexec/lockspool/lockspool.1
@@ -0,0 +1,77 @@
+.\" $OpenBSD: lockspool.1,v 1.14 2019/01/25 00:19:26 millert Exp $
+.\"
+.\" Copyright (c) 1998 Todd C. Miller <millert@openbsd.org>
+.\"
+.\" Permission to use, copy, modify, and distribute this software for any
+.\" purpose with or without fee is hereby granted, provided that the above
+.\" copyright notice and this permission notice appear in all copies.
+.\"
+.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+.\"
+.Dd $Mdocdate: January 25 2019 $
+.Dt LOCKSPOOL 1
+.Os
+.Sh NAME
+.Nm lockspool
+.Nd lock user's system mailbox
+.Sh SYNOPSIS
+.Nm lockspool
+.Op Ar username
+.Sh DESCRIPTION
+.Nm
+is useful for a client mail program to attain proper locking.
+.Nm
+obtains a
+.Pa username.lock
+for the calling user and retains it until stdin is closed or a signal like
+.Dv SIGINT ,
+.Dv SIGTERM ,
+or
+.Dv SIGHUP
+is received.
+Additionally, the superuser may specify the name of a user in order
+to lock a different mailbox.
+.Pp
+If
+.Nm
+is able to create the lock file,
+.Dq 1
+is written to stdout, otherwise
+.Dq 0
+is written and an error message is written to stderr.
+.Nm
+will try up to 10 times to get the lock (sleeping
+for a short period in between tries).
+.Pp
+Typical usage is for a user mail agent (such as
+.Xr mail 1 )
+to open a pipe to
+.Nm
+when it needs to lock the user's mail spool.
+Closing the pipe will cause
+.Nm
+to release the lock.
+.Sh FILES
+.Bl -tag -width /var/mail/username.lock -compact
+.It Pa /var/mail/username.lock
+user's mail lock file
+.El
+.Sh EXIT STATUS
+The
+.Nm
+utility exits 0 on success, and 1 if an error occurs.
+.Sh SEE ALSO
+.Xr mail 1 ,
+.Xr mail.local 8 ,
+.Xr smtpd 8
+.Sh HISTORY
+The
+.Nm
+program appeared in
+.Ox 2.4 .
diff --git a/contrib/libexec/lockspool/lockspool.c b/contrib/libexec/lockspool/lockspool.c
new file mode 100644
index 00000000..9277241b
--- /dev/null
+++ b/contrib/libexec/lockspool/lockspool.c
@@ -0,0 +1,124 @@
+/* $OpenBSD: lockspool.c,v 1.21 2020/02/09 14:59:20 millert Exp $ */
+
+/*
+ * Copyright (c) 1998 Theo de Raadt <deraadt@theos.com>
+ * Copyright (c) 1998 Todd C. Miller <millert@openbsd.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "includes.h"
+
+#include <signal.h>
+#include <pwd.h>
+#include <syslog.h>
+#include <unistd.h>
+#include <errno.h>
+#include <stdio.h>
+#include <paths.h>
+#include <stdlib.h>
+#include <poll.h>
+#include <err.h>
+
+#include "mail.local.h"
+
+void unhold(int);
+void usage(void);
+
+extern char *__progname;
+
+int
+main(int argc, char *argv[])
+{
+ struct passwd *pw;
+ struct pollfd pfd;
+ ssize_t nread;
+ char *from, c;
+ int holdfd;
+
+#if HAVE_UNVEIL
+ if (unveil(_PATH_MAILDIR, "rwc") == -1)
+ err(1, "unveil");
+#endif
+#if HAVE_PLEDGE
+ if (pledge("stdio rpath wpath getpw cpath fattr", NULL) == -1)
+ err(1, "pledge");
+#endif
+
+ openlog(__progname, LOG_PERROR, LOG_MAIL);
+
+ if (argc != 1 && argc != 2)
+ usage();
+ if (argc == 2 && getuid() != 0)
+ merr(1, "you must be root to lock someone else's spool");
+
+ signal(SIGTERM, unhold);
+ signal(SIGINT, unhold);
+ signal(SIGHUP, unhold);
+ signal(SIGPIPE, unhold);
+
+ if (argc == 2)
+ pw = getpwnam(argv[1]);
+ else
+ pw = getpwuid(getuid());
+ if (pw == NULL)
+ exit (1);
+ from = pw->pw_name;
+
+ holdfd = getlock(from, pw);
+ if (holdfd == -1) {
+ write(STDOUT_FILENO, "0\n", 2);
+ exit (1);
+ }
+ write(STDOUT_FILENO, "1\n", 2);
+
+ /* wait for the other end of the pipe to close, then release the lock */
+ pfd.fd = STDIN_FILENO;
+ pfd.events = POLLIN;
+ do {
+ if (poll(&pfd, 1, INFTIM) == -1) {
+ if (errno != EINTR)
+ break;
+ }
+ do {
+ nread = read(STDIN_FILENO, &c, 1);
+ } while (nread == 1 || (nread == -1 && errno == EINTR));
+ } while (nread == -1 && errno == EAGAIN);
+ rellock();
+ exit (0);
+}
+
+/*ARGSUSED*/
+void
+unhold(int signo)
+{
+
+ rellock();
+ _exit(0);
+}
+
+void
+usage(void)
+{
+
+ merr(1, "usage: %s [username]", __progname);
+}
diff --git a/contrib/libexec/lockspool/mail.local.h b/contrib/libexec/lockspool/mail.local.h
new file mode 100644
index 00000000..bc3137cb
--- /dev/null
+++ b/contrib/libexec/lockspool/mail.local.h
@@ -0,0 +1,42 @@
+/* $OpenBSD: mail.local.h,v 1.7 2020/02/09 14:59:21 millert Exp $ */
+
+/*-
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+void baditem(char *);
+int deliver(int, char *, int);
+void merr(int, const char *, ...);
+void mwarn(const char *, ...);
+int getlock(const char *, struct passwd *);
+void notifybiff(char *);
+void rellock(void);
+int storemail(char *);
+int lockspool(const char *, struct passwd *);
+void unlockspool(void);
+void usage(void);
diff --git a/contrib/libexec/lockspool/pathnames.h b/contrib/libexec/lockspool/pathnames.h
new file mode 100644
index 00000000..0a2c2731
--- /dev/null
+++ b/contrib/libexec/lockspool/pathnames.h
@@ -0,0 +1,38 @@
+/* $OpenBSD: pathnames.h,v 1.5 2003/06/02 19:38:24 millert Exp $*/
+
+/*-
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * from: @(#)pathnames.h 5.3 (Berkeley) 1/17/91
+ */
+#ifdef HAVE_PATHS_H
+#include <paths.h>
+#endif
+
+#define _PATH_LOCTMP "/tmp/local.XXXXXXXXXX"
+#define _PATH_LOCKSPOOL PATH_LIBEXEC"/lockspool"
diff --git a/contrib/libexec/mail.local/CVS/Entries b/contrib/libexec/mail.local/CVS/Entries
deleted file mode 100644
index e02ca255..00000000
--- a/contrib/libexec/mail.local/CVS/Entries
+++ /dev/null
@@ -1,7 +0,0 @@
-/locking.c/1.10/Mon Jan 10 21:00:50 2011//
-/mail.local.c/1.32/Tue Oct 27 23:59:31 2009//
-/Makefile/1.3/Mon Jul 9 11:41:26 2012//
-/mail.local.8/1.29/Mon Jul 9 11:36:35 2012//
-/mail.local.h/1.5/Mon Jul 9 11:36:35 2012//
-/pathnames.h/1.5/Mon Jul 9 11:36:35 2012//
-D
diff --git a/contrib/libexec/mail.local/CVS/Repository b/contrib/libexec/mail.local/CVS/Repository
deleted file mode 100644
index ca372a1e..00000000
--- a/contrib/libexec/mail.local/CVS/Repository
+++ /dev/null
@@ -1 +0,0 @@
-src/libexec/mail.local
diff --git a/contrib/libexec/mail.local/CVS/Root b/contrib/libexec/mail.local/CVS/Root
deleted file mode 100644
index 7040dfb5..00000000
--- a/contrib/libexec/mail.local/CVS/Root
+++ /dev/null
@@ -1 +0,0 @@
-anoncvs@anoncvs.spacehopper.org:/cvs
diff --git a/contrib/libexec/mail.local/Makefile.am b/contrib/libexec/mail.local/Makefile.am
index 97325c74..217659c1 100644
--- a/contrib/libexec/mail.local/Makefile.am
+++ b/contrib/libexec/mail.local/Makefile.am
@@ -2,11 +2,11 @@ pkglibexec_PROGRAMS = mail.local
mail_local_SOURCES = mail.local.c
mail_local_SOURCES += locking.c
-mail_local_SOURCES += $(top_srcdir)/smtpd/log.c
+mail_local_SOURCES += $(top_srcdir)/usr.sbin/smtpd/log.c
EXTRA_DIST = mail.local.h pathnames.h
-AM_CPPFLAGS = -I$(top_srcdir)/openbsd-compat
+AM_CPPFLAGS = -I$(top_srcdir)/openbsd-compat -DPATH_LIBEXEC=\"$(pkglibexecdir)\"
LIBCOMPAT = $(top_builddir)/openbsd-compat/libopenbsd.a
diff --git a/contrib/libexec/mail.local/locking.c b/contrib/libexec/mail.local/locking.c
index ab6eb1a8..85a48d5e 100644
--- a/contrib/libexec/mail.local/locking.c
+++ b/contrib/libexec/mail.local/locking.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: locking.c,v 1.10 2011/01/10 21:00:50 millert Exp $ */
+/* $OpenBSD: locking.c,v 1.14 2020/02/09 14:59:20 millert Exp $ */
/*
* Copyright (c) 1996-1998 Theo de Raadt <deraadt@theos.com>
@@ -30,13 +30,15 @@
#include "includes.h"
-#include <sys/param.h>
+#include <sys/types.h>
+
#include <sys/stat.h>
#include <fcntl.h>
#include <pwd.h>
#include <syslog.h>
#include <time.h>
#include <unistd.h>
+#include <limits.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
@@ -45,7 +47,7 @@
#include "pathnames.h"
#include "mail.local.h"
-static char lpath[MAXPATHLEN];
+static char lpath[PATH_MAX];
void
rellock(void)
@@ -56,7 +58,7 @@ rellock(void)
}
int
-getlock(char *name, struct passwd *pw)
+getlock(const char *name, struct passwd *pw)
{
struct stat sb, fsb;
int lfd=-1;
@@ -66,9 +68,8 @@ getlock(char *name, struct passwd *pw)
(void)snprintf(lpath, sizeof lpath, "%s/%s.lock",
_PATH_MAILDIR, name);
- if (stat(_PATH_MAILDIR, &sb) == -1)
- merr(FATAL, "%s: %s", _PATH_MAILDIR, strerror(errno));
- if ((sb.st_mode & S_IWOTH) == S_IWOTH) {
+ if (stat(_PATH_MAILDIR, &sb) != -1 &&
+ (sb.st_mode & S_IWOTH) == S_IWOTH) {
/*
* We have a writeable spool, deal with it as
* securely as possible.
@@ -93,19 +94,12 @@ getlock(char *name, struct passwd *pw)
}
goto again;
}
-#ifndef O_EXLOCK
-#define O_EXLOCK 0
-#endif
if ((lfd = open(lpath, O_CREAT|O_WRONLY|O_EXCL|O_EXLOCK,
S_IRUSR|S_IWUSR)) != -1)
break;
-#ifndef O_EXLOCK
- /* XXX : do something! */
-#endif
again:
if (tries > 10) {
- merr(NOTFATAL, "%s: %s", lpath,
- strerror(errno));
+ mwarn("%s: %s", lpath, strerror(errno));
seteuid(0);
return(-1);
}
@@ -121,6 +115,7 @@ again:
seteuid(pw->pw_uid);
}
}
+ close(lfd);
}
sleep(1U << tries);
tries++;
@@ -136,7 +131,7 @@ again:
S_IRUSR|S_IWUSR)) != -1)
break;
if (tries > 9) {
- merr(NOTFATAL, "%s: %s", lpath, strerror(errno));
+ mwarn("%s: %s", lpath, strerror(errno));
return(-1);
}
sleep(1U << tries);
@@ -149,7 +144,7 @@ again:
void
baditem(char *path)
{
- char npath[MAXPATHLEN];
+ char npath[PATH_MAX];
int fd;
if (unlink(path) == 0)
@@ -161,19 +156,27 @@ baditem(char *path)
if (rename(path, npath) == -1)
unlink(npath);
else
- merr(NOTFATAL, "nasty spool item %s renamed to %s",
- path, npath);
+ mwarn("nasty spool item %s renamed to %s", path, npath);
/* XXX if we fail to rename, another attempt will happen later */
}
void
-merr(int isfatal, const char *fmt, ...)
+mwarn(const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ vsyslog(LOG_ERR, fmt, ap);
+ va_end(ap);
+}
+
+void
+merr(int eval, const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
vsyslog(LOG_ERR, fmt, ap);
va_end(ap);
- if (isfatal)
- exit(1);
+ exit(eval);
}
diff --git a/contrib/libexec/mail.local/mail.local.8 b/contrib/libexec/mail.local/mail.local.8
index f77fa6da..330a4473 100644
--- a/contrib/libexec/mail.local/mail.local.8
+++ b/contrib/libexec/mail.local/mail.local.8
@@ -1,4 +1,4 @@
-.\" $OpenBSD: mail.local.8,v 1.29 2010/09/03 11:35:08 jmc Exp $
+.\" $OpenBSD: mail.local.8,v 1.31 2014/09/16 21:28:51 jmc Exp $
.\" Copyright (c) 1990 The Regents of the University of California.
.\" All rights reserved.
.\"
@@ -28,7 +28,7 @@
.\"
.\" from: @(#)mail.local.8 6.8 (Berkeley) 4/27/91
.\"
-.Dd $Mdocdate: September 3 2010 $
+.Dd $Mdocdate: September 16 2014 $
.Dt MAIL.LOCAL 8
.Os
.Sh NAME
@@ -125,7 +125,7 @@ user's mailbox directory
.Xr flock 2 ,
.Xr getservbyname 3 ,
.Xr comsat 8 ,
-.Xr sendmail 8
+.Xr smtpd 8
.Sh HISTORY
A superset of
.Nm
@@ -134,17 +134,12 @@ A superset of
as the program
.Xr mail 1 .
.Sh BUGS
-Since
-.Xr sendmail 8
-bases its idea of whether a message has been delivered or not
-on the return value from
-.Nm mail.local ,
-using quotas in
+Using quotas in
.Pa /var/mail
-can be problematic.
-By default,
+can be problematic if using
.Xr sendmail 8
-will ask
+as an MTA,
+since it asks
.Nm
to deliver a message to multiple recipients if possible.
This causes problems in a quota environment since a message may be
@@ -157,8 +152,13 @@ to attempt redelivery later.
That means that some users will keep getting the same message every time
.Xr sendmail 8
runs its queue.
+This problem does not exist for
+.Xr smtpd 8
+users.
.Pp
-If you are running with disk quotas on
+If you are running
+.Xr sendmail 8
+and have disk quotas on
.Pa /var/mail
it is imperative that you unset the
.Dq m
diff --git a/contrib/libexec/mail.local/mail.local.c b/contrib/libexec/mail.local/mail.local.c
index d1f7d2f2..a574b3fe 100644
--- a/contrib/libexec/mail.local/mail.local.c
+++ b/contrib/libexec/mail.local/mail.local.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: mail.local.c,v 1.32 2009/10/27 23:59:31 deraadt Exp $ */
+/* $OpenBSD: mail.local.c,v 1.39 2020/02/09 14:59:20 millert Exp $ */
/*-
* Copyright (c) 1996-1998 Theo de Raadt <deraadt@theos.com>
@@ -33,20 +33,25 @@
#include "includes.h"
-#include <sys/param.h>
+#include <sys/types.h>
+
#include <sys/stat.h>
#include <sys/socket.h>
+#include <sys/wait.h>
#include <netinet/in.h>
+#include <sysexits.h>
#include <syslog.h>
#include <fcntl.h>
#include <netdb.h>
#include <pwd.h>
#include <time.h>
#include <unistd.h>
+#include <limits.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <signal.h>
#include "pathnames.h"
#include "mail.local.h"
@@ -54,21 +59,21 @@ int
main(int argc, char *argv[])
{
struct passwd *pw;
- int ch, fd, eval, lockfile=1, holdme=0;
+ int ch, fd, eval, lockfile=1;
uid_t uid;
char *from;
openlog("mail.local", LOG_PERROR, LOG_MAIL);
from = NULL;
- while ((ch = getopt(argc, argv, "lLdf:r:H")) != -1)
+ while ((ch = getopt(argc, argv, "lLdf:r:")) != -1)
switch (ch) {
case 'd': /* backward compatible */
break;
case 'f':
case 'r': /* backward compatible */
if (from)
- merr(FATAL, "multiple -f options");
+ merr(EX_USAGE, "multiple -f options");
from = optarg;
break;
case 'l':
@@ -77,25 +82,14 @@ main(int argc, char *argv[])
case 'L':
lockfile=0;
break;
- case 'H':
- holdme=1;
- break;
default:
usage();
}
argc -= optind;
argv += optind;
- /* Support -H flag for backwards compat */
- if (holdme) {
- execl(_PATH_LOCKSPOOL, "lockspool", (char *)NULL);
- merr(FATAL, "execl: lockspool: %s", strerror(errno));
- } else {
- if (!*argv)
- usage();
- if (geteuid() != 0)
- merr(FATAL, "may only be run by the superuser");
- }
+ if (!*argv)
+ usage();
/*
* If from not specified, use the name from getlogin() if the
@@ -108,8 +102,10 @@ main(int argc, char *argv[])
from = (pw = getpwuid(uid)) ? pw->pw_name : "???";
fd = storemail(from);
- for (eval = 0; *argv; ++argv)
- eval |= deliver(fd, *argv, lockfile);
+ for (eval = 0; *argv; ++argv) {
+ if ((ch = deliver(fd, *argv, lockfile)) != 0)
+ eval = ch;
+ }
exit(eval);
}
@@ -119,24 +115,31 @@ storemail(char *from)
FILE *fp = NULL;
time_t tval;
int fd, eline;
- ssize_t len;
- size_t linesz;
+ size_t len;
char *line, *tbuf;
if ((tbuf = strdup(_PATH_LOCTMP)) == NULL)
- merr(FATAL, "unable to allocate memory");
+ merr(EX_OSERR, "unable to allocate memory");
if ((fd = mkstemp(tbuf)) == -1 || !(fp = fdopen(fd, "w+")))
- merr(FATAL, "unable to open temporary file");
+ merr(EX_OSERR, "unable to open temporary file");
(void)unlink(tbuf);
free(tbuf);
(void)time(&tval);
(void)fprintf(fp, "From %s %s", from, ctime(&tval));
- for (eline = 1, line = NULL, linesz = 0;
- (len = getline(&line, &linesz, stdin)) != -1;) {
+ for (eline = 1, tbuf = NULL; (line = fgetln(stdin, &len));) {
+ /* We have to NUL-terminate the line since fgetln does not */
if (line[len - 1] == '\n')
- line[--len] = '\0';
+ line[len - 1] = '\0';
+ else {
+ /* No trailing newline, so alloc space and copy */
+ if ((tbuf = malloc(len + 1)) == NULL)
+ merr(EX_OSERR, "unable to allocate memory");
+ memcpy(tbuf, line, len);
+ tbuf[len] = '\0';
+ line = tbuf;
+ }
if (line[0] == '\0')
eline = 1;
else {
@@ -149,13 +152,13 @@ storemail(char *from)
if (ferror(fp))
break;
}
- free(line);
+ free(tbuf);
/* Output a newline; note, empty messages are allowed. */
(void)putc('\n', fp);
(void)fflush(fp);
if (ferror(fp))
- merr(FATAL, "temporary file write error");
+ merr(EX_OSERR, "temporary file write error");
return(fd);
}
@@ -164,8 +167,8 @@ deliver(int fd, char *name, int lockfile)
{
struct stat sb, fsb;
struct passwd *pw;
- int mbfd=-1, rval=1, lfd=-1;
- char biffmsg[100], buf[8*1024], path[MAXPATHLEN];
+ int mbfd=-1, lfd=-1, rval=EX_OSERR;
+ char biffmsg[100], buf[8*1024], path[PATH_MAX];
off_t curoff;
size_t off;
ssize_t nr, nw;
@@ -175,30 +178,27 @@ deliver(int fd, char *name, int lockfile)
* handled in the sendmail aliases file.
*/
if (!(pw = getpwnam(name))) {
- merr(NOTFATAL, "unknown name: %s", name);
- return(1);
+ mwarn("unknown name: %s", name);
+ return(EX_NOUSER);
}
(void)snprintf(path, sizeof path, "%s/%s", _PATH_MAILDIR, name);
if (lockfile) {
- lfd = getlock(name, pw);
+ lfd = lockspool(name, pw);
if (lfd == -1)
- return (1);
+ return(EX_OSERR);
}
/* after this point, always exit via bad to remove lockfile */
retry:
if (lstat(path, &sb)) {
if (errno != ENOENT) {
- merr(NOTFATAL, "%s: %s", path, strerror(errno));
+ mwarn("%s: %s", path, strerror(errno));
goto bad;
}
-#ifndef O_EXLOCK
-#define O_EXLOCK 0
-#endif
if ((mbfd = open(path, O_APPEND|O_CREAT|O_EXCL|O_WRONLY|O_EXLOCK,
- S_IRUSR|S_IWUSR)) < 0) {
+ S_IRUSR|S_IWUSR)) == -1) {
#ifndef HAVE_O_EXLOCK
/* XXX : do something! */
#endif
@@ -206,7 +206,8 @@ retry:
/* file appeared since lstat */
goto retry;
} else {
- merr(NOTFATAL, "%s: %s", path, strerror(errno));
+ mwarn("%s: %s", path, strerror(errno));
+ rval = EX_CANTCREAT;
goto bad;
}
}
@@ -216,49 +217,48 @@ retry:
* that if the ownership or permissions were changed there
* was a reason for doing so.
*/
- if (fchown(mbfd, pw->pw_uid, pw->pw_gid) < 0) {
- merr(NOTFATAL, "chown %u:%u: %s",
- pw->pw_uid, pw->pw_gid, name);
+ if (fchown(mbfd, pw->pw_uid, pw->pw_gid) == -1) {
+ mwarn("chown %u:%u: %s", pw->pw_uid, pw->pw_gid, name);
goto bad;
}
} else {
if (sb.st_nlink != 1 || !S_ISREG(sb.st_mode)) {
- merr(NOTFATAL, "%s: linked or special file", path);
+ mwarn("%s: linked or special file", path);
goto bad;
}
if ((mbfd = open(path, O_APPEND|O_WRONLY|O_EXLOCK,
- S_IRUSR|S_IWUSR)) < 0) {
- merr(NOTFATAL, "%s: %s", path, strerror(errno));
+ S_IRUSR|S_IWUSR)) == -1) {
+ mwarn("%s: %s", path, strerror(errno));
goto bad;
}
- if (fstat(mbfd, &fsb)) {
+ if (fstat(mbfd, &fsb) == -1) {
/* relating error to path may be bad style */
- merr(NOTFATAL, "%s: %s", path, strerror(errno));
+ mwarn("%s: %s", path, strerror(errno));
goto bad;
}
if (sb.st_dev != fsb.st_dev || sb.st_ino != fsb.st_ino) {
- merr(NOTFATAL, "%s: changed after open", path);
+ mwarn("%s: changed after open", path);
goto bad;
}
/* paranoia? */
if (fsb.st_nlink != 1 || !S_ISREG(fsb.st_mode)) {
- merr(NOTFATAL, "%s: linked or special file", path);
+ mwarn("%s: linked or special file", path);
+ rval = EX_CANTCREAT;
goto bad;
}
}
curoff = lseek(mbfd, 0, SEEK_END);
- (void)snprintf(biffmsg, sizeof biffmsg, "%s@%qd\n", name,
- (long long int) curoff);
+ (void)snprintf(biffmsg, sizeof biffmsg, "%s@%lld\n", name, curoff);
if (lseek(fd, 0, SEEK_SET) == (off_t)-1) {
- merr(NOTFATAL, "temporary file: %s", strerror(errno));
+ mwarn("temporary file: %s", strerror(errno));
goto bad;
}
while ((nr = read(fd, buf, sizeof(buf))) > 0)
- for (off = 0; off < (size_t)nr; off += nw)
- if ((nw = write(mbfd, buf + off, nr - off)) < 0) {
- merr(NOTFATAL, "%s: %s", path, strerror(errno));
+ for (off = 0; off < nr; off += nw)
+ if ((nw = write(mbfd, buf + off, nr - off)) == -1) {
+ mwarn("%s: %s", path, strerror(errno));
(void)ftruncate(mbfd, curoff);
goto bad;
}
@@ -267,14 +267,12 @@ retry:
rval = 0;
} else {
(void)ftruncate(mbfd, curoff);
- merr(FATAL, "temporary file: %s", strerror(errno));
+ mwarn("temporary file: %s", strerror(errno));
}
bad:
- if (lfd != -1) {
- rellock();
- close(lfd);
- }
+ if (lfd != -1)
+ unlockspool();
if (mbfd != -1) {
(void)fsync(mbfd); /* Don't wait for update. */
@@ -289,39 +287,106 @@ bad:
void
notifybiff(char *msg)
{
- static struct sockaddr_in addr;
+ static struct addrinfo *res0;
+ struct addrinfo hints, *res;
static int f = -1;
- struct hostent *hp;
- struct servent *sp;
size_t len;
+ int error;
- if (!addr.sin_family) {
- /* Be silent if biff service not available. */
- if (!(sp = getservbyname("biff", "udp")))
- return;
- if (!(hp = gethostbyname("localhost"))) {
- merr(NOTFATAL, "localhost: %s", strerror(errno));
+ if (res0 == NULL) {
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = PF_UNSPEC;
+ hints.ai_socktype = SOCK_DGRAM;
+
+ error = getaddrinfo("localhost", "biff", &hints, &res0);
+ if (error) {
+ /* Be silent if biff service not available. */
+ if (error != EAI_SERVICE) {
+ mwarn("localhost: %s", gai_strerror(error));
+ }
return;
}
-#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
- addr.sin_len = sizeof(struct sockaddr_in);
-#endif
- addr.sin_family = hp->h_addrtype;
- addr.sin_port = sp->s_port;
- bcopy(hp->h_addr, &addr.sin_addr, (size_t)hp->h_length);
}
- if (f < 0 && (f = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
- merr(NOTFATAL, "socket: %s", strerror(errno));
+
+ if (f == -1) {
+ for (res = res0; res != NULL; res = res->ai_next) {
+ f = socket(res->ai_family, res->ai_socktype,
+ res->ai_protocol);
+ if (f != -1)
+ break;
+ }
+ }
+ if (f == -1) {
+ mwarn("socket: %s", strerror(errno));
return;
}
- len = strlen(msg) + 1;
- if (sendto(f, msg, len, 0, (struct sockaddr *)&addr, sizeof(addr))
- != (ssize_t)len)
- merr(NOTFATAL, "sendto biff: %s", strerror(errno));
+
+ len = strlen(msg) + 1; /* XXX */
+ if (sendto(f, msg, len, 0, res->ai_addr, res->ai_addrlen) != len)
+ mwarn("sendto biff: %s", strerror(errno));
+}
+
+static int lockfd = -1;
+static pid_t lockpid = -1;
+
+int
+lockspool(const char *name, struct passwd *pw)
+{
+ int pfd[2];
+ char ch;
+
+ if (geteuid() == 0)
+ return getlock(name, pw);
+
+ /* If not privileged, open pipe to lockspool(1) instead */
+ if (pipe2(pfd, O_CLOEXEC) == -1) {
+ merr(EX_OSERR, "pipe: %s", strerror(errno));
+ return -1;
+ }
+
+ signal(SIGPIPE, SIG_IGN);
+ switch ((lockpid = fork())) {
+ case -1:
+ merr(EX_OSERR, "fork: %s", strerror(errno));
+ return -1;
+ case 0:
+ /* child */
+ close(pfd[0]);
+ dup2(pfd[1], STDOUT_FILENO);
+ execl(_PATH_LOCKSPOOL, "lockspool", (char *)NULL);
+ merr(EX_OSERR, "execl: lockspool: %s", strerror(errno));
+ /* NOTREACHED */
+ break;
+ default:
+ /* parent */
+ close(pfd[1]);
+ lockfd = pfd[0];
+ break;
+ }
+
+ if (read(lockfd, &ch, 1) != 1 || ch != '1') {
+ unlockspool();
+ merr(EX_OSERR, "lockspool: unable to get lock");
+ }
+
+ return lockfd;
+}
+
+void
+unlockspool(void)
+{
+ if (lockpid != -1) {
+ waitpid(lockpid, NULL, 0);
+ lockpid = -1;
+ } else {
+ rellock();
+ }
+ close(lockfd);
+ lockfd = -1;
}
void
usage(void)
{
- merr(FATAL, "usage: mail.local [-Ll] [-f from] user ...");
+ merr(EX_USAGE, "usage: mail.local [-Ll] [-f from] user ...");
}
diff --git a/contrib/libexec/mail.local/mail.local.h b/contrib/libexec/mail.local/mail.local.h
index 0377aa20..bc3137cb 100644
--- a/contrib/libexec/mail.local/mail.local.h
+++ b/contrib/libexec/mail.local/mail.local.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: mail.local.h,v 1.5 2006/04/01 22:48:57 deraadt Exp $ */
+/* $OpenBSD: mail.local.h,v 1.7 2020/02/09 14:59:21 millert Exp $ */
/*-
* Copyright (c) 1990 The Regents of the University of California.
@@ -29,14 +29,14 @@
* SUCH DAMAGE.
*/
-#define FATAL 1
-#define NOTFATAL 0
-
void baditem(char *);
int deliver(int, char *, int);
void merr(int, const char *, ...);
-int getlock(char *, struct passwd *);
+void mwarn(const char *, ...);
+int getlock(const char *, struct passwd *);
void notifybiff(char *);
void rellock(void);
int storemail(char *);
+int lockspool(const char *, struct passwd *);
+void unlockspool(void);
void usage(void);
diff --git a/contrib/libexec/mail.local/pathnames.h b/contrib/libexec/mail.local/pathnames.h
index a39a6c90..0a2c2731 100644
--- a/contrib/libexec/mail.local/pathnames.h
+++ b/contrib/libexec/mail.local/pathnames.h
@@ -35,4 +35,4 @@
#endif
#define _PATH_LOCTMP "/tmp/local.XXXXXXXXXX"
-#define _PATH_LOCKSPOOL "/usr/libexec/lockspool"
+#define _PATH_LOCKSPOOL PATH_LIBEXEC"/lockspool"
diff --git a/mk/mail/mail.lmtp/Makefile.am b/mk/mail/mail.lmtp/Makefile.am
index 9847dfdf..29c33dd1 100644
--- a/mk/mail/mail.lmtp/Makefile.am
+++ b/mk/mail/mail.lmtp/Makefile.am
@@ -2,8 +2,8 @@ include $(top_srcdir)/mk/pathnames
pkglibexec_PROGRAMS = mail.lmtp
-mail_lmtp_SOURCES = $(smtpd_srcdir)/mail.lmtp.c
-mail_lmtp_SOURCES+= $(smtpd_srcdir)/log.c
+mail_lmtp_SOURCES = $(top_srcdir)/usr.sbin/smtpd/mail.lmtp.c
+mail_lmtp_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/log.c
AM_CPPFLAGS= -I$(top_srcdir)/smtpd \
-I$(top_srcdir)/openbsd-compat
diff --git a/mk/mail/mail.maildir/Makefile.am b/mk/mail/mail.maildir/Makefile.am
index d8f696ee..c2afe4d0 100644
--- a/mk/mail/mail.maildir/Makefile.am
+++ b/mk/mail/mail.maildir/Makefile.am
@@ -2,8 +2,8 @@ include $(top_srcdir)/mk/pathnames
pkglibexec_PROGRAMS = mail.maildir
-mail_maildir_SOURCES = $(smtpd_srcdir)/mail.maildir.c
-mail_maildir_SOURCES+= $(smtpd_srcdir)/log.c
+mail_maildir_SOURCES = $(top_srcdir)/usr.sbin/smtpd/mail.maildir.c
+mail_maildir_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/log.c
AM_CPPFLAGS= -I$(top_srcdir)/smtpd \
-I$(top_srcdir)/openbsd-compat
diff --git a/mk/mail/mail.mboxfile/Makefile.am b/mk/mail/mail.mboxfile/Makefile.am
index d57362c0..de5f4ea5 100644
--- a/mk/mail/mail.mboxfile/Makefile.am
+++ b/mk/mail/mail.mboxfile/Makefile.am
@@ -2,8 +2,8 @@ include $(top_srcdir)/mk/pathnames
pkglibexec_PROGRAMS = mail.mboxfile
-mail_mboxfile_SOURCES = $(smtpd_srcdir)/mail.mboxfile.c
-mail_mboxfile_SOURCES+= $(smtpd_srcdir)/log.c
+mail_mboxfile_SOURCES = $(top_srcdir)/usr.sbin/smtpd/mail.mboxfile.c
+mail_mboxfile_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/log.c
AM_CPPFLAGS= -I$(top_srcdir)/smtpd \
-I$(top_srcdir)/openbsd-compat
diff --git a/mk/mail/mail.mda/Makefile.am b/mk/mail/mail.mda/Makefile.am
index b04aefda..84bbfdb3 100644
--- a/mk/mail/mail.mda/Makefile.am
+++ b/mk/mail/mail.mda/Makefile.am
@@ -2,8 +2,8 @@ include $(top_srcdir)/mk/pathnames
pkglibexec_PROGRAMS = mail.mda
-mail_mda_SOURCES = $(smtpd_srcdir)/mail.mda.c
-mail_mda_SOURCES+= $(smtpd_srcdir)/log.c
+mail_mda_SOURCES = $(top_srcdir)/usr.sbin/smtpd/mail.mda.c
+mail_mda_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/log.c
AM_CPPFLAGS= -I$(top_srcdir)/smtpd \
-I$(top_srcdir)/openbsd-compat
diff --git a/mk/pathnames b/mk/pathnames
index 38582bcc..5cff32da 100644
--- a/mk/pathnames
+++ b/mk/pathnames
@@ -1,4 +1,4 @@
-smtpd_srcdir = $(top_srcdir)/smtpd
+smtpd_srcdir = $(top_srcdir)/usr.sbin/smtpd
compat_srcdir = $(top_srcdir)/openbsd-compat
regress_srcdir = $(top_srcdir)/regress/bin
@@ -6,6 +6,5 @@ PATHS= -DSMTPD_CONFDIR=\"$(sysconfdir)\" \
-DPATH_CHROOT=\"$(PRIVSEP_PATH)\" \
-DPATH_SMTPCTL=\"$(sbindir)/smtpctl\" \
-DPATH_MAILLOCAL=\"$(pkglibexecdir)/mail.local\" \
+ -DPATH_MAKEMAP=\"$(sbindir)/makemap\" \
-DPATH_LIBEXEC=\"$(pkglibexecdir)\"
-
-
diff --git a/mk/smtp/Makefile.am b/mk/smtp/Makefile.am
index e955a271..875cacdc 100644
--- a/mk/smtp/Makefile.am
+++ b/mk/smtp/Makefile.am
@@ -2,13 +2,13 @@ include $(top_srcdir)/mk/pathnames
bin_PROGRAMS= smtp
-smtp_SOURCES= $(smtpd_srcdir)/iobuf.c
-smtp_SOURCES+= $(smtpd_srcdir)/ioev.c
-smtp_SOURCES+= $(smtpd_srcdir)/log.c
-smtp_SOURCES+= $(smtpd_srcdir)/smtp_client.c
-smtp_SOURCES+= $(smtpd_srcdir)/smtpc.c
-smtp_SOURCES+= $(smtpd_srcdir)/ssl.c
-smtp_SOURCES+= $(smtpd_srcdir)/ssl_verify.c
+smtp_SOURCES= $(top_srcdir)/usr.sbin/smtpd/iobuf.c
+smtp_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/ioev.c
+smtp_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/log.c
+smtp_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/smtp_client.c
+smtp_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/smtpc.c
+smtp_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/ssl.c
+smtp_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/ssl_verify.c
smtp_CFLAGS= -DIO_TLS
@@ -30,7 +30,7 @@ CFLAGS+= -D_GNU_SOURCE
CPPFLAGS= -I$(srcdir) @CPPFLAGS@ $(PATHS) @DEFS@
MANPAGES= smtp.1.out
-MANPAGES_IN= $(smtpd_srcdir)/smtp.1
+MANPAGES_IN= $(top_srcdir)/usr.sbin/smtpd/smtp.1
EXTRA_DIST= $(MANPAGES_IN)
@@ -42,7 +42,7 @@ FIXPATHSCMD= $(SED) $(PATHSUBS)
$(MANPAGES): $(MANPAGES_IN)
- manpage=$(smtpd_srcdir)/`echo $@ | sed 's/\.out$$//'`; \
+ manpage=$(top_srcdir)/usr.sbin/smtpd/`echo $@ | sed 's/\.out$$//'`; \
if test "$(MANTYPE)" = "man"; then \
$(FIXPATHSCMD) $${manpage} | $(AWK) -f $(srcdir)/../mdoc2man.awk > $@; \
else \
diff --git a/mk/smtpctl/Makefile.am b/mk/smtpctl/Makefile.am
index 8b14b6bd..f7d1b418 100644
--- a/mk/smtpctl/Makefile.am
+++ b/mk/smtpctl/Makefile.am
@@ -2,37 +2,37 @@ include $(top_srcdir)/mk/pathnames
sbin_PROGRAMS= smtpctl
-smtpctl_SOURCES= $(smtpd_srcdir)/enqueue.c
-smtpctl_SOURCES+= $(smtpd_srcdir)/parser.c
-smtpctl_SOURCES+= $(smtpd_srcdir)/log.c
-smtpctl_SOURCES+= $(smtpd_srcdir)/envelope.c
-smtpctl_SOURCES+= $(smtpd_srcdir)/queue_backend.c
-smtpctl_SOURCES+= $(smtpd_srcdir)/queue_fs.c
-smtpctl_SOURCES+= $(smtpd_srcdir)/smtpctl.c
-smtpctl_SOURCES+= $(smtpd_srcdir)/spfwalk.c
-smtpctl_SOURCES+= $(smtpd_srcdir)/util.c
-smtpctl_SOURCES+= $(smtpd_srcdir)/unpack_dns.c
-smtpctl_SOURCES+= $(smtpd_srcdir)/compress_backend.c
-smtpctl_SOURCES+= $(smtpd_srcdir)/compress_gzip.c
-smtpctl_SOURCES+= $(smtpd_srcdir)/to.c
-smtpctl_SOURCES+= $(smtpd_srcdir)/expand.c
-smtpctl_SOURCES+= $(smtpd_srcdir)/tree.c
-smtpctl_SOURCES+= $(smtpd_srcdir)/dict.c
+smtpctl_SOURCES= $(top_srcdir)/usr.sbin/smtpd/enqueue.c
+smtpctl_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/parser.c
+smtpctl_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/log.c
+smtpctl_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/envelope.c
+smtpctl_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/queue_backend.c
+smtpctl_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/queue_fs.c
+smtpctl_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/smtpctl.c
+smtpctl_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/spfwalk.c
+smtpctl_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/util.c
+smtpctl_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/unpack_dns.c
+smtpctl_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/compress_backend.c
+smtpctl_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/compress_gzip.c
+smtpctl_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/to.c
+smtpctl_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/expand.c
+smtpctl_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/tree.c
+smtpctl_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/dict.c
if HAVE_DB_API
-smtpctl_SOURCES+= $(smtpd_srcdir)/config.c
-smtpctl_SOURCES+= $(smtpd_srcdir)/parse.y
-smtpctl_SOURCES+= $(smtpd_srcdir)/limit.c
-smtpctl_SOURCES+= $(smtpd_srcdir)/table.c
-smtpctl_SOURCES+= $(smtpd_srcdir)/table_static.c
-smtpctl_SOURCES+= $(smtpd_srcdir)/table_db.c
-smtpctl_SOURCES+= $(smtpd_srcdir)/table_getpwnam.c
-smtpctl_SOURCES+= $(smtpd_srcdir)/table_proc.c
-smtpctl_SOURCES+= $(smtpd_srcdir)/mailaddr.c
-smtpctl_SOURCES+= $(smtpd_srcdir)/makemap.c
+smtpctl_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/config.c
+smtpctl_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/parse.y
+smtpctl_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/limit.c
+smtpctl_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/table.c
+smtpctl_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/table_static.c
+smtpctl_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/table_db.c
+smtpctl_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/table_getpwnam.c
+smtpctl_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/table_proc.c
+smtpctl_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/mailaddr.c
+smtpctl_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/makemap.c
endif
-smtpctl_SOURCES+= $(smtpd_srcdir)/crypto.c
+smtpctl_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/crypto.c
smtpctl_CFLAGS= -DNO_IO -DCONFIG_MINIMUM
smtpctl_CFLAGS+= -DPATH_GZCAT=\"$(ZCAT)\" \
@@ -59,7 +59,7 @@ CFLAGS+= -D_GNU_SOURCE
CPPFLAGS= -I$(srcdir) @CPPFLAGS@ $(PATHS) @DEFS@
MANPAGES= smtpctl.8.out sendmail.8.out makemap.8.out newaliases.8.out
-MANPAGES_IN= $(smtpd_srcdir)/smtpctl.8 $(smtpd_srcdir)/sendmail.8 $(smtpd_srcdir)/makemap.8 $(smtpd_srcdir)/newaliases.8
+MANPAGES_IN= $(top_srcdir)/usr.sbin/smtpd/smtpctl.8 $(top_srcdir)/usr.sbin/smtpd/sendmail.8 $(top_srcdir)/usr.sbin/smtpd/makemap.8 $(top_srcdir)/usr.sbin/smtpd/newaliases.8
EXTRA_DIST= $(MANPAGES_IN)
@@ -69,9 +69,12 @@ PATHSUBS= -e 's|/var/run/smtpd.sock|$(sockdir)/smtpd.sock|g' \
FIXPATHSCMD= $(SED) $(PATHSUBS)
+if NEED_LIBASR
+AM_CPPFLAGS+= -I$(top_srcdir)/openbsd-compat/libasr
+endif
$(MANPAGES): $(MANPAGES_IN)
- manpage=$(smtpd_srcdir)/`echo $@ | sed 's/\.out$$//'`; \
+ manpage=$(top_srcdir)/usr.sbin/smtpd/`echo $@ | sed 's/\.out$$//'`; \
if test "$(MANTYPE)" = "man"; then \
$(FIXPATHSCMD) $${manpage} | $(AWK) -f $(srcdir)/../mdoc2man.awk > $@; \
else \
diff --git a/mk/smtpd/Makefile.am b/mk/smtpd/Makefile.am
index f5cbe8c8..6d652d48 100644
--- a/mk/smtpd/Makefile.am
+++ b/mk/smtpd/Makefile.am
@@ -14,85 +14,84 @@ include $(top_srcdir)/mk/pathnames
sbin_PROGRAMS= smtpd
-smtpd_SOURCES= $(smtpd_srcdir)/aliases.c
-smtpd_SOURCES+= $(smtpd_srcdir)/bounce.c
-smtpd_SOURCES+= $(smtpd_srcdir)/ca.c
-smtpd_SOURCES+= $(smtpd_srcdir)/cert.c
-smtpd_SOURCES+= $(smtpd_srcdir)/compress_backend.c
-smtpd_SOURCES+= $(smtpd_srcdir)/config.c
-smtpd_SOURCES+= $(smtpd_srcdir)/control.c
-smtpd_SOURCES+= $(smtpd_srcdir)/dict.c
-smtpd_SOURCES+= $(smtpd_srcdir)/dns.c
-smtpd_SOURCES+= $(smtpd_srcdir)/esc.c
-smtpd_SOURCES+= $(smtpd_srcdir)/envelope.c
-smtpd_SOURCES+= $(smtpd_srcdir)/expand.c
-smtpd_SOURCES+= $(smtpd_srcdir)/forward.c
-smtpd_SOURCES+= $(smtpd_srcdir)/iobuf.c
-smtpd_SOURCES+= $(smtpd_srcdir)/ioev.c
-smtpd_SOURCES+= $(smtpd_srcdir)/limit.c
-smtpd_SOURCES+= $(smtpd_srcdir)/lka.c
-smtpd_SOURCES+= $(smtpd_srcdir)/lka_filter.c
-smtpd_SOURCES+= $(smtpd_srcdir)/lka_proc.c
-smtpd_SOURCES+= $(smtpd_srcdir)/lka_report.c
-smtpd_SOURCES+= $(smtpd_srcdir)/lka_session.c
-smtpd_SOURCES+= $(smtpd_srcdir)/log.c
-smtpd_SOURCES+= $(smtpd_srcdir)/mda.c
-smtpd_SOURCES+= $(smtpd_srcdir)/mda_unpriv.c
-smtpd_SOURCES+= $(smtpd_srcdir)/mda_variables.c
-smtpd_SOURCES+= $(smtpd_srcdir)/mproc.c
-smtpd_SOURCES+= $(smtpd_srcdir)/mailaddr.c
-smtpd_SOURCES+= $(smtpd_srcdir)/mta.c
-smtpd_SOURCES+= $(smtpd_srcdir)/mta_session.c
-smtpd_SOURCES+= $(smtpd_srcdir)/parse.y
-smtpd_SOURCES+= $(smtpd_srcdir)/pony.c
-smtpd_SOURCES+= $(smtpd_srcdir)/proxy.c
-smtpd_SOURCES+= $(smtpd_srcdir)/queue.c
-smtpd_SOURCES+= $(smtpd_srcdir)/queue_backend.c
-smtpd_SOURCES+= $(smtpd_srcdir)/report_smtp.c
-smtpd_SOURCES+= $(smtpd_srcdir)/resolver.c
-smtpd_SOURCES+= $(smtpd_srcdir)/rfc5322.c
-smtpd_SOURCES+= $(smtpd_srcdir)/ruleset.c
-smtpd_SOURCES+= $(smtpd_srcdir)/runq.c
-smtpd_SOURCES+= $(smtpd_srcdir)/scheduler.c
-smtpd_SOURCES+= $(smtpd_srcdir)/scheduler_backend.c
-smtpd_SOURCES+= $(smtpd_srcdir)/smtp.c
-smtpd_SOURCES+= $(smtpd_srcdir)/smtp_session.c
-smtpd_SOURCES+= $(smtpd_srcdir)/smtpd.c
-smtpd_SOURCES+= $(smtpd_srcdir)/srs.c
-smtpd_SOURCES+= $(smtpd_srcdir)/ssl.c
-smtpd_SOURCES+= $(smtpd_srcdir)/ssl_smtpd.c
-smtpd_SOURCES+= $(smtpd_srcdir)/ssl_verify.c
-smtpd_SOURCES+= $(smtpd_srcdir)/stat_backend.c
-smtpd_SOURCES+= $(smtpd_srcdir)/table.c
-smtpd_SOURCES+= $(smtpd_srcdir)/to.c
-smtpd_SOURCES+= $(smtpd_srcdir)/tree.c
-smtpd_SOURCES+= $(smtpd_srcdir)/unpack_dns.c
-smtpd_SOURCES+= $(smtpd_srcdir)/util.c
-smtpd_SOURCES+= $(smtpd_srcdir)/waitq.c
+smtpd_SOURCES= $(top_srcdir)/usr.sbin/smtpd/aliases.c
+smtpd_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/bounce.c
+smtpd_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/ca.c
+smtpd_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/cert.c
+smtpd_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/compress_backend.c
+smtpd_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/config.c
+smtpd_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/control.c
+smtpd_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/dict.c
+smtpd_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/dns.c
+smtpd_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/esc.c
+smtpd_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/envelope.c
+smtpd_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/expand.c
+smtpd_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/forward.c
+smtpd_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/iobuf.c
+smtpd_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/limit.c
+smtpd_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/lka.c
+smtpd_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/lka_filter.c
+smtpd_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/lka_session.c
+smtpd_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/log.c
+smtpd_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/mda.c
+smtpd_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/mda_mbox.c
+smtpd_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/mda_unpriv.c
+smtpd_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/mda_variables.c
+smtpd_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/mproc.c
+smtpd_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/mailaddr.c
+smtpd_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/mta.c
+smtpd_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/mta_session.c
+smtpd_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/parse.y
+smtpd_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/pony.c
+smtpd_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/proxy.c
+smtpd_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/queue.c
+smtpd_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/queue_backend.c
+smtpd_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/report_smtp.c
+smtpd_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/resolver.c
+smtpd_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/rfc5322.c
+smtpd_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/ruleset.c
+smtpd_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/runq.c
+smtpd_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/scheduler.c
+smtpd_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/scheduler_backend.c
+smtpd_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/smtp.c
+smtpd_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/smtp_session.c
+smtpd_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/smtpd.c
+smtpd_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/srs.c
+smtpd_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/ssl.c
+smtpd_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/ssl_smtpd.c
+smtpd_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/ssl_verify.c
+smtpd_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/stat_backend.c
+smtpd_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/table.c
+smtpd_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/to.c
+smtpd_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/tree.c
+smtpd_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/unpack_dns.c
+smtpd_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/util.c
+smtpd_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/waitq.c
+smtpd_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/ioev.c
# backends
-smtpd_SOURCES+= $(smtpd_srcdir)/crypto.c
-smtpd_SOURCES+= $(smtpd_srcdir)/compress_gzip.c
+smtpd_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/crypto.c
+smtpd_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/compress_gzip.c
if HAVE_DB_API
-smtpd_SOURCES+= $(smtpd_srcdir)/table_db.c
+smtpd_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/table_db.c
endif
-smtpd_SOURCES+= $(smtpd_srcdir)/table_getpwnam.c
-smtpd_SOURCES+= $(smtpd_srcdir)/table_proc.c
-smtpd_SOURCES+= $(smtpd_srcdir)/table_static.c
-smtpd_SOURCES+= $(smtpd_srcdir)/queue_fs.c
-smtpd_SOURCES+= $(smtpd_srcdir)/queue_null.c
-smtpd_SOURCES+= $(smtpd_srcdir)/queue_proc.c
-smtpd_SOURCES+= $(smtpd_srcdir)/queue_ram.c
-smtpd_SOURCES+= $(smtpd_srcdir)/scheduler_null.c
-smtpd_SOURCES+= $(smtpd_srcdir)/scheduler_proc.c
-smtpd_SOURCES+= $(smtpd_srcdir)/scheduler_ramqueue.c
-smtpd_SOURCES+= $(smtpd_srcdir)/stat_ramstat.c
+smtpd_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/table_getpwnam.c
+smtpd_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/table_proc.c
+smtpd_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/table_static.c
+smtpd_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/queue_fs.c
+smtpd_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/queue_null.c
+smtpd_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/queue_proc.c
+smtpd_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/queue_ram.c
+smtpd_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/scheduler_null.c
+smtpd_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/scheduler_proc.c
+smtpd_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/scheduler_ramqueue.c
+smtpd_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/stat_ramstat.c
smtpd_CFLAGS= -DIO_TLS
smtpd_CFLAGS+= -DCA_FILE=\"$(CA_FILE)\"
-AM_CPPFLAGS= -I$(smtpd_srcdir) \
+AM_CPPFLAGS= -I$(top_srcdir)/usr.sbin/smtpd \
-I$(compat_srcdir)
if !NEED_ERR_H
AM_CPPFLAGS += -I$(top_srcdir)/openbsd-compat/err_h
@@ -102,6 +101,9 @@ AM_CPPFLAGS += -I$(top_srcdir)/openbsd-compat/paths_h
endif
LIBCOMPAT= $(top_builddir)/openbsd-compat/libopenbsd.a
+if NEED_LIBASR
+AM_CPPFLAGS+= -I$(top_srcdir)/openbsd-compat/libasr
+endif
LDADD= $(LIBCOMPAT) $(DB_LIB) $(ASR_LIB)
@@ -109,43 +111,46 @@ LDADD= $(LIBCOMPAT) $(DB_LIB) $(ASR_LIB)
# EAI_NODATA defined
# {v,}asprintf
# setres{g,u}id
-CFLAGS+= -D_GNU_SOURCE -DNEED_EVENT_ASR_RUN
-CPPFLAGS= -I$(srcdir) @CPPFLAGS@ $(PATHS) @DEFS@
+AM_CFLAGS= -D_GNU_SOURCE -DNEED_EVENT_ASR_RUN
+AM_CPPFLAGS+= -I$(srcdir) $(PATHS) @DEFS@
MANPAGES= aliases.5.out forward.5.out smtpd.8.out \
smtpd.conf.5.out table.5.out
-MANPAGES_IN= $(smtpd_srcdir)/aliases.5
-MANPAGES_IN+= $(smtpd_srcdir)/forward.5
-MANPAGES_IN+= $(smtpd_srcdir)/smtpd.8
-MANPAGES_IN+= $(smtpd_srcdir)/smtpd.conf.5
-MANPAGES_IN+= $(smtpd_srcdir)/table.5
+MANPAGES_IN= $(top_srcdir)/usr.sbin/smtpd/aliases.5
+MANPAGES_IN+= $(top_srcdir)/usr.sbin/smtpd/forward.5
+MANPAGES_IN+= $(top_srcdir)/usr.sbin/smtpd/smtpd.8
+MANPAGES_IN+= $(top_srcdir)/usr.sbin/smtpd/smtpd.conf.5
+MANPAGES_IN+= $(top_srcdir)/usr.sbin/smtpd/table.5
CONFIGFILES= smtpd.conf.out
-CONFIGFILES_IN= $(smtpd_srcdir)/smtpd.conf
+CONFIGFILES_IN= $(top_srcdir)/usr.sbin/smtpd/smtpd.conf
EXTRA_DIST= $(CONFIGFILES_IN) $(MANPAGES_IN)
-EXTRA_DIST+= $(smtpd_srcdir)/smtpd.h
-EXTRA_DIST+= $(smtpd_srcdir)/smtpd-api.h
-EXTRA_DIST+= $(smtpd_srcdir)/smtpd-defines.h
-EXTRA_DIST+= $(smtpd_srcdir)/ioev.h
-EXTRA_DIST+= $(smtpd_srcdir)/iobuf.h
-EXTRA_DIST+= $(smtpd_srcdir)/log.h
-EXTRA_DIST+= $(smtpd_srcdir)/ssl.h
-EXTRA_DIST+= $(smtpd_srcdir)/parser.h
-
-EXTRA_DIST+= $(backends_srcdir)/queue_utils.h
-EXTRA_DIST+= $(filters_srcdir)/asr_event.h
+EXTRA_DIST+= $(top_srcdir)/usr.sbin/smtpd/rfc5322.h
+EXTRA_DIST+= $(top_srcdir)/usr.sbin/smtpd/unpack_dns.h
+EXTRA_DIST+= $(top_srcdir)/usr.sbin/smtpd/tree.h
+EXTRA_DIST+= $(top_srcdir)/usr.sbin/smtpd/smtp.h
+EXTRA_DIST+= $(top_srcdir)/usr.sbin/smtpd/smtpd.h
+EXTRA_DIST+= $(top_srcdir)/usr.sbin/smtpd/smtpd-api.h
+EXTRA_DIST+= $(top_srcdir)/usr.sbin/smtpd/smtpd-defines.h
+EXTRA_DIST+= $(top_srcdir)/usr.sbin/smtpd/ioev.h
+EXTRA_DIST+= $(top_srcdir)/usr.sbin/smtpd/iobuf.h
+EXTRA_DIST+= $(top_srcdir)/usr.sbin/smtpd/log.h
+EXTRA_DIST+= $(top_srcdir)/usr.sbin/smtpd/ssl.h
+EXTRA_DIST+= $(top_srcdir)/usr.sbin/smtpd/parser.h
+EXTRA_DIST+= $(top_srcdir)/usr.sbin/smtpd/dict.h
PATHSUBS= -e 's|/etc/mail/|$(sysconfdir)/|g' \
- -e 's|/var/run/smtpd.sock|$(sockdir)/smtpd.sock|g'
+ -e 's|/var/run/smtpd.sock|$(sockdir)/smtpd.sock|g' \
+ -e 's|/usr/local/libexec/smtpd/|$(pkglibexecdir)|g'
FIXPATHSCMD= $(SED) $(PATHSUBS)
$(MANPAGES): $(MANPAGES_IN)
- manpage=$(smtpd_srcdir)/`echo $@ | sed 's/\.out$$//'`; \
+ manpage=$(top_srcdir)/usr.sbin/smtpd/`echo $@ | sed 's/\.out$$//'`; \
if test "$(MANTYPE)" = "man"; then \
$(FIXPATHSCMD) $${manpage} | $(AWK) -f $(srcdir)/../mdoc2man.awk > $@; \
else \
@@ -153,8 +158,8 @@ $(MANPAGES): $(MANPAGES_IN)
fi
$(CONFIGFILES): $(CONFIGFILES_IN)
- conffile=$(smtpd_srcdir)/`echo $@ | sed 's/.out$$//'`; \
- $(CAT) $(srcdir)/$${conffile} > $@
+ conffile=$(top_srcdir)/usr.sbin/smtpd/`echo $@ | sed 's/.out$$//'`; \
+ $(CAT) $${conffile} > $@
# smtpd.conf
@@ -181,8 +186,8 @@ install-exec-hook: $(CONFIGFILES) $(MANPAGES)
uninstall-hook:
# XXX to make "make distcheck" happy we need to rm smtpd.conf
-# rm $(DESTDIR)$(sysconfdir)/smtpd.conf
- rm -f $(DESTDIR)$(mandir)/$(mansubdir)5/aliases.5 \
+ rm -f $(DESTDIR)$(sysconfdir)/smtpd.conf \
+ $(DESTDIR)$(mandir)/$(mansubdir)5/aliases.5 \
$(DESTDIR)$(mandir)/$(mansubdir)5/forward.5 \
$(DESTDIR)$(mandir)/$(mansubdir)5/table.5 \
$(DESTDIR)$(mandir)/$(mansubdir)5/smtpd.conf.5 \
diff --git a/openbsd-compat/Makefile.am b/openbsd-compat/Makefile.am
index 9d9d52af..170c2b6c 100644
--- a/openbsd-compat/Makefile.am
+++ b/openbsd-compat/Makefile.am
@@ -1,7 +1,26 @@
noinst_LIBRARIES = libopenbsd.a
+AM_CPPFLAGS = -I$(top_srcdir) -I$(top_srcdir)/usr.sbin/smtpd -I$(top_srcdir)/openbsd-compat
+
libopenbsd_a_SOURCES = empty.c
+if NEED_LIBASR
+AM_CPPFLAGS += -I$(top_srcdir)/openbsd-compat/libasr
+
+libopenbsd_a_SOURCES += libasr/asr.c
+libopenbsd_a_SOURCES += libasr/asr_debug.c
+libopenbsd_a_SOURCES += libasr/asr_compat.c
+libopenbsd_a_SOURCES += libasr/asr_utils.c
+libopenbsd_a_SOURCES += libasr/getaddrinfo_async.c
+libopenbsd_a_SOURCES += libasr/gethostnamadr_async.c
+libopenbsd_a_SOURCES += libasr/getnameinfo_async.c
+libopenbsd_a_SOURCES += libasr/getnetnamadr_async.c
+libopenbsd_a_SOURCES += libasr/res_search_async.c
+libopenbsd_a_SOURCES += libasr/res_send_async.c
+endif
+
+
+
if NEED_PROGNAME
libopenbsd_a_SOURCES += progname.c
endif
@@ -95,6 +114,10 @@ if NEED_PIDFILE
libopenbsd_a_SOURCES += pidfile.c
endif
+if NEED_PIPE2
+libopenbsd_a_SOURCES += pipe2.c
+endif
+
if NEED_REALLOCARRAY
libopenbsd_a_SOURCES += reallocarray.c
endif
@@ -103,6 +126,14 @@ if NEED_RECALLOCARRAY
libopenbsd_a_SOURCES += recallocarray.c
endif
+if NEED_RES_HNOK
+libopenbsd_a_SOURCES += res_hnok.c
+endif
+
+if NEED_RES_RANDOMID
+libopenbsd_a_SOURCES += res_randomid.c
+endif
+
if NEED_SETPROCTITLE
libopenbsd_a_SOURCES += setproctitle.c
endif
@@ -185,13 +216,19 @@ EXTRA_DIST += defines.h
EXTRA_DIST += entropy.h
EXTRA_DIST += imsg.h
EXTRA_DIST += includes.h
-EXTRA_DIST += log.h
+EXTRA_DIST += $(top_srcdir)/usr.sbin/smtpd/log.h
EXTRA_DIST += openbsd-compat.h
EXTRA_DIST += sys/queue.h
EXTRA_DIST += sys/tree.h
EXTRA_DIST += bsd-vis.h
-AM_CPPFLAGS = -I$(top_srcdir)/smtpd -I$(top_srcdir)/openbsd-compat
+if NEED_LIBASR
+EXTRA_DIST += libasr/asr_compat.h
+EXTRA_DIST += libasr/asr_private.h
+EXTRA_DIST += libasr/asr.h
+EXTRA_DIST += libasr/thread_private.h
+endif
+
if !NEED_ERR_H
AM_CPPFLAGS += -I$(top_srcdir)/openbsd-compat/err_h
diff --git a/openbsd-compat/defines.h b/openbsd-compat/defines.h
index 6a6e64a4..a6e528eb 100644
--- a/openbsd-compat/defines.h
+++ b/openbsd-compat/defines.h
@@ -34,6 +34,10 @@
# define EAUTH 80
#endif
+#ifndef INFTIM
+#define INFTIM (-1)
+#endif
+
#ifndef HOST_NAME_MAX
# ifdef _POSIX_HOST_NAME_MAX
# define HOST_NAME_MAX _POSIX_HOST_NAME_MAX
@@ -64,7 +68,7 @@
#endif
#ifndef LOGIN_NAME_MAX
-# define LOGIN_NAME_MAX 9
+# define LOGIN_NAME_MAX 32
#endif
#ifndef MAXLOGNAME
@@ -92,6 +96,10 @@
# define O_NONBLOCK 00004 /* Non Blocking Open */
#endif
+#ifndef O_EXLOCK
+#define O_EXLOCK 0
+#endif
+
#ifndef S_ISDIR
# define S_ISDIR(mode) (((mode) & (_S_IFMT)) == (_S_IFDIR))
#endif /* S_ISDIR */
@@ -214,6 +222,10 @@ typedef uint16_t in_port_t;
#endif
/* user may have set a different path */
+#if !defined(_PATH_MAILDIR)
+# define _PATH_MAILDIR "/var/spool/mail"
+#endif
+
#if defined(_PATH_MAILDIR) && defined(MAIL_DIRECTORY)
# undef _PATH_MAILDIR
#endif /* defined(_PATH_MAILDIR) && defined(MAIL_DIRECTORY) */
@@ -491,4 +503,8 @@ typedef uint16_t in_port_t;
#define LOG_PERROR 0
#endif
+#ifndef MAXDNAME
+#define MAXDNAME 1025
+#endif
+
#endif /* _DEFINES_H */
diff --git a/openbsd-compat/err_h/err.h b/openbsd-compat/err_h/err.h
index a56b6188..92aa69f6 100644
--- a/openbsd-compat/err_h/err.h
+++ b/openbsd-compat/err_h/err.h
@@ -10,6 +10,9 @@ void err(int, const char *, ...);
__attribute__ ((noreturn))
void errx(int, const char *, ...);
+__attribute__ ((noreturn))
+void errc(int, int, const char *, ...);
+
void warn(const char *, ...);
void warnx(const char *, ...);
diff --git a/openbsd-compat/explicit_bzero.c b/openbsd-compat/explicit_bzero.c
index 58c81d68..d9f4abf5 100644
--- a/openbsd-compat/explicit_bzero.c
+++ b/openbsd-compat/explicit_bzero.c
@@ -5,7 +5,6 @@
*/
#include "includes.h"
-#ifndef HAVE_EXPLICIT_BZERO
#include <string.h>
@@ -14,4 +13,3 @@ explicit_bzero(void *buf, size_t len)
{
memset(buf, 0, len);
}
-#endif
diff --git a/openbsd-compat/fgetln.c b/openbsd-compat/fgetln.c
index 6c3290d0..1c51ff78 100644
--- a/openbsd-compat/fgetln.c
+++ b/openbsd-compat/fgetln.c
@@ -18,6 +18,8 @@
* portable fgetln() version, NOT reentrant
*/
+#include "includes.h"
+
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
diff --git a/openbsd-compat/freezero.c b/openbsd-compat/freezero.c
index 9b64e2b0..da20d132 100644
--- a/openbsd-compat/freezero.c
+++ b/openbsd-compat/freezero.c
@@ -22,6 +22,7 @@
#include "includes.h"
#include <stdlib.h>
+#include <strings.h>
void
freezero(void *ptr, size_t sz)
diff --git a/openbsd-compat/libasr/asr.c b/openbsd-compat/libasr/asr.c
new file mode 100644
index 00000000..008544ad
--- /dev/null
+++ b/openbsd-compat/libasr/asr.c
@@ -0,0 +1,869 @@
+/* $OpenBSD: asr.c,v 1.61 2018/10/22 17:31:24 krw Exp $ */
+/*
+ * Copyright (c) 2010-2012 Eric Faurot <eric@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "includes.h"
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <arpa/nameser.h>
+#include <netdb.h>
+
+#include <asr.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <resolv.h>
+#include <poll.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#include <limits.h>
+
+#include "asr_private.h"
+
+#include "thread_private.h"
+
+#define DEFAULT_CONF "lookup file\n"
+#define DEFAULT_LOOKUP "lookup bind file"
+
+#define RELOAD_DELAY 15 /* seconds */
+
+static void asr_check_reload(struct asr *);
+static struct asr_ctx *asr_ctx_create(void);
+static void asr_ctx_ref(struct asr_ctx *);
+static void asr_ctx_free(struct asr_ctx *);
+static int asr_ctx_add_searchdomain(struct asr_ctx *, const char *);
+static int asr_ctx_from_file(struct asr_ctx *, const char *);
+static int asr_ctx_from_string(struct asr_ctx *, const char *);
+static int asr_ctx_parse(struct asr_ctx *, const char *);
+static int asr_parse_nameserver(struct sockaddr *, const char *);
+static int asr_ndots(const char *);
+static void pass0(char **, int, struct asr_ctx *);
+static int strsplit(char *, char **, int);
+static void asr_ctx_envopts(struct asr_ctx *);
+static void *__THREAD_NAME(_asr);
+
+static struct asr *_asr = NULL;
+
+#ifndef HAVE_ISSETUGID
+#define issetugid() ((getuid() != geteuid()))
+#endif
+
+/* Allocate and configure an async "resolver". */
+static void *
+_asr_resolver(void)
+{
+ static int init = 0;
+ struct asr *asr;
+
+ if (init == 0) {
+#ifdef DEBUG
+ if (getenv("ASR_DEBUG"))
+ _asr_debug = stderr;
+#endif
+ init = 1;
+ }
+
+ if ((asr = calloc(1, sizeof(*asr))) == NULL)
+ goto fail;
+
+ asr_check_reload(asr);
+ if (asr->a_ctx == NULL) {
+ if ((asr->a_ctx = asr_ctx_create()) == NULL)
+ goto fail;
+ if (asr_ctx_from_string(asr->a_ctx, DEFAULT_CONF) == -1)
+ goto fail;
+ asr_ctx_envopts(asr->a_ctx);
+ }
+
+#ifdef DEBUG
+ _asr_dump_config(_asr_debug, asr);
+#endif
+ return (asr);
+
+ fail:
+ if (asr) {
+ if (asr->a_ctx)
+ asr_ctx_free(asr->a_ctx);
+ free(asr);
+ }
+
+ return (NULL);
+}
+
+/*
+ * Free the "asr" async resolver (or the thread-local resolver if NULL).
+ * Drop the reference to the current context.
+ */
+void
+_asr_resolver_done(void *arg)
+{
+ struct asr *asr = arg;
+ struct asr **priv;
+
+ if (asr == NULL) {
+ priv = _THREAD_PRIVATE(_asr, _asr, &_asr);
+ if (*priv == NULL)
+ return;
+ asr = *priv;
+ *priv = NULL;
+ }
+
+ _asr_ctx_unref(asr->a_ctx);
+ free(asr);
+}
+
+/*
+ * Cancel an async query.
+ */
+void
+asr_abort(struct asr_query *as)
+{
+ _asr_async_free(as);
+}
+
+/*
+ * Resume the "as" async query resolution. Return one of ASYNC_COND,
+ * or ASYNC_DONE and put query-specific return values in the user-allocated
+ * memory at "ar".
+ */
+int
+asr_run(struct asr_query *as, struct asr_result *ar)
+{
+ int r, saved_errno = errno;
+
+ memset(ar, 0, sizeof(*ar));
+
+ DPRINT("asr: asr_run(%p, %p) %s ctx=[%p]\n", as, ar,
+ _asr_querystr(as->as_type), as->as_ctx);
+ r = as->as_run(as, ar);
+
+ DPRINT("asr: asr_run(%p, %p) -> %s", as, ar, _asr_transitionstr(r));
+#ifdef DEBUG
+ if (r == ASYNC_COND)
+#endif
+ DPRINT(" fd=%i timeout=%i", ar->ar_fd, ar->ar_timeout);
+ DPRINT("\n");
+ if (r == ASYNC_DONE)
+ _asr_async_free(as);
+
+ errno = saved_errno;
+
+ return (r);
+}
+DEF_WEAK(asr_run);
+
+static int
+poll_intrsafe(struct pollfd *fds, nfds_t nfds, int timeout)
+{
+ struct timespec pollstart, pollend, elapsed;
+ int r;
+
+ if (clock_gettime(CLOCK_MONOTONIC, &pollstart))
+ return -1;
+
+ while ((r = poll(fds, 1, timeout)) == -1 && errno == EINTR) {
+ if (clock_gettime(CLOCK_MONOTONIC, &pollend))
+ return -1;
+ timespecsub(&pollend, &pollstart, &elapsed);
+ timeout -= elapsed.tv_sec * 1000 + elapsed.tv_nsec / 1000000;
+ if (timeout < 1)
+ return 0;
+ }
+
+ return r;
+}
+
+/*
+ * Same as asr_run, but run in a loop that handles the fd conditions result.
+ */
+int
+asr_run_sync(struct asr_query *as, struct asr_result *ar)
+{
+ struct pollfd fds[1];
+ int r, saved_errno = errno;
+
+ while ((r = asr_run(as, ar)) == ASYNC_COND) {
+ fds[0].fd = ar->ar_fd;
+ fds[0].events = (ar->ar_cond == ASR_WANT_READ) ? POLLIN:POLLOUT;
+
+ if (poll_intrsafe(fds, 1, ar->ar_timeout) == -1) {
+ memset(ar, 0, sizeof(*ar));
+ ar->ar_errno = errno;
+ ar->ar_h_errno = NETDB_INTERNAL;
+ ar->ar_gai_errno = EAI_SYSTEM;
+ ar->ar_rrset_errno = NETDB_INTERNAL;
+ _asr_async_free(as);
+ errno = saved_errno;
+ return ASYNC_DONE;
+ }
+
+ /*
+ * Otherwise, just ignore the error and let asr_run()
+ * catch the failure.
+ */
+ }
+
+ errno = saved_errno;
+
+ return (r);
+}
+DEF_WEAK(asr_run_sync);
+
+/*
+ * Create a new async request of the given "type" on the async context "ac".
+ * Take a reference on it so it does not get deleted while the async query
+ * is running.
+ */
+struct asr_query *
+_asr_async_new(struct asr_ctx *ac, int type)
+{
+ struct asr_query *as;
+
+ DPRINT("asr: asr_async_new(ctx=%p) type=%i refcount=%i\n", ac, type,
+ ac ? ac->ac_refcount : 0);
+ if (ac == NULL || (as = calloc(1, sizeof(*as))) == NULL)
+ return (NULL);
+
+ ac->ac_refcount += 1;
+ as->as_ctx = ac;
+ as->as_fd = -1;
+ as->as_type = type;
+ as->as_state = ASR_STATE_INIT;
+
+ return (as);
+}
+
+/*
+ * Free an async query and unref the associated context.
+ */
+void
+_asr_async_free(struct asr_query *as)
+{
+ DPRINT("asr: asr_async_free(%p)\n", as);
+
+ if (as->as_subq)
+ _asr_async_free(as->as_subq);
+
+ switch (as->as_type) {
+ case ASR_SEND:
+ if (as->as_fd != -1)
+ close(as->as_fd);
+ if (as->as.dns.obuf && !(as->as_flags & ASYNC_EXTOBUF))
+ free(as->as.dns.obuf);
+ if (as->as.dns.ibuf)
+ free(as->as.dns.ibuf);
+ if (as->as.dns.dname)
+ free(as->as.dns.dname);
+ break;
+
+ case ASR_SEARCH:
+ if (as->as.search.name)
+ free(as->as.search.name);
+ break;
+
+ case ASR_GETRRSETBYNAME:
+ if (as->as.rrset.name)
+ free(as->as.rrset.name);
+ break;
+
+ case ASR_GETHOSTBYNAME:
+ case ASR_GETHOSTBYADDR:
+ if (as->as.hostnamadr.name)
+ free(as->as.hostnamadr.name);
+ break;
+
+ case ASR_GETADDRINFO:
+ if (as->as.ai.aifirst)
+ freeaddrinfo(as->as.ai.aifirst);
+ if (as->as.ai.hostname)
+ free(as->as.ai.hostname);
+ if (as->as.ai.servname)
+ free(as->as.ai.servname);
+ if (as->as.ai.fqdn)
+ free(as->as.ai.fqdn);
+ break;
+
+ case ASR_GETNAMEINFO:
+ break;
+ }
+
+ _asr_ctx_unref(as->as_ctx);
+ free(as);
+}
+
+/*
+ * Get a context from the given resolver. This takes a new reference to
+ * the returned context, which *must* be explicitly dropped when done
+ * using this context.
+ */
+struct asr_ctx *
+_asr_use_resolver(void *arg)
+{
+ struct asr *asr = arg;
+ struct asr **priv;
+
+ if (asr == NULL) {
+ DPRINT("using thread-local resolver\n");
+ priv = _THREAD_PRIVATE(_asr, _asr, &_asr);
+ if (*priv == NULL) {
+ DPRINT("setting up thread-local resolver\n");
+ *priv = _asr_resolver();
+ }
+ asr = *priv;
+ }
+ if (asr != NULL) {
+ asr_check_reload(asr);
+ asr_ctx_ref(asr->a_ctx);
+ return (asr->a_ctx);
+ }
+ return (NULL);
+}
+
+static void
+asr_ctx_ref(struct asr_ctx *ac)
+{
+ DPRINT("asr: asr_ctx_ref(ctx=%p) refcount=%i\n", ac, ac->ac_refcount);
+ ac->ac_refcount += 1;
+}
+
+/*
+ * Drop a reference to an async context, freeing it if the reference
+ * count drops to 0.
+ */
+void
+_asr_ctx_unref(struct asr_ctx *ac)
+{
+ DPRINT("asr: asr_ctx_unref(ctx=%p) refcount=%i\n", ac,
+ ac ? ac->ac_refcount : 0);
+ if (ac == NULL)
+ return;
+ if (--ac->ac_refcount)
+ return;
+
+ asr_ctx_free(ac);
+}
+
+static void
+asr_ctx_free(struct asr_ctx *ac)
+{
+ int i;
+
+ if (ac->ac_domain)
+ free(ac->ac_domain);
+ for (i = 0; i < ASR_MAXNS; i++)
+ free(ac->ac_ns[i]);
+ for (i = 0; i < ASR_MAXDOM; i++)
+ free(ac->ac_dom[i]);
+
+ free(ac);
+}
+
+/*
+ * Reload the configuration file if it has changed on disk.
+ */
+static void
+asr_check_reload(struct asr *asr)
+{
+ struct asr_ctx *ac;
+ struct stat st;
+ struct timespec ts;
+ pid_t pid;
+
+ pid = getpid();
+ if (pid != asr->a_pid) {
+ asr->a_pid = pid;
+ asr->a_rtime = 0;
+ }
+
+ if (clock_gettime(CLOCK_MONOTONIC, &ts) == -1)
+ return;
+
+ if ((ts.tv_sec - asr->a_rtime) < RELOAD_DELAY && asr->a_rtime != 0)
+ return;
+ asr->a_rtime = ts.tv_sec;
+
+ DPRINT("asr: checking for update of \"%s\"\n", _PATH_RESCONF);
+ if (stat(_PATH_RESCONF, &st) == -1 ||
+ asr->a_mtime == st.st_mtime ||
+ (ac = asr_ctx_create()) == NULL)
+ return;
+ asr->a_mtime = st.st_mtime;
+
+ DPRINT("asr: reloading config file\n");
+ if (asr_ctx_from_file(ac, _PATH_RESCONF) == -1) {
+ asr_ctx_free(ac);
+ return;
+ }
+
+ asr_ctx_envopts(ac);
+ if (asr->a_ctx)
+ _asr_ctx_unref(asr->a_ctx);
+ asr->a_ctx = ac;
+}
+
+/*
+ * Construct a fully-qualified domain name for the given name and domain.
+ * If "name" ends with a '.' it is considered as a FQDN by itself.
+ * Otherwise, the domain, which must be a FQDN, is appended to "name" (it
+ * may have a leading dot which would be ignored). If the domain is null,
+ * then "." is used. Return the length of the constructed FQDN or (0) on
+ * error.
+ */
+size_t
+_asr_make_fqdn(const char *name, const char *domain, char *buf, size_t buflen)
+{
+ size_t len;
+
+ if (domain == NULL)
+ domain = ".";
+ else if ((len = strlen(domain)) == 0)
+ return (0);
+ else if (domain[len -1] != '.')
+ return (0);
+
+ len = strlen(name);
+ if (len == 0) {
+ if (strlcpy(buf, domain, buflen) >= buflen)
+ return (0);
+ } else if (name[len - 1] != '.') {
+ if (domain[0] == '.')
+ domain += 1;
+ if (strlcpy(buf, name, buflen) >= buflen ||
+ strlcat(buf, ".", buflen) >= buflen ||
+ strlcat(buf, domain, buflen) >= buflen)
+ return (0);
+ } else {
+ if (strlcpy(buf, name, buflen) >= buflen)
+ return (0);
+ }
+
+ return (strlen(buf));
+}
+
+/*
+ * Count the dots in a string.
+ */
+static int
+asr_ndots(const char *s)
+{
+ int n;
+
+ for (n = 0; *s; s++)
+ if (*s == '.')
+ n += 1;
+
+ return (n);
+}
+
+/*
+ * Allocate a new empty context.
+ */
+static struct asr_ctx *
+asr_ctx_create(void)
+{
+ struct asr_ctx *ac;
+
+ if ((ac = calloc(1, sizeof(*ac))) == NULL)
+ return (NULL);
+
+ ac->ac_options = RES_RECURSE | RES_DEFNAMES | RES_DNSRCH;
+ ac->ac_refcount = 1;
+ ac->ac_ndots = 1;
+#ifndef ASR_IPV4_BEFORE_IPV6
+ ac->ac_family[0] = AF_INET6;
+ ac->ac_family[1] = AF_INET;
+#else
+ ac->ac_family[0] = AF_INET;
+ ac->ac_family[1] = AF_INET6;
+#endif
+ ac->ac_family[2] = -1;
+
+ ac->ac_nscount = 0;
+ ac->ac_nstimeout = 5;
+ ac->ac_nsretries = 4;
+
+ return (ac);
+}
+
+struct asr_ctx *
+_asr_no_resolver(void)
+{
+ return asr_ctx_create();
+}
+
+/*
+ * Add a search domain to the async context.
+ */
+static int
+asr_ctx_add_searchdomain(struct asr_ctx *ac, const char *domain)
+{
+ char buf[MAXDNAME];
+
+ if (ac->ac_domcount == ASR_MAXDOM)
+ return (-1);
+
+ if (_asr_make_fqdn(domain, NULL, buf, sizeof(buf)) == 0)
+ return (-1);
+
+ if ((ac->ac_dom[ac->ac_domcount] = strdup(buf)) == NULL)
+ return (0);
+
+ ac->ac_domcount += 1;
+
+ return (1);
+}
+
+static int
+strsplit(char *line, char **tokens, int ntokens)
+{
+ int ntok;
+ char *cp, **tp;
+
+ for (cp = line, tp = tokens, ntok = 0;
+ ntok < ntokens && (*tp = strsep(&cp, " \t")) != NULL; )
+ if (**tp != '\0') {
+ tp++;
+ ntok++;
+ }
+
+ return (ntok);
+}
+
+/*
+ * Pass on a split config line.
+ */
+static void
+pass0(char **tok, int n, struct asr_ctx *ac)
+{
+ int i, j, d;
+ const char *e;
+ struct sockaddr_storage ss;
+
+ if (!strcmp(tok[0], "nameserver")) {
+ if (ac->ac_nscount == ASR_MAXNS)
+ return;
+ if (n != 2)
+ return;
+ if (asr_parse_nameserver((struct sockaddr *)&ss, tok[1]))
+ return;
+ if ((ac->ac_ns[ac->ac_nscount] = calloc(1, SS_LEN(&ss))) == NULL)
+ return;
+ memmove(ac->ac_ns[ac->ac_nscount], &ss, SS_LEN(&ss));
+ ac->ac_nscount += 1;
+
+ } else if (!strcmp(tok[0], "domain")) {
+ if (n != 2)
+ return;
+ if (ac->ac_domain)
+ return;
+ ac->ac_domain = strdup(tok[1]);
+
+ } else if (!strcmp(tok[0], "lookup")) {
+ /* ensure that each lookup is only given once */
+ for (i = 1; i < n; i++)
+ for (j = i + 1; j < n; j++)
+ if (!strcmp(tok[i], tok[j]))
+ return;
+ ac->ac_dbcount = 0;
+ for (i = 1; i < n && ac->ac_dbcount < ASR_MAXDB; i++) {
+ if (!strcmp(tok[i], "yp")) {
+ /* silently deprecated */
+ } else if (!strcmp(tok[i], "bind"))
+ ac->ac_db[ac->ac_dbcount++] = ASR_DB_DNS;
+ else if (!strcmp(tok[i], "file"))
+ ac->ac_db[ac->ac_dbcount++] = ASR_DB_FILE;
+ }
+ } else if (!strcmp(tok[0], "search")) {
+ /* resolv.conf says the last line wins */
+ for (i = 0; i < ASR_MAXDOM; i++) {
+ free(ac->ac_dom[i]);
+ ac->ac_dom[i] = NULL;
+ }
+ ac->ac_domcount = 0;
+ for (i = 1; i < n; i++)
+ asr_ctx_add_searchdomain(ac, tok[i]);
+
+ } else if (!strcmp(tok[0], "family")) {
+ if (n == 1 || n > 3)
+ return;
+ for (i = 1; i < n; i++)
+ if (strcmp(tok[i], "inet4") && strcmp(tok[i], "inet6"))
+ return;
+ for (i = 1; i < n; i++)
+ ac->ac_family[i - 1] = strcmp(tok[i], "inet4") ? \
+ AF_INET6 : AF_INET;
+ ac->ac_family[i - 1] = -1;
+
+ } else if (!strcmp(tok[0], "options")) {
+ for (i = 1; i < n; i++) {
+ if (!strcmp(tok[i], "tcp"))
+ ac->ac_options |= RES_USEVC;
+ else if (!strcmp(tok[i], "edns0"))
+ ac->ac_options |= RES_USE_EDNS0;
+ else if ((!strncmp(tok[i], "ndots:", 6))) {
+ e = NULL;
+ d = strtonum(tok[i] + 6, 1, 16, &e);
+ if (e == NULL)
+ ac->ac_ndots = d;
+ }
+ }
+ }
+}
+
+/*
+ * Setup an async context with the config specified in the string "str".
+ */
+static int
+asr_ctx_from_string(struct asr_ctx *ac, const char *str)
+{
+ char buf[512], *ch;
+
+ asr_ctx_parse(ac, str);
+
+ if (ac->ac_dbcount == 0) {
+ /* No lookup directive */
+ asr_ctx_parse(ac, DEFAULT_LOOKUP);
+ }
+
+ if (ac->ac_nscount == 0)
+ asr_ctx_parse(ac, "nameserver 127.0.0.1");
+
+ if (ac->ac_domain == NULL)
+ if (gethostname(buf, sizeof buf) == 0) {
+ ch = strchr(buf, '.');
+ if (ch)
+ ac->ac_domain = strdup(ch + 1);
+ else /* Assume root. see resolv.conf(5) */
+ ac->ac_domain = strdup("");
+ }
+
+ /* If no search domain was specified, use the local subdomains */
+ if (ac->ac_domcount == 0)
+ for (ch = ac->ac_domain; ch; ) {
+ asr_ctx_add_searchdomain(ac, ch);
+ ch = strchr(ch, '.');
+ if (ch && asr_ndots(++ch) == 0)
+ break;
+ }
+
+ return (0);
+}
+
+/*
+ * Setup the "ac" async context from the file at location "path".
+ */
+static int
+asr_ctx_from_file(struct asr_ctx *ac, const char *path)
+{
+ FILE *cf;
+ char buf[4096];
+ ssize_t r;
+
+ cf = fopen(path, "re");
+ if (cf == NULL)
+ return (-1);
+
+ r = fread(buf, 1, sizeof buf - 1, cf);
+ if (feof(cf) == 0) {
+ DPRINT("asr: config file too long: \"%s\"\n", path);
+ r = -1;
+ }
+ fclose(cf);
+ if (r == -1)
+ return (-1);
+ buf[r] = '\0';
+
+ return asr_ctx_from_string(ac, buf);
+}
+
+/*
+ * Parse lines in the configuration string. For each one, split it into
+ * tokens and pass them to "pass0" for processing.
+ */
+static int
+asr_ctx_parse(struct asr_ctx *ac, const char *str)
+{
+ size_t len;
+ const char *line;
+ char buf[1024];
+ char *tok[10];
+ int ntok;
+
+ line = str;
+ while (*line) {
+ len = strcspn(line, "\n\0");
+ if (len < sizeof buf) {
+ memmove(buf, line, len);
+ buf[len] = '\0';
+ } else
+ buf[0] = '\0';
+ line += len;
+ if (*line == '\n')
+ line++;
+ buf[strcspn(buf, ";#")] = '\0';
+ if ((ntok = strsplit(buf, tok, 10)) == 0)
+ continue;
+
+ pass0(tok, ntok, ac);
+ }
+
+ return (0);
+}
+
+/*
+ * Check for environment variables altering the configuration as described
+ * in resolv.conf(5). Although not documented there, this feature is disabled
+ * for setuid/setgid programs.
+ */
+static void
+asr_ctx_envopts(struct asr_ctx *ac)
+{
+ char buf[4096], *e;
+ size_t s;
+
+ if (issetugid()) {
+ ac->ac_options |= RES_NOALIASES;
+ return;
+ }
+
+ if ((e = getenv("RES_OPTIONS")) != NULL) {
+ strlcpy(buf, "options ", sizeof buf);
+ strlcat(buf, e, sizeof buf);
+ s = strlcat(buf, "\n", sizeof buf);
+ if (s < sizeof buf)
+ asr_ctx_parse(ac, buf);
+ }
+
+ if ((e = getenv("LOCALDOMAIN")) != NULL) {
+ strlcpy(buf, "search ", sizeof buf);
+ strlcat(buf, e, sizeof buf);
+ s = strlcat(buf, "\n", sizeof buf);
+ if (s < sizeof buf)
+ asr_ctx_parse(ac, buf);
+ }
+}
+
+/*
+ * Parse a resolv.conf(5) nameserver string into a sockaddr.
+ */
+static int
+asr_parse_nameserver(struct sockaddr *sa, const char *s)
+{
+ in_port_t portno = 53;
+
+ if (_asr_sockaddr_from_str(sa, PF_UNSPEC, s) == -1)
+ return (-1);
+
+ if (sa->sa_family == PF_INET)
+ ((struct sockaddr_in *)sa)->sin_port = htons(portno);
+ else if (sa->sa_family == PF_INET6)
+ ((struct sockaddr_in6 *)sa)->sin6_port = htons(portno);
+
+ return (0);
+}
+
+/*
+ * Turn a (uncompressed) DNS domain name into a regular nul-terminated string
+ * where labels are separated by dots. The result is put into the "buf" buffer,
+ * truncated if it exceeds "max" chars. The function returns "buf".
+ */
+char *
+_asr_strdname(const char *_dname, char *buf, size_t max)
+{
+ const unsigned char *dname = _dname;
+ char *res;
+ size_t left, n, count;
+
+ if (_dname[0] == 0) {
+ strlcpy(buf, ".", max);
+ return buf;
+ }
+
+ res = buf;
+ left = max - 1;
+ for (n = 0; dname[0] && left; n += dname[0]) {
+ count = (dname[0] < (left - 1)) ? dname[0] : (left - 1);
+ memmove(buf, dname + 1, count);
+ dname += dname[0] + 1;
+ left -= count;
+ buf += count;
+ if (left) {
+ left -= 1;
+ *buf++ = '.';
+ }
+ }
+ buf[0] = 0;
+
+ return (res);
+}
+
+/*
+ * Read and split the next line from the given namedb file.
+ * Return -1 on error, or put the result in the "tokens" array of
+ * size "ntoken" and returns the number of token on the line.
+ */
+int
+_asr_parse_namedb_line(FILE *file, char **tokens, int ntoken, char *lbuf, size_t sz)
+{
+ size_t len;
+ char *buf;
+ int ntok;
+
+ again:
+ if ((buf = fgetln(file, &len)) == NULL)
+ return (-1);
+
+ if (len >= sz)
+ goto again;
+
+ if (buf[len - 1] == '\n')
+ len--;
+ else {
+ memcpy(lbuf, buf, len);
+ buf = lbuf;
+ }
+
+ buf[len] = '\0';
+ buf[strcspn(buf, "#")] = '\0';
+ if ((ntok = strsplit(buf, tokens, ntoken)) == 0)
+ goto again;
+
+ return (ntok);
+}
+
+/*
+ * Update the async context so that it uses the next configured DB.
+ * Return 0 on success, or -1 if no more DBs is available.
+ */
+int
+_asr_iter_db(struct asr_query *as)
+{
+ if (as->as_db_idx >= as->as_ctx->ac_dbcount) {
+ DPRINT("asr_iter_db: done\n");
+ return (-1);
+ }
+
+ as->as_db_idx += 1;
+ DPRINT("asr_iter_db: %i\n", as->as_db_idx);
+
+ return (0);
+}
diff --git a/openbsd-compat/libasr/asr.h b/openbsd-compat/libasr/asr.h
new file mode 100644
index 00000000..e9725e6b
--- /dev/null
+++ b/openbsd-compat/libasr/asr.h
@@ -0,0 +1,95 @@
+/* $OpenBSD: asr.h,v 1.1 2014/03/26 18:13:15 eric Exp $ */
+/*
+ * Copyright (c) 2012-2014 Eric Faurot <eric@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
+ * Expected fd conditions
+ */
+#define ASR_WANT_READ 1
+#define ASR_WANT_WRITE 2
+
+/*
+ * Structure through which asynchronous query results are returned when
+ * calling asr_run().
+ */
+struct asr_result {
+ /* Fields set if the query is not done yet (asr_run returns 0) */
+ int ar_cond; /* ASR_WANT_READ or ASR_WANT_WRITE */
+ int ar_fd; /* the fd waiting for io condition */
+ int ar_timeout; /* time to wait for in milliseconds */
+
+ /* Error fields. Depends on the query type. */
+ int ar_errno;
+ int ar_h_errno;
+ int ar_gai_errno;
+ int ar_rrset_errno;
+
+ /* Result for res_*_async() calls */
+ int ar_count; /* number of answers in the dns reply */
+ int ar_rcode; /* response code in the dns reply */
+ void *ar_data; /* raw reply packet (must be freed) */
+ int ar_datalen; /* reply packet length */
+ struct sockaddr_storage ar_ns; /* nameserver that responded */
+
+ /* Result for other calls. Must be freed properly. */
+ struct addrinfo *ar_addrinfo;
+ struct rrsetinfo *ar_rrsetinfo;
+ struct hostent *ar_hostent;
+ struct netent *ar_netent;
+};
+
+/*
+ * Asynchronous query management.
+ */
+
+/* Forward declaration. The API uses opaque pointers as query handles. */
+struct asr_query;
+
+int asr_run(struct asr_query *, struct asr_result *);
+int asr_run_sync(struct asr_query *, struct asr_result *);
+void asr_abort(struct asr_query *);
+
+/*
+ * Asynchronous version of the resolver functions. Similar prototypes, with
+ * an extra context parameter at the end which must currently be set to NULL.
+ * All functions return a handle suitable for use with the management functions
+ * above.
+ */
+struct asr_query *res_send_async(const unsigned char *, int, void *);
+struct asr_query *res_query_async(const char *, int, int, void *);
+struct asr_query *res_search_async(const char *, int, int, void *);
+
+struct asr_query *getrrsetbyname_async(const char *, unsigned int, unsigned int,
+ unsigned int, void *);
+
+struct asr_query *gethostbyname_async(const char *, void *);
+struct asr_query *gethostbyname2_async(const char *, int, void *);
+struct asr_query *gethostbyaddr_async(const void *, socklen_t, int, void *);
+
+struct asr_query *getnetbyname_async(const char *, void *);
+struct asr_query *getnetbyaddr_async(in_addr_t, int, void *);
+
+struct asr_query *getaddrinfo_async(const char *, const char *,
+ const struct addrinfo *, void *);
+struct asr_query *getnameinfo_async(const struct sockaddr *, socklen_t, char *,
+ size_t, char *, size_t, int, void *);
+
+/* only there for -portable */
+void asr_freeaddrinfo(struct addrinfo *);
+
+/* from in event.h */
+struct event_asr * event_asr_run(struct asr_query *,
+ void (*)(struct asr_result *, void *), void *);
diff --git a/openbsd-compat/libasr/asr_compat.c b/openbsd-compat/libasr/asr_compat.c
new file mode 100644
index 00000000..ee958357
--- /dev/null
+++ b/openbsd-compat/libasr/asr_compat.c
@@ -0,0 +1,102 @@
+/* $OpenBSD: asr_debug.c,v 1.25 2018/04/28 15:16:49 schwarze Exp $ */
+/*
+ * Copyright (c) 2012 Eric Faurot <eric@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "includes.h"
+
+#include <arpa/nameser.h>
+#ifdef HAVE_ARPA_NAMESER_COMPAT_H
+#include <arpa/nameser_compat.h>
+#endif
+
+#include "asr_compat.h"
+
+#ifndef HAVE___P_CLASS
+const char *
+__p_class(int c)
+{
+ switch(c) {
+ case C_IN: return "IN";
+ case C_CHAOS: return "CHAOS";
+ case C_HS: return "HESIOD";
+ case C_ANY: return "ANY";
+ default: return "?";
+ }
+};
+#endif /* !HAVE___P_CLASS */
+
+#ifndef HAVE___P_TYPE
+const char *
+__p_type(int t)
+{
+ switch(t) {
+ case T_A: return "A";
+ case T_NS: return "NS";
+ case T_MD: return "MD";
+ case T_MF: return "MF";
+ case T_CNAME: return "CNAME";
+ case T_SOA: return "SOA";
+ case T_MB: return "MB";
+ case T_MG: return "MG";
+ case T_MR: return "MR";
+ case T_NULL: return "NULL";
+ case T_WKS: return "WKS";
+ case T_PTR: return "PTR";
+ case T_HINFO: return "HINFO";
+ case T_MINFO: return "MINFO";
+ case T_MX: return "MX";
+ case T_TXT: return "TXT";
+ case T_RP: return "RP";
+ case T_AFSDB: return "AFSDB";
+ case T_X25: return "X25";
+ case T_ISDN: return "ISDN";
+ case T_RT: return "RT";
+ case T_NSAP: return "NSAP";
+ case T_NSAP_PTR:return"NSAP_PTR";
+ case T_SIG: return "SIG";
+ case T_KEY: return "KEY";
+ case T_PX: return "PX";
+ case T_GPOS: return "GPOS";
+ case T_AAAA: return "AAAA";
+ case T_LOC: return "LOC";
+ case T_NXT: return "NXT";
+ case T_EID: return "EID";
+ case T_NIMLOC: return "NIMLOC";
+ case T_SRV: return "SRV";
+ case T_ATMA: return "ATMA";
+ case T_OPT: return "OPT";
+ case T_IXFR: return "IXFR";
+ case T_AXFR: return "AXFR";
+ case T_MAILB: return "MAILB";
+ case T_MAILA: return "MAILA";
+#ifdef T_UINFO
+ case T_UINFO: return "UINFO";
+#endif
+#ifdef T_UID
+ case T_UID: return "UID";
+#endif
+#ifdef T_GID
+ case T_GID: return "GID";
+#endif
+ case T_NAPTR: return "NAPTR";
+#ifdef T_UNSPEC
+ case T_UNSPEC: return "UNSPEC";
+#endif
+ case T_ANY: return "ANY";
+ default: return "?";
+ }
+}
+#endif /* !HAVE___P_TYPE */
diff --git a/openbsd-compat/libasr/asr_compat.h b/openbsd-compat/libasr/asr_compat.h
new file mode 100644
index 00000000..2c7686a4
--- /dev/null
+++ b/openbsd-compat/libasr/asr_compat.h
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2018 Eric Faurot <eric@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+
+/* source compat */
+#define ASR_BUFSIZ 1024
+
+#define DEF_WEAK(x)
+#define __THREAD_NAME(x) __thread_name_ ## x
+
+#ifndef __BEGIN_HIDDEN_DECLS
+#define __BEGIN_HIDDEN_DECLS
+#endif
+#ifndef __END_HIDDEN_DECLS
+#define __END_HIDDEN_DECLS
+#endif
+
+/*
+ * netdb.h
+ */
+#ifndef NETDB_SUCCESS
+#define NETDB_SUCCESS 0
+#endif
+
+#ifndef NETDB_INTERNAL
+#define NETDB_INTERNAL -1
+#endif
+
+#ifndef AI_FQDN
+#define AI_FQDN AI_CANONNAME
+#endif
+
+#ifndef AI_MASK
+#define AI_MASK \
+ (AI_PASSIVE | AI_CANONNAME | AI_NUMERICHOST | AI_NUMERICSERV | AI_ADDRCONFIG | AI_FQDN)
+#endif
+
+#ifndef SCOPE_DELIMITER
+#define SCOPE_DELIMITER '%'
+#endif
+
+#ifndef _PATH_HOSTS
+#define _PATH_HOSTS "/etc/hosts"
+#endif
+
+#ifndef _PATH_NETWORKS
+#define _PATH_NETWORKS "/etc/networks"
+#endif
+
+/*
+ * arpa/nameserv.h
+ */
+#ifndef T_OPT
+#define T_OPT 41
+#endif
+
+#ifndef DNS_MESSAGEEXTFLAG_DO
+#define DNS_MESSAGEEXTFLAG_DO 0x8000U
+#endif
+
+#ifndef HAVE___P_CLASS
+const char * __p_class(int);
+#endif
+
+#ifndef HAVE___P_TYPE
+const char * __p_type(int);
+#endif
diff --git a/openbsd-compat/libasr/asr_debug.c b/openbsd-compat/libasr/asr_debug.c
new file mode 100644
index 00000000..be80436a
--- /dev/null
+++ b/openbsd-compat/libasr/asr_debug.c
@@ -0,0 +1,362 @@
+/* $OpenBSD: asr_debug.c,v 1.26 2019/07/03 03:24:03 deraadt Exp $ */
+/*
+ * Copyright (c) 2012 Eric Faurot <eric@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "includes.h"
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/nameser.h>
+#ifdef HAVE_ARPA_NAMESER_COMPAT_H
+#include <arpa/nameser_compat.h>
+#endif
+#include <arpa/inet.h>
+#include <netdb.h>
+
+#include <asr.h>
+#include <resolv.h>
+#include <string.h>
+
+#include "asr_private.h"
+
+static const char *rcodetostr(uint16_t);
+static const char *print_dname(const char *, char *, size_t);
+static const char *print_header(const struct asr_dns_header *, char *, size_t);
+static const char *print_query(const struct asr_dns_query *, char *, size_t);
+static const char *print_rr(const struct asr_dns_rr *, char *, size_t);
+
+FILE *_asr_debug = NULL;
+
+#define OPCODE_SHIFT 11
+
+static const char *
+rcodetostr(uint16_t v)
+{
+ switch (v) {
+ case NOERROR: return "NOERROR";
+ case FORMERR: return "FORMERR";
+ case SERVFAIL: return "SERVFAIL";
+ case NXDOMAIN: return "NXDOMAIN";
+ case NOTIMP: return "NOTIMP";
+ case REFUSED: return "REFUSED";
+ default: return "?";
+ }
+}
+
+static const char *
+print_dname(const char *_dname, char *buf, size_t max)
+{
+ return (_asr_strdname(_dname, buf, max));
+}
+
+static const char *
+print_rr(const struct asr_dns_rr *rr, char *buf, size_t max)
+{
+ char *res;
+ char tmp[256];
+ char tmp2[256];
+ int r;
+
+ res = buf;
+
+ r = snprintf(buf, max, "%s %u %s %s ",
+ print_dname(rr->rr_dname, tmp, sizeof tmp),
+ rr->rr_ttl,
+ __p_class(rr->rr_class),
+ __p_type(rr->rr_type));
+ if (r < 0 || (size_t)r >= max) {
+ buf[0] = '\0';
+ return (buf);
+ }
+
+ if ((size_t)r >= max)
+ return (buf);
+
+ max -= r;
+ buf += r;
+
+ switch (rr->rr_type) {
+ case T_CNAME:
+ print_dname(rr->rr.cname.cname, buf, max);
+ break;
+ case T_MX:
+ snprintf(buf, max, "%lu %s",
+ (unsigned long)rr->rr.mx.preference,
+ print_dname(rr->rr.mx.exchange, tmp, sizeof tmp));
+ break;
+ case T_NS:
+ print_dname(rr->rr.ns.nsname, buf, max);
+ break;
+ case T_PTR:
+ print_dname(rr->rr.ptr.ptrname, buf, max);
+ break;
+ case T_SOA:
+ snprintf(buf, max, "%s %s %lu %lu %lu %lu %lu",
+ print_dname(rr->rr.soa.rname, tmp, sizeof tmp),
+ print_dname(rr->rr.soa.mname, tmp2, sizeof tmp2),
+ (unsigned long)rr->rr.soa.serial,
+ (unsigned long)rr->rr.soa.refresh,
+ (unsigned long)rr->rr.soa.retry,
+ (unsigned long)rr->rr.soa.expire,
+ (unsigned long)rr->rr.soa.minimum);
+ break;
+ case T_A:
+ if (rr->rr_class != C_IN)
+ goto other;
+ snprintf(buf, max, "%s", inet_ntop(AF_INET,
+ &rr->rr.in_a.addr, tmp, sizeof tmp));
+ break;
+ case T_AAAA:
+ if (rr->rr_class != C_IN)
+ goto other;
+ snprintf(buf, max, "%s", inet_ntop(AF_INET6,
+ &rr->rr.in_aaaa.addr6, tmp, sizeof tmp));
+ break;
+ default:
+ other:
+ snprintf(buf, max, "(rdlen=%i)", (int)rr->rr.other.rdlen);
+ break;
+ }
+
+ return (res);
+}
+
+static const char *
+print_query(const struct asr_dns_query *q, char *buf, size_t max)
+{
+ char b[256];
+
+ snprintf(buf, max, "%s %s %s",
+ print_dname(q->q_dname, b, sizeof b),
+ __p_class(q->q_class), __p_type(q->q_type));
+
+ return (buf);
+}
+
+static const char *
+print_header(const struct asr_dns_header *h, char *buf, size_t max)
+{
+ snprintf(buf, max,
+ "id:0x%04x %s op:%i %s %s %s %s z:%i %s %s r:%s qd:%i an:%i ns:%i ar:%i",
+ ((int)h->id),
+ (h->flags & QR_MASK) ? "QR":" ",
+ (int)(OPCODE(h->flags) >> OPCODE_SHIFT),
+ (h->flags & AA_MASK) ? "AA":" ",
+ (h->flags & TC_MASK) ? "TC":" ",
+ (h->flags & RD_MASK) ? "RD":" ",
+ (h->flags & RA_MASK) ? "RA":" ",
+ (h->flags & Z_MASK),
+ (h->flags & AD_MASK) ? "AD":" ",
+ (h->flags & CD_MASK) ? "CD":" ",
+ rcodetostr(RCODE(h->flags)),
+ h->qdcount, h->ancount, h->nscount, h->arcount);
+
+ return (buf);
+}
+
+void
+_asr_dump_packet(FILE *f, const void *data, size_t len)
+{
+ char buf[1024];
+ struct asr_unpack p;
+ struct asr_dns_header h;
+ struct asr_dns_query q;
+ struct asr_dns_rr rr;
+ int i, an, ns, ar, n;
+
+ if (f == NULL)
+ return;
+
+ _asr_unpack_init(&p, data, len);
+
+ if (_asr_unpack_header(&p, &h) == -1) {
+ fprintf(f, ";; BAD PACKET: %s\n", strerror(p.err));
+ return;
+ }
+
+ fprintf(f, ";; HEADER %s\n", print_header(&h, buf, sizeof buf));
+
+ if (h.qdcount)
+ fprintf(f, ";; QUERY SECTION:\n");
+ for (i = 0; i < h.qdcount; i++) {
+ if (_asr_unpack_query(&p, &q) == -1)
+ goto error;
+ fprintf(f, "%s\n", print_query(&q, buf, sizeof buf));
+ }
+
+ an = 0;
+ ns = an + h.ancount;
+ ar = ns + h.nscount;
+ n = ar + h.arcount;
+
+ for (i = 0; i < n; i++) {
+ if (i == an)
+ fprintf(f, "\n;; ANSWER SECTION:\n");
+ if (i == ns)
+ fprintf(f, "\n;; AUTHORITY SECTION:\n");
+ if (i == ar)
+ fprintf(f, "\n;; ADDITIONAL SECTION:\n");
+
+ if (_asr_unpack_rr(&p, &rr) == -1)
+ goto error;
+ fprintf(f, "%s\n", print_rr(&rr, buf, sizeof buf));
+ }
+
+ if (p.offset != len)
+ fprintf(f, ";; REMAINING GARBAGE %zu\n", len - p.offset);
+
+ error:
+ if (p.err)
+ fprintf(f, ";; ERROR AT OFFSET %zu/%zu: %s\n", p.offset, p.len,
+ strerror(p.err));
+}
+
+const char *
+_asr_print_sockaddr(const struct sockaddr *sa, char *buf, size_t len)
+{
+ char h[256];
+ int portno;
+ union {
+ const struct sockaddr *sa;
+ const struct sockaddr_in *sin;
+ const struct sockaddr_in6 *sin6;
+ } s;
+
+ s.sa = sa;
+
+ switch (sa->sa_family) {
+ case AF_INET:
+ inet_ntop(AF_INET, &s.sin->sin_addr, h, sizeof h);
+ portno = ntohs(s.sin->sin_port);
+ break;
+ case AF_INET6:
+ inet_ntop(AF_INET6, &s.sin6->sin6_addr, h, sizeof h);
+ portno = ntohs(s.sin6->sin6_port);
+ break;
+ default:
+ snprintf(buf, len, "?");
+ return (buf);
+ }
+
+ snprintf(buf, len, "%s:%i", h, portno);
+ return (buf);
+}
+
+void
+_asr_dump_config(FILE *f, struct asr *a)
+{
+ char buf[256];
+ int i;
+ struct asr_ctx *ac;
+ unsigned int o;
+
+ if (f == NULL)
+ return;
+
+ ac = a->a_ctx;
+
+ fprintf(f, "--------- ASR CONFIG ---------------\n");
+ fprintf(f, "DOMAIN \"%s\"\n", ac->ac_domain);
+ fprintf(f, "SEARCH\n");
+ for (i = 0; i < ac->ac_domcount; i++)
+ fprintf(f, " \"%s\"\n", ac->ac_dom[i]);
+ fprintf(f, "OPTIONS\n");
+ fprintf(f, " options:");
+ o = ac->ac_options;
+
+#define PRINTOPT(flag, n) if (o & (flag)) { fprintf(f, " " n); o &= ~(flag); }
+ PRINTOPT(RES_INIT, "INIT");
+ PRINTOPT(RES_DEBUG, "DEBUG");
+ PRINTOPT(RES_USEVC, "USEVC");
+ PRINTOPT(RES_IGNTC, "IGNTC");
+ PRINTOPT(RES_RECURSE, "RECURSE");
+ PRINTOPT(RES_DEFNAMES, "DEFNAMES");
+ PRINTOPT(RES_STAYOPEN, "STAYOPEN");
+ PRINTOPT(RES_DNSRCH, "DNSRCH");
+ PRINTOPT(RES_NOALIASES, "NOALIASES");
+ PRINTOPT(RES_USE_EDNS0, "USE_EDNS0");
+ PRINTOPT(RES_USE_DNSSEC, "USE_DNSSEC");
+ if (o)
+ fprintf(f, " 0x%08x", o);
+ fprintf(f, "\n");
+
+ fprintf(f, " ndots: %i\n", ac->ac_ndots);
+ fprintf(f, " family:");
+ for (i = 0; ac->ac_family[i] != -1; i++)
+ fprintf(f, " %s", (ac->ac_family[i] == AF_INET)?"inet4":"inet6");
+ fprintf(f, "\n");
+ fprintf(f, "NAMESERVERS timeout=%i retry=%i\n",
+ ac->ac_nstimeout,
+ ac->ac_nsretries);
+ for (i = 0; i < ac->ac_nscount; i++)
+ fprintf(f, " %s\n", _asr_print_sockaddr(ac->ac_ns[i], buf,
+ sizeof buf));
+ fprintf(f, "LOOKUP %s", ac->ac_db);
+ fprintf(f, "\n------------------------------------\n");
+}
+
+#define CASE(n) case n: return #n
+
+const char *
+_asr_statestr(int state)
+{
+ switch (state) {
+ CASE(ASR_STATE_INIT);
+ CASE(ASR_STATE_NEXT_DOMAIN);
+ CASE(ASR_STATE_NEXT_DB);
+ CASE(ASR_STATE_SAME_DB);
+ CASE(ASR_STATE_NEXT_FAMILY);
+ CASE(ASR_STATE_NEXT_NS);
+ CASE(ASR_STATE_UDP_SEND);
+ CASE(ASR_STATE_UDP_RECV);
+ CASE(ASR_STATE_TCP_WRITE);
+ CASE(ASR_STATE_TCP_READ);
+ CASE(ASR_STATE_PACKET);
+ CASE(ASR_STATE_SUBQUERY);
+ CASE(ASR_STATE_NOT_FOUND);
+ CASE(ASR_STATE_HALT);
+ default:
+ return "?";
+ }
+};
+
+const char *
+_asr_querystr(int type)
+{
+ switch (type) {
+ CASE(ASR_SEND);
+ CASE(ASR_SEARCH);
+ CASE(ASR_GETRRSETBYNAME);
+ CASE(ASR_GETHOSTBYNAME);
+ CASE(ASR_GETHOSTBYADDR);
+ CASE(ASR_GETADDRINFO);
+ CASE(ASR_GETNAMEINFO);
+ default:
+ return "?";
+ }
+}
+
+const char *
+_asr_transitionstr(int type)
+{
+ switch (type) {
+ CASE(ASYNC_COND);
+ CASE(ASYNC_DONE);
+ default:
+ return "?";
+ }
+}
diff --git a/openbsd-compat/libasr/asr_private.h b/openbsd-compat/libasr/asr_private.h
new file mode 100644
index 00000000..acf0e874
--- /dev/null
+++ b/openbsd-compat/libasr/asr_private.h
@@ -0,0 +1,359 @@
+/* $OpenBSD: asr_private.h,v 1.47 2018/04/28 15:16:49 schwarze Exp $ */
+/*
+ * Copyright (c) 2012 Eric Faurot <eric@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <stdio.h>
+
+#include "asr_compat.h"
+
+#define QR_MASK (0x1 << 15)
+#define OPCODE_MASK (0xf << 11)
+#define AA_MASK (0x1 << 10)
+#define TC_MASK (0x1 << 9)
+#define RD_MASK (0x1 << 8)
+#define RA_MASK (0x1 << 7)
+#define Z_MASK (0x1 << 6)
+#define AD_MASK (0x1 << 5)
+#define CD_MASK (0x1 << 4)
+#define RCODE_MASK (0xf)
+
+#define OPCODE(v) ((v) & OPCODE_MASK)
+#define RCODE(v) ((v) & RCODE_MASK)
+
+
+struct asr_pack {
+ char *buf;
+ size_t len;
+ size_t offset;
+ int err;
+};
+
+struct asr_unpack {
+ const char *buf;
+ size_t len;
+ size_t offset;
+ int err;
+};
+
+struct asr_dns_header {
+ uint16_t id;
+ uint16_t flags;
+ uint16_t qdcount;
+ uint16_t ancount;
+ uint16_t nscount;
+ uint16_t arcount;
+};
+
+struct asr_dns_query {
+ char q_dname[MAXDNAME];
+ uint16_t q_type;
+ uint16_t q_class;
+};
+
+struct asr_dns_rr {
+ char rr_dname[MAXDNAME];
+ uint16_t rr_type;
+ uint16_t rr_class;
+ uint32_t rr_ttl;
+ union {
+ struct {
+ char cname[MAXDNAME];
+ } cname;
+ struct {
+ uint16_t preference;
+ char exchange[MAXDNAME];
+ } mx;
+ struct {
+ char nsname[MAXDNAME];
+ } ns;
+ struct {
+ char ptrname[MAXDNAME];
+ } ptr;
+ struct {
+ char mname[MAXDNAME];
+ char rname[MAXDNAME];
+ uint32_t serial;
+ uint32_t refresh;
+ uint32_t retry;
+ uint32_t expire;
+ uint32_t minimum;
+ } soa;
+ struct {
+ struct in_addr addr;
+ } in_a;
+ struct {
+ struct in6_addr addr6;
+ } in_aaaa;
+ struct {
+ uint16_t rdlen;
+ const void *rdata;
+ } other;
+ } rr;
+};
+
+
+#define ASR_MAXNS 5
+#define ASR_MAXDB 3
+#define ASR_MAXDOM 10
+
+enum async_type {
+ ASR_SEND,
+ ASR_SEARCH,
+ ASR_GETRRSETBYNAME,
+ ASR_GETHOSTBYNAME,
+ ASR_GETHOSTBYADDR,
+ ASR_GETADDRINFO,
+ ASR_GETNAMEINFO,
+};
+
+#define ASR_DB_FILE 'f'
+#define ASR_DB_DNS 'b'
+
+struct asr_ctx {
+ int ac_refcount;
+ int ac_options;
+ int ac_ndots;
+ char *ac_domain;
+ int ac_domcount;
+ char *ac_dom[ASR_MAXDOM];
+ int ac_dbcount;
+ char ac_db[ASR_MAXDB + 1];
+ int ac_family[3];
+
+ int ac_nscount;
+ int ac_nstimeout;
+ int ac_nsretries;
+ struct sockaddr *ac_ns[ASR_MAXNS];
+
+};
+
+struct asr {
+ pid_t a_pid;
+ time_t a_mtime;
+ time_t a_rtime;
+ struct asr_ctx *a_ctx;
+};
+
+#define ASYNC_COND 0
+#define ASYNC_DONE 1
+
+#define ASYNC_DOM_FQDN 0x00000001
+#define ASYNC_DOM_NDOTS 0x00000002
+#define ASYNC_DOM_DOMAIN 0x00000004
+#define ASYNC_DOM_ASIS 0x00000008
+
+#define ASYNC_NODATA 0x00000100
+#define ASYNC_AGAIN 0x00000200
+
+#define ASYNC_GETNET 0x00001000
+#define ASYNC_EXTOBUF 0x00002000
+
+#define ASYNC_NO_INET 0x00010000
+#define ASYNC_NO_INET6 0x00020000
+
+struct asr_query {
+ int (*as_run)(struct asr_query *, struct asr_result *);
+ struct asr_ctx *as_ctx;
+ int as_type;
+ int as_flags;
+ int as_state;
+
+ /* cond */
+ int as_timeout;
+ int as_fd;
+ struct asr_query *as_subq;
+
+ /* loop indices in ctx */
+ int as_dom_step;
+ int as_dom_idx;
+ int as_dom_flags;
+ int as_family_idx;
+ int as_db_idx;
+
+ int as_count;
+
+ union {
+ struct {
+ uint16_t reqid;
+ int class;
+ int type;
+ char *dname; /* not fqdn! */
+ int rcode; /* response code */
+ int ancount; /* answer count */
+
+ int nsidx;
+ int nsloop;
+
+ /* io buffers for query/response */
+ unsigned char *obuf;
+ size_t obuflen;
+ size_t obufsize;
+ unsigned char *ibuf;
+ size_t ibuflen;
+ size_t ibufsize;
+ size_t datalen; /* for tcp io */
+ uint16_t pktlen;
+ } dns;
+
+ struct {
+ int class;
+ int type;
+ char *name;
+ int saved_h_errno;
+ } search;
+
+ struct {
+ int flags;
+ int class;
+ int type;
+ char *name;
+ } rrset;
+
+ struct {
+ char *name;
+ int family;
+ char addr[16];
+ int addrlen;
+ int subq_h_errno;
+ } hostnamadr;
+
+ struct {
+ char *hostname;
+ char *servname;
+ int port_tcp;
+ int port_udp;
+ union {
+ struct sockaddr sa;
+ struct sockaddr_in sain;
+ struct sockaddr_in6 sain6;
+ } sa;
+
+ struct addrinfo hints;
+ char *fqdn;
+ struct addrinfo *aifirst;
+ struct addrinfo *ailast;
+ } ai;
+
+ struct {
+ char *hostname;
+ char *servname;
+ size_t hostnamelen;
+ size_t servnamelen;
+ union {
+ struct sockaddr sa;
+ struct sockaddr_in sain;
+ struct sockaddr_in6 sain6;
+ } sa;
+ int flags;
+ } ni;
+#define MAXTOKEN 10
+ } as;
+
+};
+
+#define AS_DB(p) ((p)->as_ctx->ac_db[(p)->as_db_idx - 1])
+#define AS_FAMILY(p) ((p)->as_ctx->ac_family[(p)->as_family_idx])
+
+enum asr_state {
+ ASR_STATE_INIT,
+ ASR_STATE_NEXT_DOMAIN,
+ ASR_STATE_NEXT_DB,
+ ASR_STATE_SAME_DB,
+ ASR_STATE_NEXT_FAMILY,
+ ASR_STATE_NEXT_NS,
+ ASR_STATE_UDP_SEND,
+ ASR_STATE_UDP_RECV,
+ ASR_STATE_TCP_WRITE,
+ ASR_STATE_TCP_READ,
+ ASR_STATE_PACKET,
+ ASR_STATE_SUBQUERY,
+ ASR_STATE_NOT_FOUND,
+ ASR_STATE_HALT,
+};
+
+#define MAXPACKETSZ 4096
+
+__BEGIN_HIDDEN_DECLS
+
+/* asr_utils.c */
+void _asr_pack_init(struct asr_pack *, char *, size_t);
+int _asr_pack_header(struct asr_pack *, const struct asr_dns_header *);
+int _asr_pack_query(struct asr_pack *, uint16_t, uint16_t, const char *);
+int _asr_pack_edns0(struct asr_pack *, uint16_t, int);
+void _asr_unpack_init(struct asr_unpack *, const char *, size_t);
+int _asr_unpack_header(struct asr_unpack *, struct asr_dns_header *);
+int _asr_unpack_query(struct asr_unpack *, struct asr_dns_query *);
+int _asr_unpack_rr(struct asr_unpack *, struct asr_dns_rr *);
+int _asr_sockaddr_from_str(struct sockaddr *, int, const char *);
+ssize_t _asr_dname_from_fqdn(const char *, char *, size_t);
+ssize_t _asr_addr_as_fqdn(const char *, int, char *, size_t);
+
+/* asr.c */
+void _asr_resolver_done(void *);
+struct asr_ctx *_asr_use_resolver(void *);
+struct asr_ctx *_asr_no_resolver(void);
+void _asr_ctx_unref(struct asr_ctx *);
+struct asr_query *_asr_async_new(struct asr_ctx *, int);
+void _asr_async_free(struct asr_query *);
+size_t _asr_make_fqdn(const char *, const char *, char *, size_t);
+char *_asr_strdname(const char *, char *, size_t);
+int _asr_iter_db(struct asr_query *);
+int _asr_parse_namedb_line(FILE *, char **, int, char *, size_t);
+
+/* *_async.c */
+struct asr_query *_res_query_async_ctx(const char *, int, int, struct asr_ctx *);
+struct asr_query *_res_search_async_ctx(const char *, int, int, struct asr_ctx *);
+struct asr_query *_gethostbyaddr_async_ctx(const void *, socklen_t, int,
+ struct asr_ctx *);
+
+int _asr_iter_domain(struct asr_query *, const char *, char *, size_t);
+
+#ifdef DEBUG
+
+#define DPRINT(...) do { if(_asr_debug) { \
+ fprintf(_asr_debug, __VA_ARGS__); \
+ } } while (0)
+#define DPRINT_PACKET(n, p, s) do { if(_asr_debug) { \
+ fprintf(_asr_debug, "----- %s -----\n", n); \
+ _asr_dump_packet(_asr_debug, (p), (s)); \
+ fprintf(_asr_debug, "--------------\n"); \
+ } } while (0)
+
+#else /* DEBUG */
+
+#define DPRINT(...)
+#define DPRINT_PACKET(...)
+
+#endif /* DEBUG */
+
+const char *_asr_querystr(int);
+const char *_asr_statestr(int);
+const char *_asr_transitionstr(int);
+const char *_asr_print_sockaddr(const struct sockaddr *, char *, size_t);
+void _asr_dump_config(FILE *, struct asr *);
+void _asr_dump_packet(FILE *, const void *, size_t);
+
+extern FILE *_asr_debug;
+
+#define async_set_state(a, s) do { \
+ DPRINT("asr: [%s@%p] %s -> %s\n", \
+ _asr_querystr((a)->as_type), \
+ as, \
+ _asr_statestr((a)->as_state), \
+ _asr_statestr((s))); \
+ (a)->as_state = (s); } while (0)
+
+__END_HIDDEN_DECLS
diff --git a/openbsd-compat/libasr/asr_run.3 b/openbsd-compat/libasr/asr_run.3
new file mode 100644
index 00000000..61c1b02c
--- /dev/null
+++ b/openbsd-compat/libasr/asr_run.3
@@ -0,0 +1,316 @@
+.\" $OpenBSD: asr_run.3,v 1.3 2017/02/18 19:23:05 jca Exp $
+.\"
+.\" Copyright (c) 2012-2014, Eric Faurot <eric@openbsd.org>
+.\"
+.\" Permission to use, copy, modify, and distribute this software for any
+.\" purpose with or without fee is hereby granted, provided that the above
+.\" copyright notice and this permission notice appear in all copies.
+.\"
+.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+.\"
+.Dd $Mdocdate: February 18 2017 $
+.Dt ASR_RUN 3
+.Os
+.Sh NAME
+.Nm asr_run ,
+.Nm asr_run_sync ,
+.Nm asr_abort ,
+.Nm res_send_async ,
+.Nm res_query_async ,
+.Nm res_search_async ,
+.Nm getrrsetbyname_async ,
+.Nm gethostbyname_async ,
+.Nm gethostbyname2_async ,
+.Nm gethostbyaddr_async ,
+.Nm getnetbyname_async ,
+.Nm getnetbyaddr_async ,
+.Nm getaddrinfo_async ,
+.Nm getnameinfo_async
+.Nd asynchronous resolver functions
+.Sh SYNOPSIS
+.In sys/types.h
+.In sys/socket.h
+.In netdb.h
+.In asr.h
+.Ft int
+.Fn asr_run "struct asr_query *aq" "struct asr_result *ar"
+.Ft int
+.Fn asr_run_sync "struct asr_query *aq" "struct asr_result *ar"
+.Ft void
+.Fn asr_abort "struct asr_query *aq"
+.Ft struct asr_query *
+.Fn res_send_async "const unsigned char *pkt" "int pktlen" "void *asr"
+.Ft struct asr_query *
+.Fn res_query_async "const char *name" "int class" "int type" "void *asr"
+.Ft struct asr_query *
+.Fn res_search_async "const char *name" "int class" "int type" "void *asr"
+.Ft struct asr_query *
+.Fn getrrsetbyname_async "const char *hostname" "unsigned int rdclass" "unsigned int rdtype" "unsigned int flags" "void *asr"
+.Ft struct asr_query *
+.Fn gethostbyname_async "const char *name" "void *asr"
+.Ft struct asr_query *
+.Fn gethostbyname2_async "const char *name" "int af" "void *asr"
+.Ft struct asr_query *
+.Fn gethostbyaddr_async "const void *addr" "socklen_t len" "int af" "void *asr"
+.Ft struct asr_query *
+.Fn getnetbyname_async "const char *name" "void *asr"
+.Ft struct asr_query *
+.Fn getnetbyaddr_async "in_addr_t net" "int type" "void *asr"
+.Ft struct asr_query *
+.Fn getaddrinfo_async "const char *hostname" "const char *servname" "const struct addrinfo *hints" "void *asr"
+.Ft struct asr_query *
+.Fn getnameinfo_async "const struct sockaddr *sa" "socklen_t salen" "char *host" "size_t hostlen" "char *serv" "size_t servlen" "int flags" "void *asr"
+.Sh DESCRIPTION
+The
+.Nm asr
+functions provide a simple interface for asynchronous address
+resolution and nameserver querying.
+They should be used in place of the classical resolver functions
+of libc when blocking is not desirable.
+.Pp
+The principle of operation is as follows:
+All async requests are made against an
+.Nm asr
+context which basically defines a list of sources to query and a
+strategy to do so.
+The user creates a query through one of the dedicated functions, and
+gets a handle representing the internal query.
+A query is a state-machine that can be run to try to fulfill a
+particular request.
+This is done by calling in a generic API that performs the state
+transitions until it needs to give the control back to the user,
+either because a result is available, or because the next transition
+implies a blocking call (a file descriptor needs to be read from or
+written to).
+The user is responsible for dealing with the situation: either get
+the result, or wait until the fd conditions are met, and then call
+back into the resolving machinery when it is ready to proceed.
+.Pp
+The
+.Fn asr_run
+function drives the resolving process.
+It runs the asynchronous query represented by the
+.Fa aq
+handle until a result is available, or until it cannot continue
+without blocking.
+The results are returned to the user through the
+.Fa ar
+parameter, which must be a valid pointer to user allocated memory.
+.Fa ar
+is defined as:
+.Bd -literal
+struct asr_result {
+
+ /* Fields set if the query is not done yet (asr_run returns 0) */
+ int ar_cond; /* ASR_WANT_READ or ASR_WANT_WRITE */
+ int ar_fd; /* the fd waiting for io condition */
+ int ar_timeout; /* time to wait for in milliseconds */
+
+ /* Error fields. Depends on the query type. */
+ int ar_errno;
+ int ar_h_errno;
+ int ar_gai_errno;
+ int ar_rrset_errno;
+
+ /* Result for res_*_async() calls */
+ int ar_count; /* number of answers in the dns reply */
+ int ar_rcode; /* response code in the dns reply */
+ void *ar_data; /* raw reply packet (must be freed) */
+ int ar_datalen; /* reply packet length */
+ struct sockaddr_storage ar_ns; /* nameserver that responded */
+
+ /* Result for other calls. Must be freed properly. */
+ struct addrinfo *ar_addrinfo;
+ struct rrsetinfo *ar_rrsetinfo;
+ struct hostent *ar_hostent;
+ struct netent *ar_netent;
+};
+.Ed
+.Pp
+The function returns one of the following values:
+.Bl -tag -width "0 " -offset indent
+.It 0
+The query cannot be processed further until a specific condition on a
+file descriptor becomes true.
+The following members of the
+.Fa ar
+structure are filled:
+.Pp
+.Bl -tag -width "ar_timeout " -compact
+.It Fa ar_cond
+one of ASR_WANT_READ or ASR_WANT_WRITE,
+.It Fa ar_fd
+the file descriptor waiting for an IO operation,
+.It Fa ar_timeout
+the amount of time to wait for in milliseconds.
+.El
+.Pp
+The caller is expected to call
+.Fn asr_run
+again once the condition holds or the timeout expires.
+.It 1
+The query is completed.
+The members relevant to the actual async query type are set accordingly,
+including error conditions.
+In any case, the query is cleared and its handle is invalidated.
+.El
+.Pp
+Note that although the query itself may fail (the error being properly reported
+in the
+.Fa ar
+structure), the
+.Fn asr_run
+function itself cannot fail and it always preserves errno.
+.Pp
+The
+.Fn asr_run_sync
+function is a wrapper around
+.Fn asr_run
+that handles the read/write conditions, thus falling back to a blocking
+interface.
+It only returns 1.
+It also preserves errno.
+.Pp
+The
+.Fn asr_abort
+function clears a running query.
+It can be called when the query is waiting on a file descriptor.
+Note that a completed query is already cleared when
+.Fn asr_run
+returns, so
+.Fn asr_abort
+must not be called in this case.
+.Pp
+The remaining functions are used to initiate different kinds of query
+on the
+.Fa asr
+resolver context.
+The specific operational details for each of them are described below.
+All functions return a handle to an internal query, or NULL if they could
+not allocate the necessary resources to initiate the query.
+All other errors (especially invalid parameters) are reported when calling
+.Fn asr_run .
+They usually have the same interface as an existing resolver function, with
+an additional
+.Ar asr
+argument, which specifies the context to use for this request.
+For now, the argument must always be NULL, which will use the default
+context for the current thread.
+.Pp
+The
+.Fn res_send_async ,
+.Fn res_query_async
+and
+.Fn res_search_async
+functions are asynchronous versions of the standard libc resolver routines.
+Their interface is very similar, except that the response buffer is always
+allocated internally.
+The return value is found upon completion in the
+.Fa ar_datalen
+member of the response structure.
+In addition, the
+.Fa ar_ns
+structure contains the address of the DNS server that sent the response,
+.Fa ar_rcode
+contains the code returned by the server in the DNS response packet, and
+.Fa ar_count
+contains the number of answers in the packet.
+If a response is received it is placed in a newly allocated buffer
+and returned as
+.Fa ar_data
+member.
+This buffer must be freed by the caller.
+On error, the
+.Fa ar_errno
+and
+.Fa ar_h_errno
+members are set accordingly.
+.Pp
+The
+.Fn getrrsetbyname_async
+function is an asynchronous version of
+.Xr getrrsetbyname 3 .
+Upon completion, the return code is found in
+.Fa ar_rrset_errno
+and the address to the newly allocated result set is set in
+.Fa ar_rrsetinfo .
+As for the blocking function, it must be freed by calling
+.Xr freerrset 3 .
+.Pp
+The
+.Fn gethostbyname_async ,
+.Fn gethostbyname2_async
+and
+.Fn gethostbyaddr_async
+functions provide an asynchronous version of the network host entry functions.
+Upon completion,
+.Ar ar_h_errno
+is set and the resulting hostent address, if found, is set
+in the
+.Ar ar_hostent
+field.
+Note that unlike their blocking counterparts, these functions always return a
+pointer to newly allocated memory, which must be released by the caller using
+.Xr free 3 .
+.Pp
+Similarly, the
+.Fn getnetbyname_async
+and
+.Fn getnetbyaddr_async
+functions provide an asynchronous version of the network entry functions.
+Upon completion,
+.Ar ar_h_errno
+is set and the resulting netent address, if found, is set
+in the
+.Ar ar_netent
+field.
+The memory there is also allocated for the request, and it must be freed by
+.Xr free 3 .
+.Pp
+The
+.Fn getaddrinfo_async
+function is an asynchronous version of the
+.Xr getaddrinfo 3
+call.
+It provides a chain of addrinfo structures with all valid combinations of
+socket address for the given
+.Fa hostname ,
+.Fa servname
+and
+.Fa hints .
+Those three parameters have the same meaning as for the blocking counterpart.
+Upon completion the return code is set in
+.Fa ar_gai_errno .
+The
+.Fa ar_errno
+member may also be set.
+On success, the
+.Fa ar_addrinfo
+member points to a newly allocated list of addrinfo.
+This list must be freed with
+.Xr freeaddrinfo 3 .
+.Sh WORKING WITH THREADS
+This implementation of the asynchronous resolver interface is thread-safe
+and lock-free internally, but the following restriction applies:
+Two different threads must not create queries on the same context or
+run queries originating from the same context at the same time.
+If they want to do that, all calls must be protected by a mutex around
+that context.
+.Pp
+It is generally not a problem since the main point of the asynchronous
+resolver is to multiplex queries within a single thread of control,
+so sharing a resolver among threads is not useful.
+.Sh SEE ALSO
+.Xr getaddrinfo 3 ,
+.Xr gethostbyname 3 ,
+.Xr getnameinfo 3 ,
+.Xr getnetbyname 3 ,
+.Xr getrrsetbyname 3 ,
+.Xr res_send 3 ,
+.Xr resolv.conf 5
diff --git a/openbsd-compat/libasr/asr_utils.c b/openbsd-compat/libasr/asr_utils.c
new file mode 100644
index 00000000..e3d24c93
--- /dev/null
+++ b/openbsd-compat/libasr/asr_utils.c
@@ -0,0 +1,574 @@
+/* $OpenBSD: asr_utils.c,v 1.18 2017/09/23 20:55:06 jca Exp $ */
+/*
+ * Copyright (c) 2009-2012 Eric Faurot <eric@faurot.net>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "includes.h"
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <arpa/nameser.h>
+#ifdef HAVE_ARPA_NAMESER_COMPAT_H
+#include <arpa/nameser_compat.h>
+#endif
+#include <netdb.h>
+
+#include <asr.h>
+#include <ctype.h>
+#include <errno.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "asr_private.h"
+
+static int dname_check_label(const char *, size_t);
+static ssize_t dname_expand(const unsigned char *, size_t, size_t, size_t *,
+ char *, size_t);
+
+static int unpack_data(struct asr_unpack *, void *, size_t);
+static int unpack_u16(struct asr_unpack *, uint16_t *);
+static int unpack_u32(struct asr_unpack *, uint32_t *);
+static int unpack_inaddr(struct asr_unpack *, struct in_addr *);
+static int unpack_in6addr(struct asr_unpack *, struct in6_addr *);
+static int unpack_dname(struct asr_unpack *, char *, size_t);
+
+static int pack_data(struct asr_pack *, const void *, size_t);
+static int pack_u16(struct asr_pack *, uint16_t);
+static int pack_dname(struct asr_pack *, const char *);
+
+static int
+dname_check_label(const char *s, size_t l)
+{
+ if (l == 0 || l > 63)
+ return (-1);
+
+ return (0);
+}
+
+ssize_t
+_asr_dname_from_fqdn(const char *str, char *dst, size_t max)
+{
+ ssize_t res;
+ size_t l, n;
+ char *d;
+
+ res = 0;
+
+ /* special case: the root domain */
+ if (str[0] == '.') {
+ if (str[1] != '\0')
+ return (-1);
+ if (dst && max >= 1)
+ *dst = '\0';
+ return (1);
+ }
+
+ for (; *str; str = d + 1) {
+
+ d = strchr(str, '.');
+ if (d == NULL || d == str)
+ return (-1);
+
+ l = (d - str);
+
+ if (dname_check_label(str, l) == -1)
+ return (-1);
+
+ res += l + 1;
+
+ if (dst) {
+ *dst++ = l;
+ max -= 1;
+ n = (l > max) ? max : l;
+ memmove(dst, str, n);
+ max -= n;
+ if (max == 0)
+ dst = NULL;
+ else
+ dst += n;
+ }
+ }
+
+ if (dst)
+ *dst++ = '\0';
+
+ return (res + 1);
+}
+
+static ssize_t
+dname_expand(const unsigned char *data, size_t len, size_t offset,
+ size_t *newoffset, char *dst, size_t max)
+{
+ size_t n, count, end, ptr, start;
+ ssize_t res;
+
+ if (offset >= len)
+ return (-1);
+
+ res = 0;
+ end = start = offset;
+
+ for (; (n = data[offset]); ) {
+ if ((n & 0xc0) == 0xc0) {
+ if (offset + 2 > len)
+ return (-1);
+ ptr = 256 * (n & ~0xc0) + data[offset + 1];
+ if (ptr >= start)
+ return (-1);
+ if (end < offset + 2)
+ end = offset + 2;
+ offset = start = ptr;
+ continue;
+ }
+ if (offset + n + 1 > len)
+ return (-1);
+
+ if (dname_check_label(data + offset + 1, n) == -1)
+ return (-1);
+
+ /* copy n + at offset+1 */
+ if (dst != NULL && max != 0) {
+ count = (max < n + 1) ? (max) : (n + 1);
+ memmove(dst, data + offset, count);
+ dst += count;
+ max -= count;
+ }
+ res += n + 1;
+ offset += n + 1;
+ if (end < offset)
+ end = offset;
+ }
+ if (end < offset + 1)
+ end = offset + 1;
+
+ if (dst != NULL && max != 0)
+ dst[0] = 0;
+ if (newoffset)
+ *newoffset = end;
+ return (res + 1);
+}
+
+void
+_asr_pack_init(struct asr_pack *pack, char *buf, size_t len)
+{
+ pack->buf = buf;
+ pack->len = len;
+ pack->offset = 0;
+ pack->err = 0;
+}
+
+void
+_asr_unpack_init(struct asr_unpack *unpack, const char *buf, size_t len)
+{
+ unpack->buf = buf;
+ unpack->len = len;
+ unpack->offset = 0;
+ unpack->err = 0;
+}
+
+static int
+unpack_data(struct asr_unpack *p, void *data, size_t len)
+{
+ if (p->err)
+ return (-1);
+
+ if (p->len - p->offset < len) {
+ p->err = EOVERFLOW;
+ return (-1);
+ }
+
+ memmove(data, p->buf + p->offset, len);
+ p->offset += len;
+
+ return (0);
+}
+
+static int
+unpack_u16(struct asr_unpack *p, uint16_t *u16)
+{
+ if (unpack_data(p, u16, 2) == -1)
+ return (-1);
+
+ *u16 = ntohs(*u16);
+
+ return (0);
+}
+
+static int
+unpack_u32(struct asr_unpack *p, uint32_t *u32)
+{
+ if (unpack_data(p, u32, 4) == -1)
+ return (-1);
+
+ *u32 = ntohl(*u32);
+
+ return (0);
+}
+
+static int
+unpack_inaddr(struct asr_unpack *p, struct in_addr *a)
+{
+ return (unpack_data(p, a, 4));
+}
+
+static int
+unpack_in6addr(struct asr_unpack *p, struct in6_addr *a6)
+{
+ return (unpack_data(p, a6, 16));
+}
+
+static int
+unpack_dname(struct asr_unpack *p, char *dst, size_t max)
+{
+ ssize_t e;
+
+ if (p->err)
+ return (-1);
+
+ e = dname_expand(p->buf, p->len, p->offset, &p->offset, dst, max);
+ if (e == -1) {
+ p->err = EINVAL;
+ return (-1);
+ }
+ if (e < 0 || e > MAXDNAME) {
+ p->err = ERANGE;
+ return (-1);
+ }
+
+ return (0);
+}
+
+int
+_asr_unpack_header(struct asr_unpack *p, struct asr_dns_header *h)
+{
+ if (unpack_data(p, h, HFIXEDSZ) == -1)
+ return (-1);
+
+ h->flags = ntohs(h->flags);
+ h->qdcount = ntohs(h->qdcount);
+ h->ancount = ntohs(h->ancount);
+ h->nscount = ntohs(h->nscount);
+ h->arcount = ntohs(h->arcount);
+
+ return (0);
+}
+
+int
+_asr_unpack_query(struct asr_unpack *p, struct asr_dns_query *q)
+{
+ unpack_dname(p, q->q_dname, sizeof(q->q_dname));
+ unpack_u16(p, &q->q_type);
+ unpack_u16(p, &q->q_class);
+
+ return (p->err) ? (-1) : (0);
+}
+
+int
+_asr_unpack_rr(struct asr_unpack *p, struct asr_dns_rr *rr)
+{
+ uint16_t rdlen;
+ size_t save_offset;
+
+ unpack_dname(p, rr->rr_dname, sizeof(rr->rr_dname));
+ unpack_u16(p, &rr->rr_type);
+ unpack_u16(p, &rr->rr_class);
+ unpack_u32(p, &rr->rr_ttl);
+ unpack_u16(p, &rdlen);
+
+ if (p->err)
+ return (-1);
+
+ if (p->len - p->offset < rdlen) {
+ p->err = EOVERFLOW;
+ return (-1);
+ }
+
+ save_offset = p->offset;
+
+ switch (rr->rr_type) {
+
+ case T_CNAME:
+ unpack_dname(p, rr->rr.cname.cname, sizeof(rr->rr.cname.cname));
+ break;
+
+ case T_MX:
+ unpack_u16(p, &rr->rr.mx.preference);
+ unpack_dname(p, rr->rr.mx.exchange, sizeof(rr->rr.mx.exchange));
+ break;
+
+ case T_NS:
+ unpack_dname(p, rr->rr.ns.nsname, sizeof(rr->rr.ns.nsname));
+ break;
+
+ case T_PTR:
+ unpack_dname(p, rr->rr.ptr.ptrname, sizeof(rr->rr.ptr.ptrname));
+ break;
+
+ case T_SOA:
+ unpack_dname(p, rr->rr.soa.mname, sizeof(rr->rr.soa.mname));
+ unpack_dname(p, rr->rr.soa.rname, sizeof(rr->rr.soa.rname));
+ unpack_u32(p, &rr->rr.soa.serial);
+ unpack_u32(p, &rr->rr.soa.refresh);
+ unpack_u32(p, &rr->rr.soa.retry);
+ unpack_u32(p, &rr->rr.soa.expire);
+ unpack_u32(p, &rr->rr.soa.minimum);
+ break;
+
+ case T_A:
+ if (rr->rr_class != C_IN)
+ goto other;
+ unpack_inaddr(p, &rr->rr.in_a.addr);
+ break;
+
+ case T_AAAA:
+ if (rr->rr_class != C_IN)
+ goto other;
+ unpack_in6addr(p, &rr->rr.in_aaaa.addr6);
+ break;
+ default:
+ other:
+ rr->rr.other.rdata = p->buf + p->offset;
+ rr->rr.other.rdlen = rdlen;
+ p->offset += rdlen;
+ }
+
+ if (p->err)
+ return (-1);
+
+ /* make sure that the advertised rdlen is really ok */
+ if (p->offset - save_offset != rdlen)
+ p->err = EINVAL;
+
+ return (p->err) ? (-1) : (0);
+}
+
+static int
+pack_data(struct asr_pack *p, const void *data, size_t len)
+{
+ if (p->err)
+ return (-1);
+
+ if (p->len < p->offset + len) {
+ p->err = EOVERFLOW;
+ return (-1);
+ }
+
+ memmove(p->buf + p->offset, data, len);
+ p->offset += len;
+
+ return (0);
+}
+
+static int
+pack_u16(struct asr_pack *p, uint16_t v)
+{
+ v = htons(v);
+
+ return (pack_data(p, &v, 2));
+}
+
+static int
+pack_dname(struct asr_pack *p, const char *dname)
+{
+ /* dname compression would be nice to have here.
+ * need additionnal context.
+ */
+ return (pack_data(p, dname, strlen(dname) + 1));
+}
+
+int
+_asr_pack_header(struct asr_pack *p, const struct asr_dns_header *h)
+{
+ struct asr_dns_header c;
+
+ c.id = h->id;
+ c.flags = htons(h->flags);
+ c.qdcount = htons(h->qdcount);
+ c.ancount = htons(h->ancount);
+ c.nscount = htons(h->nscount);
+ c.arcount = htons(h->arcount);
+
+ return (pack_data(p, &c, HFIXEDSZ));
+}
+
+int
+_asr_pack_query(struct asr_pack *p, uint16_t type, uint16_t class, const char *dname)
+{
+ pack_dname(p, dname);
+ pack_u16(p, type);
+ pack_u16(p, class);
+
+ return (p->err) ? (-1) : (0);
+}
+
+int
+_asr_pack_edns0(struct asr_pack *p, uint16_t pktsz, int dnssec_do)
+{
+ DPRINT("asr EDNS0 pktsz:%hu dnssec:%s\n", pktsz,
+ dnssec_do ? "yes" : "no");
+
+ pack_dname(p, ""); /* root */
+ pack_u16(p, T_OPT); /* OPT */
+ pack_u16(p, pktsz); /* UDP payload size */
+
+ /* extended RCODE and flags */
+ pack_u16(p, 0);
+ pack_u16(p, dnssec_do ? DNS_MESSAGEEXTFLAG_DO : 0);
+
+ pack_u16(p, 0); /* RDATA len */
+
+ return (p->err) ? (-1) : (0);
+}
+
+int
+_asr_sockaddr_from_str(struct sockaddr *sa, int family, const char *str)
+{
+ struct in_addr ina;
+ struct in6_addr in6a;
+ struct sockaddr_in *sin;
+ struct sockaddr_in6 *sin6;
+ char *cp, *str2;
+ const char *errstr;
+
+ switch (family) {
+ case PF_UNSPEC:
+ if (_asr_sockaddr_from_str(sa, PF_INET, str) == 0)
+ return (0);
+ return _asr_sockaddr_from_str(sa, PF_INET6, str);
+
+ case PF_INET:
+ if (inet_pton(PF_INET, str, &ina) != 1)
+ return (-1);
+
+ sin = (struct sockaddr_in *)sa;
+ memset(sin, 0, sizeof *sin);
+#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
+ sin->sin_len = sizeof(struct sockaddr_in);
+#endif
+ sin->sin_family = PF_INET;
+ sin->sin_addr.s_addr = ina.s_addr;
+ return (0);
+
+ case PF_INET6:
+ cp = strchr(str, SCOPE_DELIMITER);
+ if (cp) {
+ str2 = strdup(str);
+ if (str2 == NULL)
+ return (-1);
+ str2[cp - str] = '\0';
+ if (inet_pton(PF_INET6, str2, &in6a) != 1) {
+ free(str2);
+ return (-1);
+ }
+ cp++;
+ free(str2);
+ } else if (inet_pton(PF_INET6, str, &in6a) != 1)
+ return (-1);
+
+ sin6 = (struct sockaddr_in6 *)sa;
+ memset(sin6, 0, sizeof *sin6);
+#ifdef HAVE_STRUCT_SOCKADDR_IN6_SIN6_LEN
+ sin6->sin6_len = sizeof(struct sockaddr_in6);
+#endif
+ sin6->sin6_family = PF_INET6;
+ sin6->sin6_addr = in6a;
+
+ if (cp == NULL)
+ return (0);
+
+ if (IN6_IS_ADDR_LINKLOCAL(&in6a) ||
+ IN6_IS_ADDR_MC_LINKLOCAL(&in6a) ||
+ IN6_IS_ADDR_MC_NODELOCAL(&in6a))
+ if ((sin6->sin6_scope_id = if_nametoindex(cp)))
+ return (0);
+
+ sin6->sin6_scope_id = strtonum(cp, 0, UINT32_MAX, &errstr);
+ if (errstr)
+ return (-1);
+ return (0);
+
+ default:
+ break;
+ }
+
+ return (-1);
+}
+
+ssize_t
+_asr_addr_as_fqdn(const char *addr, int family, char *dst, size_t max)
+{
+ const struct in6_addr *in6_addr;
+ in_addr_t in_addr;
+
+ switch (family) {
+ case AF_INET:
+ in_addr = ntohl(*((const in_addr_t *)addr));
+ snprintf(dst, max,
+ "%d.%d.%d.%d.in-addr.arpa.",
+ in_addr & 0xff,
+ (in_addr >> 8) & 0xff,
+ (in_addr >> 16) & 0xff,
+ (in_addr >> 24) & 0xff);
+ break;
+ case AF_INET6:
+ in6_addr = (const struct in6_addr *)addr;
+ snprintf(dst, max,
+ "%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x."
+ "%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x."
+ "ip6.arpa.",
+ in6_addr->s6_addr[15] & 0xf,
+ (in6_addr->s6_addr[15] >> 4) & 0xf,
+ in6_addr->s6_addr[14] & 0xf,
+ (in6_addr->s6_addr[14] >> 4) & 0xf,
+ in6_addr->s6_addr[13] & 0xf,
+ (in6_addr->s6_addr[13] >> 4) & 0xf,
+ in6_addr->s6_addr[12] & 0xf,
+ (in6_addr->s6_addr[12] >> 4) & 0xf,
+ in6_addr->s6_addr[11] & 0xf,
+ (in6_addr->s6_addr[11] >> 4) & 0xf,
+ in6_addr->s6_addr[10] & 0xf,
+ (in6_addr->s6_addr[10] >> 4) & 0xf,
+ in6_addr->s6_addr[9] & 0xf,
+ (in6_addr->s6_addr[9] >> 4) & 0xf,
+ in6_addr->s6_addr[8] & 0xf,
+ (in6_addr->s6_addr[8] >> 4) & 0xf,
+ in6_addr->s6_addr[7] & 0xf,
+ (in6_addr->s6_addr[7] >> 4) & 0xf,
+ in6_addr->s6_addr[6] & 0xf,
+ (in6_addr->s6_addr[6] >> 4) & 0xf,
+ in6_addr->s6_addr[5] & 0xf,
+ (in6_addr->s6_addr[5] >> 4) & 0xf,
+ in6_addr->s6_addr[4] & 0xf,
+ (in6_addr->s6_addr[4] >> 4) & 0xf,
+ in6_addr->s6_addr[3] & 0xf,
+ (in6_addr->s6_addr[3] >> 4) & 0xf,
+ in6_addr->s6_addr[2] & 0xf,
+ (in6_addr->s6_addr[2] >> 4) & 0xf,
+ in6_addr->s6_addr[1] & 0xf,
+ (in6_addr->s6_addr[1] >> 4) & 0xf,
+ in6_addr->s6_addr[0] & 0xf,
+ (in6_addr->s6_addr[0] >> 4) & 0xf);
+ break;
+ default:
+ return (-1);
+ }
+ return (0);
+}
diff --git a/openbsd-compat/libasr/getaddrinfo.c b/openbsd-compat/libasr/getaddrinfo.c
new file mode 100644
index 00000000..37fe2d4d
--- /dev/null
+++ b/openbsd-compat/libasr/getaddrinfo.c
@@ -0,0 +1,55 @@
+/* $OpenBSD: getaddrinfo.c,v 1.9 2015/10/08 14:08:44 eric Exp $ */
+/*
+ * Copyright (c) 2012 Eric Faurot <eric@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netdb.h>
+
+#include <asr.h>
+#include <errno.h>
+#include <resolv.h>
+
+int
+getaddrinfo(const char *hostname, const char *servname,
+ const struct addrinfo *hints, struct addrinfo **res)
+{
+ struct asr_query *as;
+ struct asr_result ar;
+ int saved_errno = errno;
+
+ if (hints == NULL || (hints->ai_flags & AI_NUMERICHOST) == 0)
+ res_init();
+
+ as = getaddrinfo_async(hostname, servname, hints, NULL);
+ if (as == NULL) {
+ if (errno == ENOMEM) {
+ errno = saved_errno;
+ return (EAI_MEMORY);
+ }
+ return (EAI_SYSTEM);
+ }
+
+ asr_run_sync(as, &ar);
+
+ *res = ar.ar_addrinfo;
+ if (ar.ar_gai_errno == EAI_SYSTEM)
+ errno = ar.ar_errno;
+
+ return (ar.ar_gai_errno);
+}
+DEF_WEAK(getaddrinfo);
diff --git a/openbsd-compat/libasr/getaddrinfo_async.c b/openbsd-compat/libasr/getaddrinfo_async.c
new file mode 100644
index 00000000..1fd44ff7
--- /dev/null
+++ b/openbsd-compat/libasr/getaddrinfo_async.c
@@ -0,0 +1,756 @@
+/* $OpenBSD: getaddrinfo_async.c,v 1.56 2018/11/03 09:13:24 eric Exp $ */
+/*
+ * Copyright (c) 2012 Eric Faurot <eric@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "includes.h"
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/uio.h>
+#include <netinet/in.h>
+#include <arpa/nameser.h>
+#ifdef HAVE_ARPA_NAMESER_COMPAT_H
+#include <arpa/nameser_compat.h>
+#endif
+#include <net/if.h>
+#include <netdb.h>
+
+#include <asr.h>
+#include <errno.h>
+#include <ifaddrs.h>
+#include <resolv.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <limits.h>
+
+#include "asr_private.h"
+
+struct match {
+ int family;
+ int socktype;
+ int protocol;
+};
+
+static int getaddrinfo_async_run(struct asr_query *, struct asr_result *);
+static int get_port(const char *, const char *, int);
+static int iter_family(struct asr_query *, int);
+static int addrinfo_add(struct asr_query *, const struct sockaddr *, const char *);
+static int addrinfo_from_file(struct asr_query *, int, FILE *);
+static int addrinfo_from_pkt(struct asr_query *, char *, size_t);
+static int addrconfig_setup(struct asr_query *);
+
+static const struct match matches[] = {
+ { PF_INET, SOCK_DGRAM, IPPROTO_UDP },
+ { PF_INET, SOCK_STREAM, IPPROTO_TCP },
+ { PF_INET, SOCK_RAW, 0 },
+ { PF_INET6, SOCK_DGRAM, IPPROTO_UDP },
+ { PF_INET6, SOCK_STREAM, IPPROTO_TCP },
+ { PF_INET6, SOCK_RAW, 0 },
+ { -1, 0, 0, },
+};
+
+#define MATCH_FAMILY(a, b) ((a) == matches[(b)].family || (a) == PF_UNSPEC)
+#define MATCH_PROTO(a, b) ((a) == matches[(b)].protocol || (a) == 0 || matches[(b)].protocol == 0)
+/* Do not match SOCK_RAW unless explicitly specified */
+#define MATCH_SOCKTYPE(a, b) ((a) == matches[(b)].socktype || ((a) == 0 && \
+ matches[(b)].socktype != SOCK_RAW))
+
+enum {
+ DOM_INIT,
+ DOM_DOMAIN,
+ DOM_DONE
+};
+
+struct asr_query *
+getaddrinfo_async(const char *hostname, const char *servname,
+ const struct addrinfo *hints, void *asr)
+{
+ struct asr_ctx *ac;
+ struct asr_query *as;
+
+ if (hints == NULL || (hints->ai_flags & AI_NUMERICHOST) == 0)
+ ac = _asr_use_resolver(asr);
+ else
+ ac = _asr_no_resolver();
+ if ((as = _asr_async_new(ac, ASR_GETADDRINFO)) == NULL)
+ goto abort; /* errno set */
+ as->as_run = getaddrinfo_async_run;
+
+ if (hostname) {
+ if ((as->as.ai.hostname = strdup(hostname)) == NULL)
+ goto abort; /* errno set */
+ }
+ if (servname && (as->as.ai.servname = strdup(servname)) == NULL)
+ goto abort; /* errno set */
+ if (hints)
+ memmove(&as->as.ai.hints, hints, sizeof *hints);
+ else {
+ memset(&as->as.ai.hints, 0, sizeof as->as.ai.hints);
+ as->as.ai.hints.ai_family = PF_UNSPEC;
+ as->as.ai.hints.ai_flags = AI_ADDRCONFIG;
+ }
+
+ _asr_ctx_unref(ac);
+ return (as);
+ abort:
+ if (as)
+ _asr_async_free(as);
+ _asr_ctx_unref(ac);
+ return (NULL);
+}
+DEF_WEAK(getaddrinfo_async);
+
+static int
+getaddrinfo_async_run(struct asr_query *as, struct asr_result *ar)
+{
+ char fqdn[MAXDNAME];
+ const char *str;
+ struct addrinfo *ai;
+ int i, family, r;
+ FILE *f;
+ union {
+ struct sockaddr sa;
+ struct sockaddr_in sain;
+ struct sockaddr_in6 sain6;
+ } sa;
+
+ next:
+ switch (as->as_state) {
+
+ case ASR_STATE_INIT:
+
+ /*
+ * First, make sure the parameters are valid.
+ */
+
+ as->as_count = 0;
+
+ if (as->as.ai.hostname == NULL &&
+ as->as.ai.servname == NULL) {
+ ar->ar_gai_errno = EAI_NONAME;
+ async_set_state(as, ASR_STATE_HALT);
+ break;
+ }
+
+ if (as->as.ai.hostname && as->as.ai.hostname[0] == '\0') {
+ ar->ar_gai_errno = EAI_NODATA;
+ async_set_state(as, ASR_STATE_HALT);
+ break;
+ }
+
+ ai = &as->as.ai.hints;
+
+#ifdef EAI_BADHINTS
+ if (ai->ai_addrlen ||
+ ai->ai_canonname ||
+ ai->ai_addr ||
+ ai->ai_next) {
+ ar->ar_gai_errno = EAI_BADHINTS;
+ async_set_state(as, ASR_STATE_HALT);
+ break;
+ }
+#endif
+
+ if (ai->ai_flags & ~AI_MASK ||
+ (ai->ai_flags & AI_CANONNAME && ai->ai_flags & AI_FQDN)) {
+ ar->ar_gai_errno = EAI_BADFLAGS;
+ async_set_state(as, ASR_STATE_HALT);
+ break;
+ }
+
+ if (ai->ai_family != PF_UNSPEC &&
+ ai->ai_family != PF_INET &&
+ ai->ai_family != PF_INET6) {
+ ar->ar_gai_errno = EAI_FAMILY;
+ async_set_state(as, ASR_STATE_HALT);
+ break;
+ }
+
+ if (ai->ai_socktype &&
+ ai->ai_socktype != SOCK_DGRAM &&
+ ai->ai_socktype != SOCK_STREAM &&
+ ai->ai_socktype != SOCK_RAW) {
+ ar->ar_gai_errno = EAI_SOCKTYPE;
+ async_set_state(as, ASR_STATE_HALT);
+ break;
+ }
+
+ if (ai->ai_socktype == SOCK_RAW &&
+ get_port(as->as.ai.servname, NULL, 1) != 0) {
+ ar->ar_gai_errno = EAI_SERVICE;
+ async_set_state(as, ASR_STATE_HALT);
+ break;
+ }
+
+ /* Restrict result set to configured address families */
+ if (ai->ai_flags & AI_ADDRCONFIG) {
+ if (addrconfig_setup(as) == -1) {
+ ar->ar_errno = errno;
+ ar->ar_gai_errno = EAI_SYSTEM;
+ async_set_state(as, ASR_STATE_HALT);
+ break;
+ }
+ }
+
+ /* Make sure there is at least a valid combination */
+ for (i = 0; matches[i].family != -1; i++)
+ if (MATCH_FAMILY(ai->ai_family, i) &&
+ MATCH_SOCKTYPE(ai->ai_socktype, i) &&
+ MATCH_PROTO(ai->ai_protocol, i))
+ break;
+ if (matches[i].family == -1) {
+#ifdef EAI_BADHINTS
+ ar->ar_gai_errno = EAI_BADHINTS;
+#else
+ ar->ar_gai_errno = EAI_FAIL;
+#endif
+ async_set_state(as, ASR_STATE_HALT);
+ break;
+ }
+
+ if (ai->ai_protocol == 0 || ai->ai_protocol == IPPROTO_UDP)
+ as->as.ai.port_udp = get_port(as->as.ai.servname, "udp",
+ as->as.ai.hints.ai_flags & AI_NUMERICSERV);
+ if (ai->ai_protocol == 0 || ai->ai_protocol == IPPROTO_TCP)
+ as->as.ai.port_tcp = get_port(as->as.ai.servname, "tcp",
+ as->as.ai.hints.ai_flags & AI_NUMERICSERV);
+ if (as->as.ai.port_tcp == -2 || as->as.ai.port_udp == -2 ||
+ (as->as.ai.port_tcp == -1 && as->as.ai.port_udp == -1) ||
+ (ai->ai_protocol && (as->as.ai.port_udp == -1 ||
+ as->as.ai.port_tcp == -1))) {
+ ar->ar_gai_errno = EAI_SERVICE;
+ async_set_state(as, ASR_STATE_HALT);
+ break;
+ }
+
+ ar->ar_gai_errno = 0;
+
+ /* If hostname is NULL, use local address */
+ if (as->as.ai.hostname == NULL) {
+ for (family = iter_family(as, 1);
+ family != -1;
+ family = iter_family(as, 0)) {
+ /*
+ * We could use statically built sockaddrs for
+ * those, rather than parsing over and over.
+ */
+ if (family == PF_INET)
+ str = (ai->ai_flags & AI_PASSIVE) ? \
+ "0.0.0.0" : "127.0.0.1";
+ else /* PF_INET6 */
+ str = (ai->ai_flags & AI_PASSIVE) ? \
+ "::" : "::1";
+ /* This can't fail */
+ _asr_sockaddr_from_str(&sa.sa, family, str);
+ if ((r = addrinfo_add(as, &sa.sa, NULL))) {
+ ar->ar_gai_errno = r;
+ break;
+ }
+ }
+ if (ar->ar_gai_errno == 0 && as->as_count == 0) {
+ ar->ar_gai_errno = EAI_NODATA;
+ }
+ async_set_state(as, ASR_STATE_HALT);
+ break;
+ }
+
+ /* Try numeric addresses first */
+ for (family = iter_family(as, 1);
+ family != -1;
+ family = iter_family(as, 0)) {
+
+ if (_asr_sockaddr_from_str(&sa.sa, family,
+ as->as.ai.hostname) == -1)
+ continue;
+
+ if ((r = addrinfo_add(as, &sa.sa, NULL)))
+ ar->ar_gai_errno = r;
+ break;
+ }
+ if (ar->ar_gai_errno || as->as_count) {
+ async_set_state(as, ASR_STATE_HALT);
+ break;
+ }
+
+ if (ai->ai_flags & AI_NUMERICHOST) {
+ ar->ar_gai_errno = EAI_NONAME;
+ async_set_state(as, ASR_STATE_HALT);
+ break;
+ }
+
+ async_set_state(as, ASR_STATE_NEXT_DB);
+ break;
+
+ case ASR_STATE_NEXT_DB:
+ if (_asr_iter_db(as) == -1) {
+ async_set_state(as, ASR_STATE_NOT_FOUND);
+ break;
+ }
+ as->as_family_idx = 0;
+ async_set_state(as, ASR_STATE_SAME_DB);
+ break;
+
+ case ASR_STATE_NEXT_FAMILY:
+ as->as_family_idx += 1;
+ if (as->as.ai.hints.ai_family != AF_UNSPEC ||
+ AS_FAMILY(as) == -1) {
+ /* The family was specified, or we have tried all
+ * families with this DB.
+ */
+ if (as->as_count) {
+ ar->ar_gai_errno = 0;
+ async_set_state(as, ASR_STATE_HALT);
+ } else
+ async_set_state(as, ASR_STATE_NEXT_DOMAIN);
+ break;
+ }
+ async_set_state(as, ASR_STATE_SAME_DB);
+ break;
+
+ case ASR_STATE_NEXT_DOMAIN:
+ /* domain search is only for dns */
+ if (AS_DB(as) != ASR_DB_DNS) {
+ async_set_state(as, ASR_STATE_NEXT_DB);
+ break;
+ }
+ as->as_family_idx = 0;
+
+ free(as->as.ai.fqdn);
+ as->as.ai.fqdn = NULL;
+ r = _asr_iter_domain(as, as->as.ai.hostname, fqdn, sizeof(fqdn));
+ if (r == -1) {
+ async_set_state(as, ASR_STATE_NEXT_DB);
+ break;
+ }
+ if (r == 0) {
+ ar->ar_gai_errno = EAI_FAIL;
+ async_set_state(as, ASR_STATE_HALT);
+ break;
+ }
+ as->as.ai.fqdn = strdup(fqdn);
+ if (as->as.ai.fqdn == NULL) {
+ ar->ar_gai_errno = EAI_MEMORY;
+ async_set_state(as, ASR_STATE_HALT);
+ break;
+ }
+
+ async_set_state(as, ASR_STATE_SAME_DB);
+ break;
+
+ case ASR_STATE_SAME_DB:
+ /* query the current DB again */
+ switch (AS_DB(as)) {
+ case ASR_DB_DNS:
+ if (as->as.ai.fqdn == NULL) {
+ /* First try, initialize domain iteration */
+ as->as_dom_flags = 0;
+ as->as_dom_step = DOM_INIT;
+ async_set_state(as, ASR_STATE_NEXT_DOMAIN);
+ break;
+ }
+
+ family = (as->as.ai.hints.ai_family == AF_UNSPEC) ?
+ AS_FAMILY(as) : as->as.ai.hints.ai_family;
+
+ if (family == AF_INET &&
+ as->as_flags & ASYNC_NO_INET) {
+ async_set_state(as, ASR_STATE_NEXT_FAMILY);
+ break;
+ } else if (family == AF_INET6 &&
+ as->as_flags & ASYNC_NO_INET6) {
+ async_set_state(as, ASR_STATE_NEXT_FAMILY);
+ break;
+ }
+
+ as->as_subq = _res_query_async_ctx(as->as.ai.fqdn,
+ C_IN, (family == AF_INET6) ? T_AAAA : T_A,
+ as->as_ctx);
+
+ if (as->as_subq == NULL) {
+ if (errno == ENOMEM)
+ ar->ar_gai_errno = EAI_MEMORY;
+ else
+ ar->ar_gai_errno = EAI_FAIL;
+ async_set_state(as, ASR_STATE_HALT);
+ break;
+ }
+ async_set_state(as, ASR_STATE_SUBQUERY);
+ break;
+
+ case ASR_DB_FILE:
+ f = fopen(_PATH_HOSTS, "re");
+ if (f == NULL) {
+ async_set_state(as, ASR_STATE_NEXT_DB);
+ break;
+ }
+ family = (as->as.ai.hints.ai_family == AF_UNSPEC) ?
+ AS_FAMILY(as) : as->as.ai.hints.ai_family;
+
+ r = addrinfo_from_file(as, family, f);
+ if (r == -1) {
+ if (errno == ENOMEM)
+ ar->ar_gai_errno = EAI_MEMORY;
+ else
+ ar->ar_gai_errno = EAI_FAIL;
+ async_set_state(as, ASR_STATE_HALT);
+ } else
+ async_set_state(as, ASR_STATE_NEXT_FAMILY);
+ fclose(f);
+ break;
+
+ default:
+ async_set_state(as, ASR_STATE_NEXT_DB);
+ }
+ break;
+
+ case ASR_STATE_SUBQUERY:
+ if ((r = asr_run(as->as_subq, ar)) == ASYNC_COND)
+ return (ASYNC_COND);
+
+ as->as_subq = NULL;
+
+ if (ar->ar_datalen == -1) {
+ async_set_state(as, ASR_STATE_NEXT_FAMILY);
+ break;
+ }
+
+ r = addrinfo_from_pkt(as, ar->ar_data, ar->ar_datalen);
+ if (r == -1) {
+ if (errno == ENOMEM)
+ ar->ar_gai_errno = EAI_MEMORY;
+ else
+ ar->ar_gai_errno = EAI_FAIL;
+ async_set_state(as, ASR_STATE_HALT);
+ } else
+ async_set_state(as, ASR_STATE_NEXT_FAMILY);
+ free(ar->ar_data);
+ break;
+
+ case ASR_STATE_NOT_FOUND:
+ /* No result found. Maybe we can try again. */
+ if (as->as_flags & ASYNC_AGAIN)
+ ar->ar_gai_errno = EAI_AGAIN;
+ else
+ ar->ar_gai_errno = EAI_NODATA;
+ async_set_state(as, ASR_STATE_HALT);
+ break;
+
+ case ASR_STATE_HALT:
+ if (ar->ar_gai_errno == 0) {
+ ar->ar_count = as->as_count;
+ ar->ar_addrinfo = as->as.ai.aifirst;
+ as->as.ai.aifirst = NULL;
+ } else {
+ ar->ar_count = 0;
+ ar->ar_addrinfo = NULL;
+ }
+ return (ASYNC_DONE);
+
+ default:
+ ar->ar_errno = EOPNOTSUPP;
+ ar->ar_gai_errno = EAI_SYSTEM;
+ async_set_state(as, ASR_STATE_HALT);
+ break;
+ }
+ goto next;
+}
+
+/*
+ * Retreive the port number for the service name "servname" and
+ * the protocol "proto".
+ */
+static int
+get_port(const char *servname, const char *proto, int numonly)
+{
+#ifdef HAVE_GETSERVBYNAME_R_4_ARGS
+ struct servent se;
+#endif
+#ifdef HAVE_STRUCT_SERVENT_DATA
+ struct servent_data sed;
+#endif
+ int port;
+ const char *e;
+
+ if (servname == NULL)
+ return (0);
+
+ e = NULL;
+ port = strtonum(servname, 0, USHRT_MAX, &e);
+ if (e == NULL)
+ return (port);
+ if (errno == ERANGE)
+ return (-2); /* invalid */
+ if (numonly)
+ return (-2);
+
+ port = -1;
+#ifdef HAVE_STRUCT_SERVENT_DATA
+ memset(&sed, 0, sizeof(sed));
+#endif
+#ifdef HAVE_GETSERVBYNAME_R_4_ARGS
+ if (getservbyname_r(servname, proto, &se, &sed) != -1)
+ port = ntohs(se.s_port);
+#endif
+#ifdef HAVE_ENDSERVENT_R
+ endservent_r(&sed);
+#endif
+
+ return (port);
+}
+
+/*
+ * Iterate over the address families that are to be queried. Use the
+ * list on the async context, unless a specific family was given in hints.
+ */
+static int
+iter_family(struct asr_query *as, int first)
+{
+ if (first) {
+ as->as_family_idx = 0;
+ if (as->as.ai.hints.ai_family != PF_UNSPEC)
+ return as->as.ai.hints.ai_family;
+ return AS_FAMILY(as);
+ }
+
+ if (as->as.ai.hints.ai_family != PF_UNSPEC)
+ return (-1);
+
+ as->as_family_idx++;
+
+ return AS_FAMILY(as);
+}
+
+/*
+ * Use the sockaddr at "sa" to extend the result list on the "as" context,
+ * with the specified canonical name "cname". This function adds one
+ * entry per protocol/socktype match.
+ */
+static int
+addrinfo_add(struct asr_query *as, const struct sockaddr *sa, const char *cname)
+{
+ struct addrinfo *ai;
+ int i, port, proto;
+
+ for (i = 0; matches[i].family != -1; i++) {
+ if (matches[i].family != sa->sa_family ||
+ !MATCH_SOCKTYPE(as->as.ai.hints.ai_socktype, i) ||
+ !MATCH_PROTO(as->as.ai.hints.ai_protocol, i))
+ continue;
+
+ proto = as->as.ai.hints.ai_protocol;
+ if (!proto)
+ proto = matches[i].protocol;
+
+ if (proto == IPPROTO_TCP)
+ port = as->as.ai.port_tcp;
+ else if (proto == IPPROTO_UDP)
+ port = as->as.ai.port_udp;
+ else
+ port = 0;
+
+ /* servname specified, but not defined for this protocol */
+ if (port == -1)
+ continue;
+
+ ai = calloc(1, sizeof(*ai) + SA_LEN(sa));
+ if (ai == NULL)
+ return (EAI_MEMORY);
+ ai->ai_family = sa->sa_family;
+ ai->ai_socktype = matches[i].socktype;
+ ai->ai_protocol = proto;
+ ai->ai_flags = as->as.ai.hints.ai_flags;
+ ai->ai_addrlen = SA_LEN(sa);
+ ai->ai_addr = (void *)(ai + 1);
+ if (cname &&
+ as->as.ai.hints.ai_flags & (AI_CANONNAME | AI_FQDN)) {
+ if ((ai->ai_canonname = strdup(cname)) == NULL) {
+ free(ai);
+ return (EAI_MEMORY);
+ }
+ }
+ memmove(ai->ai_addr, sa, SA_LEN(sa));
+ if (sa->sa_family == PF_INET)
+ ((struct sockaddr_in *)ai->ai_addr)->sin_port =
+ htons(port);
+ else if (sa->sa_family == PF_INET6)
+ ((struct sockaddr_in6 *)ai->ai_addr)->sin6_port =
+ htons(port);
+
+ if (as->as.ai.aifirst == NULL)
+ as->as.ai.aifirst = ai;
+ if (as->as.ai.ailast)
+ as->as.ai.ailast->ai_next = ai;
+ as->as.ai.ailast = ai;
+ as->as_count += 1;
+ }
+
+ return (0);
+}
+
+void
+asr_freeaddrinfo(struct addrinfo *ai)
+{
+ struct addrinfo *ai_next;
+
+ while (ai) {
+ ai_next = ai->ai_next;
+ if (ai->ai_canonname)
+ free(ai->ai_canonname);
+ free(ai);
+ ai = ai_next;
+ }
+}
+
+static int
+addrinfo_from_file(struct asr_query *as, int family, FILE *f)
+{
+ char *tokens[MAXTOKEN], *c, buf[ASR_BUFSIZ + 1];
+ int n, i;
+ union {
+ struct sockaddr sa;
+ struct sockaddr_in sain;
+ struct sockaddr_in6 sain6;
+ } u;
+
+ for (;;) {
+ n = _asr_parse_namedb_line(f, tokens, MAXTOKEN, buf, sizeof(buf));
+ if (n == -1)
+ break; /* ignore errors reading the file */
+
+ for (i = 1; i < n; i++) {
+ if (strcasecmp(as->as.ai.hostname, tokens[i]))
+ continue;
+ if (_asr_sockaddr_from_str(&u.sa, family, tokens[0]) == -1)
+ continue;
+ break;
+ }
+ if (i == n)
+ continue;
+
+ if (as->as.ai.hints.ai_flags & (AI_CANONNAME | AI_FQDN))
+ c = tokens[1];
+ else
+ c = NULL;
+
+ if (addrinfo_add(as, &u.sa, c))
+ return (-1); /* errno set */
+ }
+ return (0);
+}
+
+static int
+addrinfo_from_pkt(struct asr_query *as, char *pkt, size_t pktlen)
+{
+ struct asr_unpack p;
+ struct asr_dns_header h;
+ struct asr_dns_query q;
+ struct asr_dns_rr rr;
+ int i;
+ union {
+ struct sockaddr sa;
+ struct sockaddr_in sain;
+ struct sockaddr_in6 sain6;
+ } u;
+ char buf[MAXDNAME], *c;
+
+ _asr_unpack_init(&p, pkt, pktlen);
+ _asr_unpack_header(&p, &h);
+ for (; h.qdcount; h.qdcount--)
+ _asr_unpack_query(&p, &q);
+
+ for (i = 0; i < h.ancount; i++) {
+ _asr_unpack_rr(&p, &rr);
+ if (rr.rr_type != q.q_type ||
+ rr.rr_class != q.q_class)
+ continue;
+
+ memset(&u, 0, sizeof u);
+ if (rr.rr_type == T_A) {
+#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
+ u.sain.sin_len = sizeof u.sain;
+#endif
+ u.sain.sin_family = AF_INET;
+ u.sain.sin_addr = rr.rr.in_a.addr;
+ u.sain.sin_port = 0;
+ } else if (rr.rr_type == T_AAAA) {
+#ifdef HAVE_STRUCT_SOCKADDR_IN6_SIN6_LEN
+ u.sain6.sin6_len = sizeof u.sain6;
+#endif
+ u.sain6.sin6_family = AF_INET6;
+ u.sain6.sin6_addr = rr.rr.in_aaaa.addr6;
+ u.sain6.sin6_port = 0;
+ } else
+ continue;
+
+ if (as->as.ai.hints.ai_flags & AI_CANONNAME) {
+ _asr_strdname(rr.rr_dname, buf, sizeof buf);
+ buf[strlen(buf) - 1] = '\0';
+ c = res_hnok(buf) ? buf : NULL;
+ } else if (as->as.ai.hints.ai_flags & AI_FQDN)
+ c = as->as.ai.fqdn;
+ else
+ c = NULL;
+
+ if (addrinfo_add(as, &u.sa, c))
+ return (-1); /* errno set */
+ }
+ return (0);
+}
+
+static int
+addrconfig_setup(struct asr_query *as)
+{
+ struct ifaddrs *ifa, *ifa0;
+ struct sockaddr_in *sinp;
+ struct sockaddr_in6 *sin6p;
+
+ if (getifaddrs(&ifa0) == -1)
+ return (-1);
+
+ as->as_flags |= ASYNC_NO_INET | ASYNC_NO_INET6;
+
+ for (ifa = ifa0; ifa != NULL; ifa = ifa->ifa_next) {
+ if (ifa->ifa_addr == NULL)
+ continue;
+
+ switch (ifa->ifa_addr->sa_family) {
+ case PF_INET:
+ sinp = (struct sockaddr_in *)ifa->ifa_addr;
+
+ if (sinp->sin_addr.s_addr == htonl(INADDR_LOOPBACK))
+ continue;
+
+ as->as_flags &= ~ASYNC_NO_INET;
+ break;
+ case PF_INET6:
+ sin6p = (struct sockaddr_in6 *)ifa->ifa_addr;
+
+ if (IN6_IS_ADDR_LOOPBACK(&sin6p->sin6_addr))
+ continue;
+
+ if (IN6_IS_ADDR_LINKLOCAL(&sin6p->sin6_addr))
+ continue;
+
+ as->as_flags &= ~ASYNC_NO_INET6;
+ break;
+ }
+ }
+
+ freeifaddrs(ifa0);
+
+ return (0);
+}
diff --git a/openbsd-compat/libasr/gethostnamadr.c b/openbsd-compat/libasr/gethostnamadr.c
new file mode 100644
index 00000000..2fce46b3
--- /dev/null
+++ b/openbsd-compat/libasr/gethostnamadr.c
@@ -0,0 +1,200 @@
+/* $OpenBSD: gethostnamadr.c,v 1.13 2015/09/14 07:38:37 guenther Exp $ */
+/*
+ * Copyright (c) 2012,2013 Eric Faurot <eric@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/param.h> /* ALIGN */
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netdb.h>
+
+#include <asr.h>
+#include <errno.h>
+#include <resolv.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+static int _gethostbyname(const char *, int, struct hostent *, char *, size_t,
+ int *);
+static int _fillhostent(const struct hostent *, struct hostent *, char *,
+ size_t);
+
+static struct hostent _hostent;
+static char _entbuf[4096];
+
+static char *_empty[] = { NULL, };
+
+static int
+_fillhostent(const struct hostent *h, struct hostent *r, char *buf, size_t len)
+{
+ char **ptr, *end, *pos;
+ size_t n, i;
+ int naliases, naddrs;
+
+ bzero(buf, len);
+ bzero(r, sizeof(*r));
+ r->h_aliases = _empty;
+ r->h_addr_list = _empty;
+
+ end = buf + len;
+ ptr = (char **)ALIGN(buf);
+
+ if ((char *)ptr >= end)
+ return (ERANGE);
+
+ for (naliases = 0; h->h_aliases[naliases]; naliases++)
+ ;
+ for (naddrs = 0; h->h_addr_list[naddrs]; naddrs++)
+ ;
+
+ pos = (char *)(ptr + (naliases + 1) + (naddrs + 1));
+ if (pos >= end)
+ return (ERANGE);
+
+ r->h_name = NULL;
+ r->h_addrtype = h->h_addrtype;
+ r->h_length = h->h_length;
+ r->h_aliases = ptr;
+ r->h_addr_list = ptr + naliases + 1;
+
+ n = strlcpy(pos, h->h_name, end - pos);
+ if (n >= end - pos)
+ return (ERANGE);
+ r->h_name = pos;
+ pos += n + 1;
+
+ for (i = 0; i < naliases; i++) {
+ n = strlcpy(pos, h->h_aliases[i], end - pos);
+ if (n >= end - pos)
+ return (ERANGE);
+ r->h_aliases[i] = pos;
+ pos += n + 1;
+ }
+
+ pos = (char *)ALIGN(pos);
+ if (pos >= end)
+ return (ERANGE);
+
+ for (i = 0; i < naddrs; i++) {
+ if (r->h_length > end - pos)
+ return (ERANGE);
+ memmove(pos, h->h_addr_list[i], r->h_length);
+ r->h_addr_list[i] = pos;
+ pos += r->h_length;
+ }
+
+ return (0);
+}
+
+static int
+_gethostbyname(const char *name, int af, struct hostent *ret, char *buf,
+ size_t buflen, int *h_errnop)
+{
+ struct asr_query *as;
+ struct asr_result ar;
+ int r;
+
+ if (af == -1)
+ as = gethostbyname_async(name, NULL);
+ else
+ as = gethostbyname2_async(name, af, NULL);
+
+ if (as == NULL)
+ return (errno);
+
+ asr_run_sync(as, &ar);
+
+ errno = ar.ar_errno;
+ *h_errnop = ar.ar_h_errno;
+ if (ar.ar_hostent == NULL)
+ return (0);
+
+ r = _fillhostent(ar.ar_hostent, ret, buf, buflen);
+ free(ar.ar_hostent);
+
+ return (r);
+}
+
+struct hostent *
+gethostbyname(const char *name)
+{
+ struct hostent *h;
+
+ res_init();
+
+ if (_res.options & RES_USE_INET6 &&
+ (h = gethostbyname2(name, AF_INET6)))
+ return (h);
+
+ return gethostbyname2(name, AF_INET);
+}
+DEF_WEAK(gethostbyname);
+
+struct hostent *
+gethostbyname2(const char *name, int af)
+{
+ int r;
+
+ res_init();
+
+ r = _gethostbyname(name, af, &_hostent, _entbuf, sizeof(_entbuf),
+ &h_errno);
+ if (r) {
+ h_errno = NETDB_INTERNAL;
+ errno = r;
+ }
+
+ if (h_errno)
+ return (NULL);
+
+ return (&_hostent);
+}
+DEF_WEAK(gethostbyname2);
+
+struct hostent *
+gethostbyaddr(const void *addr, socklen_t len, int af)
+{
+ struct asr_query *as;
+ struct asr_result ar;
+ int r;
+
+ res_init();
+
+ as = gethostbyaddr_async(addr, len, af, NULL);
+ if (as == NULL) {
+ h_errno = NETDB_INTERNAL;
+ return (NULL);
+ }
+
+ asr_run_sync(as, &ar);
+
+ errno = ar.ar_errno;
+ h_errno = ar.ar_h_errno;
+ if (ar.ar_hostent == NULL)
+ return (NULL);
+
+ r = _fillhostent(ar.ar_hostent, &_hostent, _entbuf, sizeof(_entbuf));
+ free(ar.ar_hostent);
+
+ if (r) {
+ h_errno = NETDB_INTERNAL;
+ errno = r;
+ return (NULL);
+ }
+
+ return (&_hostent);
+}
diff --git a/openbsd-compat/libasr/gethostnamadr_async.c b/openbsd-compat/libasr/gethostnamadr_async.c
new file mode 100644
index 00000000..2636e848
--- /dev/null
+++ b/openbsd-compat/libasr/gethostnamadr_async.c
@@ -0,0 +1,676 @@
+/* $OpenBSD: gethostnamadr_async.c,v 1.45 2019/06/27 05:26:37 martijn Exp $ */
+/*
+ * Copyright (c) 2012 Eric Faurot <eric@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "includes.h"
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <arpa/nameser.h>
+#ifdef HAVE_ARPA_NAMESER_COMPAT_H
+#include <arpa/nameser_compat.h>
+#endif
+#include <netdb.h>
+
+#include <asr.h>
+#include <ctype.h>
+#include <errno.h>
+#include <resolv.h> /* for res_hnok */
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <limits.h>
+
+#include "asr_private.h"
+
+#define MAXALIASES 35
+#define MAXADDRS 35
+
+struct hostent_ext {
+ struct hostent h;
+ char *aliases[MAXALIASES + 1];
+ char *addrs[MAXADDRS + 1];
+ char *end;
+ char *pos;
+};
+
+struct netent_ext {
+ struct netent n;
+ char *aliases[MAXALIASES + 1];
+ char *end;
+ char *pos;
+};
+
+static int gethostnamadr_async_run(struct asr_query *, struct asr_result *);
+static struct hostent_ext *hostent_alloc(int);
+static int hostent_set_cname(struct hostent_ext *, const char *, int);
+static int hostent_add_alias(struct hostent_ext *, const char *, int);
+static int hostent_add_addr(struct hostent_ext *, const void *, size_t);
+static struct hostent_ext *hostent_from_addr(int, const char *, const char *);
+static struct hostent_ext *hostent_file_match(FILE *, int, int, const char *,
+ int);
+static struct hostent_ext *hostent_from_packet(int, int, char *, size_t);
+static void netent_from_hostent(struct asr_result *ar);
+
+struct asr_query *
+gethostbyname_async(const char *name, void *asr)
+{
+ return gethostbyname2_async(name, AF_INET, asr);
+}
+DEF_WEAK(gethostbyname_async);
+
+struct asr_query *
+gethostbyname2_async(const char *name, int af, void *asr)
+{
+ struct asr_ctx *ac;
+ struct asr_query *as;
+
+ /* the original segfaults */
+ if (name == NULL) {
+ errno = EINVAL;
+ return (NULL);
+ }
+
+ ac = _asr_use_resolver(asr);
+ if ((as = _asr_async_new(ac, ASR_GETHOSTBYNAME)) == NULL)
+ goto abort; /* errno set */
+ as->as_run = gethostnamadr_async_run;
+
+ as->as.hostnamadr.family = af;
+ if (af == AF_INET)
+ as->as.hostnamadr.addrlen = INADDRSZ;
+ else if (af == AF_INET6)
+ as->as.hostnamadr.addrlen = IN6ADDRSZ;
+ as->as.hostnamadr.name = strdup(name);
+ if (as->as.hostnamadr.name == NULL)
+ goto abort; /* errno set */
+
+ _asr_ctx_unref(ac);
+ return (as);
+
+ abort:
+ if (as)
+ _asr_async_free(as);
+ _asr_ctx_unref(ac);
+ return (NULL);
+}
+DEF_WEAK(gethostbyname2_async);
+
+struct asr_query *
+gethostbyaddr_async(const void *addr, socklen_t len, int af, void *asr)
+{
+ struct asr_ctx *ac;
+ struct asr_query *as;
+
+ ac = _asr_use_resolver(asr);
+ as = _gethostbyaddr_async_ctx(addr, len, af, ac);
+ _asr_ctx_unref(ac);
+
+ return (as);
+}
+DEF_WEAK(gethostbyaddr_async);
+
+struct asr_query *
+_gethostbyaddr_async_ctx(const void *addr, socklen_t len, int af,
+ struct asr_ctx *ac)
+{
+ struct asr_query *as;
+
+ if ((as = _asr_async_new(ac, ASR_GETHOSTBYADDR)) == NULL)
+ goto abort; /* errno set */
+ as->as_run = gethostnamadr_async_run;
+
+ as->as.hostnamadr.family = af;
+ as->as.hostnamadr.addrlen = len;
+ if (len > 0)
+ memmove(as->as.hostnamadr.addr, addr, (len > 16) ? 16 : len);
+
+ return (as);
+
+ abort:
+ if (as)
+ _asr_async_free(as);
+ return (NULL);
+}
+
+static int
+gethostnamadr_async_run(struct asr_query *as, struct asr_result *ar)
+{
+ struct hostent_ext *h;
+ int r, type, saved_errno;
+ FILE *f;
+ char name[MAXDNAME], *data, addr[16], *c;
+
+ next:
+ switch (as->as_state) {
+
+ case ASR_STATE_INIT:
+
+ if (as->as.hostnamadr.family != AF_INET &&
+ as->as.hostnamadr.family != AF_INET6) {
+ ar->ar_h_errno = NETDB_INTERNAL;
+ ar->ar_errno = EAFNOSUPPORT;
+ async_set_state(as, ASR_STATE_HALT);
+ break;
+ }
+
+ if ((as->as.hostnamadr.family == AF_INET &&
+ as->as.hostnamadr.addrlen != INADDRSZ) ||
+ (as->as.hostnamadr.family == AF_INET6 &&
+ as->as.hostnamadr.addrlen != IN6ADDRSZ)) {
+ ar->ar_h_errno = NETDB_INTERNAL;
+ ar->ar_errno = EINVAL;
+ async_set_state(as, ASR_STATE_HALT);
+ break;
+ }
+
+ if (as->as_type == ASR_GETHOSTBYNAME) {
+
+ if (as->as.hostnamadr.name[0] == '\0') {
+ ar->ar_h_errno = NO_DATA;
+ async_set_state(as, ASR_STATE_HALT);
+ break;
+ }
+
+ /* Name might be an IP address string */
+ for (c = as->as.hostnamadr.name; *c; c++)
+ if (!isdigit((unsigned char)*c) &&
+ *c != '.' && *c != ':')
+ break;
+ if (*c == 0 &&
+ inet_pton(as->as.hostnamadr.family,
+ as->as.hostnamadr.name, addr) == 1) {
+ h = hostent_from_addr(as->as.hostnamadr.family,
+ as->as.hostnamadr.name, addr);
+ if (h == NULL) {
+ ar->ar_errno = errno;
+ ar->ar_h_errno = NETDB_INTERNAL;
+ }
+ else {
+ ar->ar_hostent = &h->h;
+ ar->ar_h_errno = NETDB_SUCCESS;
+ }
+ async_set_state(as, ASR_STATE_HALT);
+ break;
+ }
+ }
+ async_set_state(as, ASR_STATE_NEXT_DB);
+ break;
+
+ case ASR_STATE_NEXT_DB:
+
+ if (_asr_iter_db(as) == -1) {
+ async_set_state(as, ASR_STATE_NOT_FOUND);
+ break;
+ }
+
+ switch (AS_DB(as)) {
+
+ case ASR_DB_DNS:
+
+ /* Create a subquery to do the DNS lookup */
+
+ if (as->as_type == ASR_GETHOSTBYNAME) {
+ type = (as->as.hostnamadr.family == AF_INET) ?
+ T_A : T_AAAA;
+ as->as_subq = _res_search_async_ctx(
+ as->as.hostnamadr.name,
+ C_IN, type, as->as_ctx);
+ } else {
+ _asr_addr_as_fqdn(as->as.hostnamadr.addr,
+ as->as.hostnamadr.family,
+ name, sizeof(name));
+ as->as_subq = _res_query_async_ctx(
+ name, C_IN, T_PTR, as->as_ctx);
+ }
+
+ if (as->as_subq == NULL) {
+ ar->ar_errno = errno;
+ ar->ar_h_errno = NETDB_INTERNAL;
+ async_set_state(as, ASR_STATE_HALT);
+ break;
+ }
+
+ async_set_state(as, ASR_STATE_SUBQUERY);
+ break;
+
+ case ASR_DB_FILE:
+
+ /* Try to find a match in the host file */
+
+ if ((f = fopen(_PATH_HOSTS, "re")) == NULL)
+ break;
+
+ if (as->as_type == ASR_GETHOSTBYNAME)
+ data = as->as.hostnamadr.name;
+ else
+ data = as->as.hostnamadr.addr;
+
+ h = hostent_file_match(f, as->as_type,
+ as->as.hostnamadr.family, data,
+ as->as.hostnamadr.addrlen);
+ saved_errno = errno;
+ fclose(f);
+ errno = saved_errno;
+
+ if (h == NULL) {
+ if (errno) {
+ ar->ar_errno = errno;
+ ar->ar_h_errno = NETDB_INTERNAL;
+ async_set_state(as, ASR_STATE_HALT);
+ }
+ /* otherwise not found */
+ break;
+ }
+ ar->ar_hostent = &h->h;
+ ar->ar_h_errno = NETDB_SUCCESS;
+ async_set_state(as, ASR_STATE_HALT);
+ break;
+ }
+ break;
+
+ case ASR_STATE_SUBQUERY:
+
+ /* Run the DNS subquery. */
+
+ if ((r = asr_run(as->as_subq, ar)) == ASYNC_COND)
+ return (ASYNC_COND);
+
+ /* Done. */
+ as->as_subq = NULL;
+
+ /*
+ * We either got no packet or a packet without an answer.
+ * Saveguard the h_errno and use the next DB.
+ */
+ if (ar->ar_count == 0) {
+ free(ar->ar_data);
+ as->as.hostnamadr.subq_h_errno = ar->ar_h_errno;
+ async_set_state(as, ASR_STATE_NEXT_DB);
+ break;
+ }
+
+ /* Read the hostent from the packet. */
+
+ h = hostent_from_packet(as->as_type,
+ as->as.hostnamadr.family, ar->ar_data, ar->ar_datalen);
+ free(ar->ar_data);
+ if (h == NULL) {
+ ar->ar_errno = errno;
+ ar->ar_h_errno = NETDB_INTERNAL;
+ async_set_state(as, ASR_STATE_HALT);
+ break;
+ }
+
+ if (as->as_type == ASR_GETHOSTBYADDR) {
+ if (hostent_add_addr(h, as->as.hostnamadr.addr,
+ as->as.hostnamadr.addrlen) == -1) {
+ free(h);
+ ar->ar_errno = errno;
+ ar->ar_h_errno = NETDB_INTERNAL;
+ async_set_state(as, ASR_STATE_HALT);
+ break;
+ }
+ }
+
+ /*
+ * No valid hostname or address found in the dns packet.
+ * Ignore it.
+ */
+ if ((as->as_type == ASR_GETHOSTBYNAME &&
+ h->h.h_addr_list[0] == NULL) ||
+ h->h.h_name == NULL) {
+ free(h);
+ async_set_state(as, ASR_STATE_NEXT_DB);
+ break;
+ }
+
+ ar->ar_hostent = &h->h;
+ ar->ar_h_errno = NETDB_SUCCESS;
+ async_set_state(as, ASR_STATE_HALT);
+ break;
+
+ case ASR_STATE_NOT_FOUND:
+ ar->ar_errno = 0;
+ if (as->as.hostnamadr.subq_h_errno)
+ ar->ar_h_errno = as->as.hostnamadr.subq_h_errno;
+ else
+ ar->ar_h_errno = HOST_NOT_FOUND;
+ async_set_state(as, ASR_STATE_HALT);
+ break;
+
+ case ASR_STATE_HALT:
+ if (ar->ar_h_errno == NETDB_SUCCESS &&
+ as->as_flags & ASYNC_GETNET)
+ netent_from_hostent(ar);
+ if (ar->ar_h_errno) {
+ ar->ar_hostent = NULL;
+ ar->ar_netent = NULL;
+ } else
+ ar->ar_errno = 0;
+ return (ASYNC_DONE);
+
+ default:
+ ar->ar_errno = EOPNOTSUPP;
+ ar->ar_h_errno = NETDB_INTERNAL;
+ ar->ar_gai_errno = EAI_SYSTEM;
+ async_set_state(as, ASR_STATE_HALT);
+ break;
+ }
+ goto next;
+}
+
+/*
+ * Create a hostent from a numeric address string.
+ */
+static struct hostent_ext *
+hostent_from_addr(int family, const char *name, const char *addr)
+{
+ struct hostent_ext *h;
+
+ if ((h = hostent_alloc(family)) == NULL)
+ return (NULL);
+ if (hostent_set_cname(h, name, 0) == -1)
+ goto fail;
+ if (hostent_add_addr(h, addr, h->h.h_length) == -1)
+ goto fail;
+ return (h);
+fail:
+ free(h);
+ return (NULL);
+}
+
+/*
+ * Lookup the first matching entry in the hostfile, either by address or by
+ * name depending on reqtype, and build a hostent from the line.
+ */
+static struct hostent_ext *
+hostent_file_match(FILE *f, int reqtype, int family, const char *data,
+ int datalen)
+{
+ char *tokens[MAXTOKEN], addr[16], buf[ASR_BUFSIZ + 1];
+ struct hostent_ext *h;
+ int n, i;
+
+ for (;;) {
+ n = _asr_parse_namedb_line(f, tokens, MAXTOKEN, buf, sizeof(buf));
+ if (n == -1) {
+ errno = 0; /* ignore errors reading the file */
+ return (NULL);
+ }
+
+ /* there must be an address and at least one name */
+ if (n < 2)
+ continue;
+
+ if (reqtype == ASR_GETHOSTBYNAME) {
+ for (i = 1; i < n; i++) {
+ if (strcasecmp(data, tokens[i]))
+ continue;
+ if (inet_pton(family, tokens[0], addr) == 1)
+ goto found;
+ }
+ } else {
+ if (inet_pton(family, tokens[0], addr) == 1 &&
+ memcmp(addr, data, datalen) == 0)
+ goto found;
+ }
+ }
+
+found:
+ if ((h = hostent_alloc(family)) == NULL)
+ return (NULL);
+ if (hostent_set_cname(h, tokens[1], 0) == -1)
+ goto fail;
+ for (i = 2; i < n; i ++)
+ if (hostent_add_alias(h, tokens[i], 0) == -1)
+ goto fail;
+ if (hostent_add_addr(h, addr, h->h.h_length) == -1)
+ goto fail;
+ return (h);
+fail:
+ free(h);
+ return (NULL);
+}
+
+/*
+ * Fill the hostent from the given DNS packet.
+ */
+static struct hostent_ext *
+hostent_from_packet(int reqtype, int family, char *pkt, size_t pktlen)
+{
+ struct hostent_ext *h;
+ struct asr_unpack p;
+ struct asr_dns_header hdr;
+ struct asr_dns_query q;
+ struct asr_dns_rr rr;
+ char dname[MAXDNAME];
+
+ if ((h = hostent_alloc(family)) == NULL)
+ return (NULL);
+
+ _asr_unpack_init(&p, pkt, pktlen);
+ _asr_unpack_header(&p, &hdr);
+ for (; hdr.qdcount; hdr.qdcount--)
+ _asr_unpack_query(&p, &q);
+ strlcpy(dname, q.q_dname, sizeof(dname));
+
+ for (; hdr.ancount; hdr.ancount--) {
+ _asr_unpack_rr(&p, &rr);
+ if (rr.rr_class != C_IN)
+ continue;
+ switch (rr.rr_type) {
+
+ case T_CNAME:
+ if (reqtype == ASR_GETHOSTBYNAME) {
+ if (hostent_add_alias(h, rr.rr_dname, 1) == -1)
+ goto fail;
+ } else {
+ if (strcasecmp(rr.rr_dname, dname) == 0)
+ strlcpy(dname, rr.rr.cname.cname,
+ sizeof(dname));
+ }
+ break;
+
+ case T_PTR:
+ if (reqtype != ASR_GETHOSTBYADDR)
+ break;
+ if (strcasecmp(rr.rr_dname, dname) != 0)
+ continue;
+ if (hostent_set_cname(h, rr.rr.ptr.ptrname, 1) == -1)
+ hostent_add_alias(h, rr.rr.ptr.ptrname, 1);
+ break;
+
+ case T_A:
+ if (reqtype != ASR_GETHOSTBYNAME)
+ break;
+ if (family != AF_INET)
+ break;
+ if (hostent_set_cname(h, rr.rr_dname, 1) == -1)
+ ;
+ if (hostent_add_addr(h, &rr.rr.in_a.addr, 4) == -1)
+ goto fail;
+ break;
+
+ case T_AAAA:
+ if (reqtype != ASR_GETHOSTBYNAME)
+ break;
+ if (family != AF_INET6)
+ break;
+ if (hostent_set_cname(h, rr.rr_dname, 1) == -1)
+ ;
+ if (hostent_add_addr(h, &rr.rr.in_aaaa.addr6, 16) == -1)
+ goto fail;
+ break;
+ }
+ }
+
+ return (h);
+fail:
+ free(h);
+ return (NULL);
+}
+
+static struct hostent_ext *
+hostent_alloc(int family)
+{
+ struct hostent_ext *h;
+ size_t alloc;
+
+ alloc = sizeof(*h) + 1024;
+ if ((h = calloc(1, alloc)) == NULL)
+ return (NULL);
+
+ h->h.h_addrtype = family;
+ h->h.h_length = (family == AF_INET) ? 4 : 16;
+ h->h.h_aliases = h->aliases;
+ h->h.h_addr_list = h->addrs;
+ h->pos = (char *)(h) + sizeof(*h);
+ h->end = h->pos + 1024;
+
+ return (h);
+}
+
+static int
+hostent_set_cname(struct hostent_ext *h, const char *name, int isdname)
+{
+ char buf[MAXDNAME];
+ size_t n;
+
+ if (h->h.h_name)
+ return (-1);
+
+ if (isdname) {
+ _asr_strdname(name, buf, sizeof buf);
+ buf[strlen(buf) - 1] = '\0';
+ if (!res_hnok(buf))
+ return (-1);
+ name = buf;
+ }
+
+ n = strlen(name) + 1;
+ if (h->pos + n >= h->end)
+ return (-1);
+
+ h->h.h_name = h->pos;
+ memmove(h->pos, name, n);
+ h->pos += n;
+ return (0);
+}
+
+static int
+hostent_add_alias(struct hostent_ext *h, const char *name, int isdname)
+{
+ char buf[MAXDNAME];
+ size_t i, n;
+
+ for (i = 0; i < MAXALIASES; i++)
+ if (h->aliases[i] == NULL)
+ break;
+ if (i == MAXALIASES)
+ return (0);
+
+ if (isdname) {
+ _asr_strdname(name, buf, sizeof buf);
+ buf[strlen(buf)-1] = '\0';
+ if (!res_hnok(buf))
+ return (-1);
+ name = buf;
+ }
+
+ n = strlen(name) + 1;
+ if (h->pos + n >= h->end)
+ return (0);
+
+ h->aliases[i] = h->pos;
+ memmove(h->pos, name, n);
+ h->pos += n;
+ return (0);
+}
+
+static int
+hostent_add_addr(struct hostent_ext *h, const void *addr, size_t size)
+{
+ int i;
+
+ for (i = 0; i < MAXADDRS; i++)
+ if (h->addrs[i] == NULL)
+ break;
+ if (i == MAXADDRS)
+ return (0);
+
+ if (h->pos + size >= h->end)
+ return (0);
+
+ h->addrs[i] = h->pos;
+ memmove(h->pos, addr, size);
+ h->pos += size;
+ return (0);
+}
+
+static void
+netent_from_hostent(struct asr_result *ar)
+{
+ struct in_addr *addr;
+ struct netent_ext *n;
+ struct hostent_ext *h;
+ char **na, **ha;
+ size_t sz;
+
+ /* Allocate and initialize the output. */
+ if ((n = calloc(1, sizeof(*n) + 1024)) == NULL) {
+ ar->ar_h_errno = NETDB_INTERNAL;
+ ar->ar_errno = errno;
+ goto out;
+ }
+ n->pos = (char *)(n) + sizeof(*n);
+ n->end = n->pos + 1024;
+ n->n.n_name = n->pos;
+ n->n.n_aliases = n->aliases;
+
+ /* Copy the fixed-size data. */
+ h = (struct hostent_ext *)ar->ar_hostent;
+ addr = (struct in_addr *)h->h.h_addr;
+ n->n.n_net = ntohl(addr->s_addr);
+ n->n.n_addrtype = h->h.h_addrtype;
+
+ /* Copy the network name. */
+ sz = strlen(h->h.h_name) + 1;
+ memcpy(n->pos, h->h.h_name, sz);
+ n->pos += sz;
+
+ /*
+ * Copy the aliases.
+ * No overflow check is needed because we are merely copying
+ * a part of the data from a structure of the same size.
+ */
+ na = n->aliases;
+ for (ha = h->aliases; *ha != NULL; ha++) {
+ sz = strlen(*ha) + 1;
+ memcpy(n->pos, *ha, sz);
+ *na++ = n->pos;
+ n->pos += sz;
+ }
+ *na = NULL;
+
+ /* Handle the return values. */
+ ar->ar_netent = &n->n;
+out:
+ free(ar->ar_hostent);
+ ar->ar_hostent = NULL;
+}
diff --git a/openbsd-compat/libasr/getnameinfo.c b/openbsd-compat/libasr/getnameinfo.c
new file mode 100644
index 00000000..7bee468d
--- /dev/null
+++ b/openbsd-compat/libasr/getnameinfo.c
@@ -0,0 +1,205 @@
+/* $OpenBSD: getnameinfo.c,v 1.9 2019/07/03 03:24:03 deraadt Exp $ */
+/*
+ * Copyright (c) 2012 Eric Faurot <eric@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <arpa/inet.h>
+#include <netinet/in.h>
+#include <netdb.h>
+
+#include <asr.h>
+#include <errno.h>
+#include <resolv.h>
+#include <string.h>
+
+static size_t asr_print_addr(const struct sockaddr *, char *, size_t);
+static size_t asr_print_port(const struct sockaddr *, const char *, char *, size_t);
+
+#define SA_IN(sa) ((struct sockaddr_in*)(sa))
+#define SA_IN6(sa) ((struct sockaddr_in6*)(sa))
+
+/*
+ * Print the textual representation (as given by inet_ntop(3)) of the address
+ * set in "sa".
+ *
+ * Return the total length of the string it tried to create or 0 if an error
+ * occured, in which case errno is set. On success, the constructed string
+ * is guaranteed to be NUL-terminated. Overflow must be detected by checking
+ * the returned size against buflen.
+ *
+ */
+static size_t
+asr_print_addr(const struct sockaddr *sa, char *buf, size_t buflen)
+{
+ unsigned int ifidx;
+ char tmp[sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255")];
+ char scope[IF_NAMESIZE + 1], *ifname;
+ const void *addr;
+ size_t s;
+
+ switch(sa->sa_family) {
+ case AF_INET:
+ addr = &SA_IN(sa)->sin_addr;
+ break;
+ case AF_INET6:
+ addr = &SA_IN6(sa)->sin6_addr;
+ break;
+ default:
+ errno = EINVAL;
+ return (0);
+ }
+
+ if (inet_ntop(sa->sa_family, addr, tmp, sizeof(tmp)) == NULL)
+ return (0); /* errno set */
+
+ s = strlcpy(buf, tmp, buflen);
+
+ if (sa->sa_family == AF_INET6 && SA_IN6(sa)->sin6_scope_id) {
+
+ scope[0] = SCOPE_DELIMITER;
+ scope[1] = '\0';
+
+ ifidx = SA_IN6(sa)->sin6_scope_id;
+ ifname = NULL;
+
+ if (IN6_IS_ADDR_LINKLOCAL(&(SA_IN6(sa)->sin6_addr)) ||
+ IN6_IS_ADDR_MC_LINKLOCAL(&(SA_IN6(sa)->sin6_addr)) ||
+ IN6_IS_ADDR_MC_INTFACELOCAL(&(SA_IN6(sa)->sin6_addr)))
+ ifname = if_indextoname(ifidx, scope + 1);
+
+ if (ifname == NULL)
+ (void)snprintf(scope + 1, sizeof(scope) - 1, "%u", ifidx);
+
+ if (s < buflen)
+ (void)strlcat(buf, scope, buflen);
+
+ s += strlen(scope);
+ }
+
+ return (s);
+}
+
+/*
+ * Print the textual representation of the port set on "sa".
+ *
+ * If proto is not NULL, it is used as parameter to "getservbyport_r(3)" to
+ * return a service name. If it's not set, or if no matching service is found,
+ * it prints the portno.
+ *
+ * Return the total length of the string it tried to create or 0 if an error
+ * occured, in which case errno is set. On success, the constructed string
+ * is guaranteed to be NUL-terminated. Overflow must be detected by checking
+ * the returned size against buflen.
+ */
+static size_t
+asr_print_port(const struct sockaddr *sa, const char *proto, char *buf, size_t buflen)
+{
+ struct servent s;
+ struct servent_data sd;
+ int port, r, saved_errno;
+ size_t n;
+
+ switch(sa->sa_family) {
+ case AF_INET:
+ port = SA_IN(sa)->sin_port;
+ break;
+ case AF_INET6:
+ port = SA_IN6(sa)->sin6_port;
+ break;
+ default:
+ errno = EINVAL;
+ return (0);
+ }
+
+ if (proto) {
+ memset(&sd, 0, sizeof (sd));
+ saved_errno = errno;
+ if (getservbyport_r(port, proto, &s, &sd) != -1) {
+ n = strlcpy(buf, s.s_name, buflen);
+ endservent_r(&sd);
+ return (n);
+ }
+ errno = saved_errno;
+ }
+
+ r = snprintf(buf, buflen, "%u", ntohs(port));
+ if (r < 0 || r >= buflen) /* Actually, this can not happen */
+ return (0);
+
+ return (r);
+}
+
+int
+getnameinfo(const struct sockaddr *sa, socklen_t salen, char *host,
+ size_t hostlen, char *serv, size_t servlen, int flags)
+{
+ struct asr_query *as;
+ struct asr_result ar;
+ int saved_errno = errno;
+ const char *proto;
+ size_t r;
+
+ /*
+ * Take a shortcut if we don't care about hostname,
+ * or if NI_NUMERICHOST is set.
+ */
+ if (host == NULL || hostlen == 0 ||
+ (host && hostlen && (flags & NI_NUMERICHOST))) {
+ if (host) {
+ r = asr_print_addr(sa, host, hostlen);
+ if (r == 0)
+ return (EAI_SYSTEM); /* errno set */
+ if (r >= hostlen)
+ return (EAI_OVERFLOW);
+ }
+
+ if (serv && servlen) {
+ if (flags & NI_NUMERICSERV)
+ proto = NULL;
+ else
+ proto = (flags & NI_DGRAM) ? "udp" : "tcp";
+ r = asr_print_port(sa, proto, serv, servlen);
+ if (r == 0)
+ return (EAI_SYSTEM); /* errno set */
+ if (r >= servlen)
+ return (EAI_OVERFLOW);
+ }
+
+ errno = saved_errno;
+ return (0);
+ }
+
+ res_init();
+
+ as = getnameinfo_async(sa, salen, host, hostlen, serv, servlen, flags,
+ NULL);
+ if (as == NULL) {
+ if (errno == ENOMEM) {
+ errno = saved_errno;
+ return (EAI_MEMORY);
+ }
+ return (EAI_SYSTEM);
+ }
+
+ asr_run_sync(as, &ar);
+ if (ar.ar_gai_errno == EAI_SYSTEM)
+ errno = ar.ar_errno;
+
+ return (ar.ar_gai_errno);
+}
+DEF_WEAK(getnameinfo);
diff --git a/openbsd-compat/libasr/getnameinfo_async.c b/openbsd-compat/libasr/getnameinfo_async.c
new file mode 100644
index 00000000..faba8860
--- /dev/null
+++ b/openbsd-compat/libasr/getnameinfo_async.c
@@ -0,0 +1,300 @@
+/* $OpenBSD: getnameinfo_async.c,v 1.14 2019/07/03 03:24:03 deraadt Exp $ */
+/*
+ * Copyright (c) 2012 Eric Faurot <eric@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "includes.h"
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <arpa/nameser.h>
+#include <netdb.h>
+
+#include <asr.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "asr_private.h"
+
+static int getnameinfo_async_run(struct asr_query *, struct asr_result *);
+static int _servname(struct asr_query *);
+static int _numerichost(struct asr_query *);
+
+struct asr_query *
+getnameinfo_async(const struct sockaddr *sa, socklen_t slen, char *host,
+ size_t hostlen, char *serv, size_t servlen, int flags, void *asr)
+{
+ struct asr_ctx *ac;
+ struct asr_query *as;
+
+ ac = _asr_use_resolver(asr);
+ if ((as = _asr_async_new(ac, ASR_GETNAMEINFO)) == NULL)
+ goto abort; /* errno set */
+ as->as_run = getnameinfo_async_run;
+
+ if (sa->sa_family == AF_INET)
+ memmove(&as->as.ni.sa.sa, sa, sizeof (as->as.ni.sa.sain));
+ else if (sa->sa_family == AF_INET6)
+ memmove(&as->as.ni.sa.sa, sa, sizeof (as->as.ni.sa.sain6));
+
+#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
+ as->as.ni.sa.sa.sa_len = slen;
+#endif
+ as->as.ni.hostname = host;
+ as->as.ni.hostnamelen = hostlen;
+ as->as.ni.servname = serv;
+ as->as.ni.servnamelen = servlen;
+ as->as.ni.flags = flags;
+
+ _asr_ctx_unref(ac);
+ return (as);
+
+ abort:
+ if (as)
+ _asr_async_free(as);
+ _asr_ctx_unref(ac);
+ return (NULL);
+}
+DEF_WEAK(getnameinfo_async);
+
+static int
+getnameinfo_async_run(struct asr_query *as, struct asr_result *ar)
+{
+ void *addr;
+ socklen_t addrlen;
+ int r;
+
+ next:
+ switch (as->as_state) {
+
+ case ASR_STATE_INIT:
+
+ /* Make sure the parameters are all valid. */
+
+ if (as->as.ni.sa.sa.sa_family != AF_INET &&
+ as->as.ni.sa.sa.sa_family != AF_INET6) {
+ ar->ar_gai_errno = EAI_FAMILY;
+ async_set_state(as, ASR_STATE_HALT);
+ break;
+ }
+
+ if ((as->as.ni.sa.sa.sa_family == AF_INET &&
+ (SA_LEN(&as->as.ni.sa.sa) != sizeof (as->as.ni.sa.sain))) ||
+ (as->as.ni.sa.sa.sa_family == AF_INET6 &&
+ (SA_LEN(&as->as.ni.sa.sa) != sizeof (as->as.ni.sa.sain6)))) {
+ ar->ar_gai_errno = EAI_FAIL;
+ async_set_state(as, ASR_STATE_HALT);
+ break;
+ }
+
+ /* Set the service name first, if needed. */
+ if (_servname(as) == -1) {
+ ar->ar_gai_errno = EAI_OVERFLOW;
+ async_set_state(as, ASR_STATE_HALT);
+ break;
+ }
+
+ if (as->as.ni.hostname == NULL || as->as.ni.hostnamelen == 0) {
+ ar->ar_gai_errno = 0;
+ async_set_state(as, ASR_STATE_HALT);
+ break;
+ }
+
+ if (as->as.ni.flags & NI_NUMERICHOST) {
+ if (_numerichost(as) == -1) {
+ if (errno == ENOMEM)
+ ar->ar_gai_errno = EAI_MEMORY;
+ else if (errno == ENOSPC)
+ ar->ar_gai_errno = EAI_OVERFLOW;
+ else {
+ ar->ar_errno = errno;
+ ar->ar_gai_errno = EAI_SYSTEM;
+ }
+ } else
+ ar->ar_gai_errno = 0;
+ async_set_state(as, ASR_STATE_HALT);
+ break;
+ }
+
+ if (as->as.ni.sa.sa.sa_family == AF_INET) {
+ addrlen = sizeof(as->as.ni.sa.sain.sin_addr);
+ addr = &as->as.ni.sa.sain.sin_addr;
+ } else {
+ addrlen = sizeof(as->as.ni.sa.sain6.sin6_addr);
+ addr = &as->as.ni.sa.sain6.sin6_addr;
+ }
+
+ /*
+ * Create a subquery to lookup the address.
+ */
+ as->as_subq = _gethostbyaddr_async_ctx(addr, addrlen,
+ as->as.ni.sa.sa.sa_family,
+ as->as_ctx);
+ if (as->as_subq == NULL) {
+ ar->ar_gai_errno = EAI_MEMORY;
+ async_set_state(as, ASR_STATE_HALT);
+ break;
+ }
+
+ async_set_state(as, ASR_STATE_SUBQUERY);
+ break;
+
+ case ASR_STATE_SUBQUERY:
+
+ if ((r = asr_run(as->as_subq, ar)) == ASYNC_COND)
+ return (ASYNC_COND);
+
+ /*
+ * Request done.
+ */
+ as->as_subq = NULL;
+
+ if (ar->ar_hostent == NULL) {
+ if (as->as.ni.flags & NI_NAMEREQD) {
+ ar->ar_gai_errno = EAI_NONAME;
+ } else if (_numerichost(as) == -1) {
+ if (errno == ENOMEM)
+ ar->ar_gai_errno = EAI_MEMORY;
+ else if (errno == ENOSPC)
+ ar->ar_gai_errno = EAI_OVERFLOW;
+ else {
+ ar->ar_errno = errno;
+ ar->ar_gai_errno = EAI_SYSTEM;
+ }
+ } else
+ ar->ar_gai_errno = 0;
+ } else {
+ if (strlcpy(as->as.ni.hostname,
+ ar->ar_hostent->h_name,
+ as->as.ni.hostnamelen) >= as->as.ni.hostnamelen)
+ ar->ar_gai_errno = EAI_OVERFLOW;
+ else
+ ar->ar_gai_errno = 0;
+ free(ar->ar_hostent);
+ }
+
+ async_set_state(as, ASR_STATE_HALT);
+ break;
+
+ case ASR_STATE_HALT:
+ return (ASYNC_DONE);
+
+ default:
+ ar->ar_errno = EOPNOTSUPP;
+ ar->ar_gai_errno = EAI_SYSTEM;
+ async_set_state(as, ASR_STATE_HALT);
+ break;
+ }
+ goto next;
+}
+
+
+/*
+ * Set the service name on the result buffer is not NULL.
+ * return (-1) if the buffer is too small.
+ */
+static int
+_servname(struct asr_query *as)
+{
+ struct servent s;
+#ifdef HAVE_STRUCT_SERVENT_DATA
+ struct servent_data sd;
+#endif
+ int port, r;
+ char *buf = as->as.ni.servname;
+ size_t buflen = as->as.ni.servnamelen;
+
+ if (as->as.ni.servname == NULL || as->as.ni.servnamelen == 0)
+ return (0);
+
+ if (as->as.ni.sa.sa.sa_family == AF_INET)
+ port = as->as.ni.sa.sain.sin_port;
+ else
+ port = as->as.ni.sa.sain6.sin6_port;
+
+ if (!(as->as.ni.flags & NI_NUMERICSERV)) {
+#ifdef HAVE_STRUCT_SERVENT_DATA
+ memset(&sd, 0, sizeof (sd));
+#endif
+#ifdef HAVE_GETSERVBYPORT_R_4_ARGS
+ r = getservbyport_r(port,
+ (as->as.ni.flags & NI_DGRAM) ? "udp" : "tcp",
+ &s, &sd);
+#else
+ r = -1;
+#endif
+ if (r != -1) {
+ r = strlcpy(buf, s.s_name, buflen) >= buflen;
+#ifdef HAVE_ENDSERVENT_R
+ endservent_r(&sd);
+#endif
+ return (r ? -1 : 0);
+ }
+ }
+
+ r = snprintf(buf, buflen, "%u", ntohs(port));
+ if (r < 0 || (size_t)r >= buflen)
+ return (-1);
+
+ return (0);
+}
+
+/*
+ * Write the numeric address
+ */
+static int
+_numerichost(struct asr_query *as)
+{
+ unsigned int ifidx;
+ char scope[IF_NAMESIZE + 1], *ifname;
+ void *addr;
+ char *buf = as->as.ni.hostname;
+ size_t buflen = as->as.ni.hostnamelen;
+
+ if (as->as.ni.sa.sa.sa_family == AF_INET)
+ addr = &as->as.ni.sa.sain.sin_addr;
+ else
+ addr = &as->as.ni.sa.sain6.sin6_addr;
+
+ if (inet_ntop(as->as.ni.sa.sa.sa_family, addr, buf, buflen) == NULL)
+ return (-1); /* errno set */
+
+ if (as->as.ni.sa.sa.sa_family == AF_INET6 &&
+ as->as.ni.sa.sain6.sin6_scope_id) {
+
+ scope[0] = SCOPE_DELIMITER;
+ scope[1] = '\0';
+
+ ifidx = as->as.ni.sa.sain6.sin6_scope_id;
+ ifname = NULL;
+
+ if (IN6_IS_ADDR_LINKLOCAL(&as->as.ni.sa.sain6.sin6_addr) ||
+ IN6_IS_ADDR_MC_LINKLOCAL(&as->as.ni.sa.sain6.sin6_addr) ||
+ IN6_IS_ADDR_MC_NODELOCAL(&as->as.ni.sa.sain6.sin6_addr))
+ ifname = if_indextoname(ifidx, scope + 1);
+
+ if (ifname == NULL)
+ snprintf(scope + 1, sizeof(scope) - 1, "%u", ifidx);
+
+ strlcat(buf, scope, buflen);
+ }
+
+ return (0);
+}
diff --git a/openbsd-compat/libasr/getnetnamadr.c b/openbsd-compat/libasr/getnetnamadr.c
new file mode 100644
index 00000000..141b4a9a
--- /dev/null
+++ b/openbsd-compat/libasr/getnetnamadr.c
@@ -0,0 +1,134 @@
+/* $OpenBSD: getnetnamadr.c,v 1.9 2015/01/16 16:48:51 deraadt Exp $ */
+/*
+ * Copyright (c) 2012 Eric Faurot <eric@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/param.h> /* ALIGN */
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netdb.h>
+
+#include <asr.h>
+#include <errno.h>
+#include <resolv.h>
+#include <stdlib.h>
+#include <string.h>
+
+static void _fillnetent(const struct netent *, struct netent *, char *buf,
+ size_t);
+
+static struct netent _netent;
+static char _entbuf[4096];
+
+static char *_empty[] = { NULL, };
+
+static void
+_fillnetent(const struct netent *e, struct netent *r, char *buf, size_t len)
+{
+ char **ptr, *end, *pos;
+ size_t n, i;
+ int naliases;
+
+ bzero(buf, len);
+ bzero(r, sizeof(*r));
+ r->n_aliases = _empty;
+
+ end = buf + len;
+ ptr = (char **)ALIGN(buf);
+
+ if ((char *)ptr >= end)
+ return;
+
+ for (naliases = 0; e->n_aliases[naliases]; naliases++)
+ ;
+
+ r->n_name = NULL;
+ r->n_addrtype = e->n_addrtype;
+ r->n_net = e->n_net;
+ r->n_aliases = ptr;
+
+ pos = (char *)(ptr + (naliases + 1));
+ if (pos > end)
+ r->n_aliases = _empty;
+
+ n = strlcpy(pos, e->n_name, end - pos);
+ if (n >= end - pos)
+ return;
+ r->n_name = pos;
+ pos += n + 1;
+
+ for (i = 0; i < naliases; i++) {
+ n = strlcpy(pos, e->n_aliases[i], end - pos);
+ if (n >= end - pos)
+ return;
+ r->n_aliases[i] = pos;
+ pos += n + 1;
+ }
+}
+
+struct netent *
+getnetbyname(const char *name)
+{
+ struct asr_query *as;
+ struct asr_result ar;
+
+ res_init();
+
+ as = getnetbyname_async(name, NULL);
+ if (as == NULL) {
+ h_errno = NETDB_INTERNAL;
+ return (NULL);
+ }
+
+ asr_run_sync(as, &ar);
+
+ errno = ar.ar_errno;
+ h_errno = ar.ar_h_errno;
+ if (ar.ar_netent == NULL)
+ return (NULL);
+
+ _fillnetent(ar.ar_netent, &_netent, _entbuf, sizeof(_entbuf));
+ free(ar.ar_netent);
+
+ return (&_netent);
+}
+
+struct netent *
+getnetbyaddr(in_addr_t net, int type)
+{
+ struct asr_query *as;
+ struct asr_result ar;
+
+ res_init();
+
+ as = getnetbyaddr_async(net, type, NULL);
+ if (as == NULL) {
+ h_errno = NETDB_INTERNAL;
+ return (NULL);
+ }
+
+ asr_run_sync(as, &ar);
+
+ errno = ar.ar_errno;
+ h_errno = ar.ar_h_errno;
+ if (ar.ar_netent == NULL)
+ return (NULL);
+
+ _fillnetent(ar.ar_netent, &_netent, _entbuf, sizeof(_entbuf));
+ free(ar.ar_netent);
+
+ return (&_netent);
+}
diff --git a/openbsd-compat/libasr/getnetnamadr_async.c b/openbsd-compat/libasr/getnetnamadr_async.c
new file mode 100644
index 00000000..1e02d008
--- /dev/null
+++ b/openbsd-compat/libasr/getnetnamadr_async.c
@@ -0,0 +1,52 @@
+/* $OpenBSD: getnetnamadr_async.c,v 1.26 2018/04/28 15:16:49 schwarze Exp $ */
+/*
+ * Copyright (c) 2012 Eric Faurot <eric@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "includes.h"
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#include <arpa/nameser.h>
+#include <netdb.h>
+#include <asr.h>
+
+#include "asr_private.h"
+
+struct asr_query *
+getnetbyname_async(const char *name, void *asr)
+{
+ struct asr_query *as;
+
+ if ((as = gethostbyname_async(name, asr)) != NULL)
+ as->as_flags |= ASYNC_GETNET;
+ return (as);
+}
+DEF_WEAK(getnetbyname_async);
+
+struct asr_query *
+getnetbyaddr_async(in_addr_t net, int family, void *asr)
+{
+ struct in_addr in;
+ struct asr_query *as;
+
+ in.s_addr = htonl(net);
+ as = gethostbyaddr_async(&in, sizeof(in), family, asr);
+ if (as != NULL)
+ as->as_flags |= ASYNC_GETNET;
+ return (as);
+}
+DEF_WEAK(getnetbyaddr_async);
diff --git a/openbsd-compat/libasr/getrrsetbyname.c b/openbsd-compat/libasr/getrrsetbyname.c
new file mode 100644
index 00000000..24df2c8b
--- /dev/null
+++ b/openbsd-compat/libasr/getrrsetbyname.c
@@ -0,0 +1,83 @@
+/* $OpenBSD: getrrsetbyname.c,v 1.6 2015/09/14 07:38:37 guenther Exp $ */
+/*
+ * Copyright (c) 2012 Eric Faurot <eric@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netdb.h>
+
+#include <asr.h>
+#include <errno.h>
+#include <resolv.h>
+#include <stdlib.h>
+
+int
+getrrsetbyname(const char *name, unsigned int class, unsigned int type,
+ unsigned int flags, struct rrsetinfo **res)
+{
+ struct asr_query *as;
+ struct asr_result ar;
+ int r, saved_errno = errno;
+
+ res_init();
+
+ as = getrrsetbyname_async(name, class, type, flags, NULL);
+ if (as == NULL) {
+ r = (errno == ENOMEM) ? ERRSET_NOMEMORY : ERRSET_FAIL;
+ errno = saved_errno;
+ return (r);
+ }
+
+ asr_run_sync(as, &ar);
+
+ *res = ar.ar_rrsetinfo;
+
+ return (ar.ar_rrset_errno);
+}
+
+/* from net/getrrsetbyname.c */
+void
+freerrset(struct rrsetinfo *rrset)
+{
+ u_int16_t i;
+
+ if (rrset == NULL)
+ return;
+
+ if (rrset->rri_rdatas) {
+ for (i = 0; i < rrset->rri_nrdatas; i++) {
+ if (rrset->rri_rdatas[i].rdi_data == NULL)
+ break;
+ free(rrset->rri_rdatas[i].rdi_data);
+ }
+ free(rrset->rri_rdatas);
+ }
+
+ if (rrset->rri_sigs) {
+ for (i = 0; i < rrset->rri_nsigs; i++) {
+ if (rrset->rri_sigs[i].rdi_data == NULL)
+ break;
+ free(rrset->rri_sigs[i].rdi_data);
+ }
+ free(rrset->rri_sigs);
+ }
+
+ if (rrset->rri_name)
+ free(rrset->rri_name);
+ free(rrset);
+}
+DEF_WEAK(freerrset);
diff --git a/openbsd-compat/libasr/getrrsetbyname_async.c b/openbsd-compat/libasr/getrrsetbyname_async.c
new file mode 100644
index 00000000..9ff7f6c7
--- /dev/null
+++ b/openbsd-compat/libasr/getrrsetbyname_async.c
@@ -0,0 +1,590 @@
+/* $OpenBSD: getrrsetbyname_async.c,v 1.11 2017/02/23 17:04:02 eric Exp $ */
+/*
+ * Copyright (c) 2012 Eric Faurot <eric@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "includes.h"
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/uio.h>
+#include <netinet/in.h>
+#include <arpa/nameser.h>
+#include <netdb.h>
+
+#include <asr.h>
+#include <errno.h>
+#include <resolv.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "asr_private.h"
+
+static int getrrsetbyname_async_run(struct asr_query *, struct asr_result *);
+static void get_response(struct asr_result *, const char *, int);
+
+struct asr_query *
+getrrsetbyname_async(const char *hostname, unsigned int rdclass,
+ unsigned int rdtype, unsigned int flags, void *asr)
+{
+ struct asr_ctx *ac;
+ struct asr_query *as;
+
+ ac = _asr_use_resolver(asr);
+ if ((as = _asr_async_new(ac, ASR_GETRRSETBYNAME)) == NULL)
+ goto abort; /* errno set */
+ as->as_run = getrrsetbyname_async_run;
+
+ as->as.rrset.flags = flags;
+ as->as.rrset.class = rdclass;
+ as->as.rrset.type = rdtype;
+ as->as.rrset.name = strdup(hostname);
+ if (as->as.rrset.name == NULL)
+ goto abort; /* errno set */
+
+ _asr_ctx_unref(ac);
+ return (as);
+ abort:
+ if (as)
+ _asr_async_free(as);
+
+ _asr_ctx_unref(ac);
+ return (NULL);
+}
+DEF_WEAK(getrrsetbyname_async);
+
+static int
+getrrsetbyname_async_run(struct asr_query *as, struct asr_result *ar)
+{
+ next:
+ switch (as->as_state) {
+
+ case ASR_STATE_INIT:
+
+ /* Check for invalid class and type. */
+ if (as->as.rrset.class > 0xffff || as->as.rrset.type > 0xffff) {
+ ar->ar_rrset_errno = ERRSET_INVAL;
+ async_set_state(as, ASR_STATE_HALT);
+ break;
+ }
+
+ /* Do not allow queries of class or type ANY. */
+ if (as->as.rrset.class == 0xff || as->as.rrset.type == 0xff) {
+ ar->ar_rrset_errno = ERRSET_INVAL;
+ async_set_state(as, ASR_STATE_HALT);
+ break;
+ }
+
+ /* Do not allow flags yet, unimplemented. */
+ if (as->as.rrset.flags) {
+ ar->ar_rrset_errno = ERRSET_INVAL;
+ async_set_state(as, ASR_STATE_HALT);
+ break;
+ }
+
+ /* Create a delegate the lookup to a subquery. */
+ as->as_subq = _res_query_async_ctx(
+ as->as.rrset.name,
+ as->as.rrset.class,
+ as->as.rrset.type,
+ as->as_ctx);
+ if (as->as_subq == NULL) {
+ ar->ar_rrset_errno = ERRSET_FAIL;
+ async_set_state(as, ASR_STATE_HALT);
+ break;
+ }
+
+ async_set_state(as, ASR_STATE_SUBQUERY);
+ break;
+
+ case ASR_STATE_SUBQUERY:
+
+ if ((asr_run(as->as_subq, ar)) == ASYNC_COND)
+ return (ASYNC_COND);
+
+ as->as_subq = NULL;
+
+ /* No packet received.*/
+ if (ar->ar_datalen == -1) {
+ switch (ar->ar_h_errno) {
+ case HOST_NOT_FOUND:
+ ar->ar_rrset_errno = ERRSET_NONAME;
+ break;
+ case NO_DATA:
+ ar->ar_rrset_errno = ERRSET_NODATA;
+ break;
+ default:
+ ar->ar_rrset_errno = ERRSET_FAIL;
+ break;
+ }
+ async_set_state(as, ASR_STATE_HALT);
+ break;
+ }
+
+ /* Got a packet but no answer. */
+ if (ar->ar_count == 0) {
+ free(ar->ar_data);
+ switch (ar->ar_rcode) {
+ case NXDOMAIN:
+ ar->ar_rrset_errno = ERRSET_NONAME;
+ break;
+ case NOERROR:
+ ar->ar_rrset_errno = ERRSET_NODATA;
+ break;
+ default:
+ ar->ar_rrset_errno = ERRSET_FAIL;
+ break;
+ }
+ async_set_state(as, ASR_STATE_HALT);
+ break;
+ }
+
+ get_response(ar, ar->ar_data, ar->ar_datalen);
+ free(ar->ar_data);
+ async_set_state(as, ASR_STATE_HALT);
+ break;
+
+ case ASR_STATE_HALT:
+ if (ar->ar_rrset_errno)
+ ar->ar_rrsetinfo = NULL;
+ return (ASYNC_DONE);
+
+ default:
+ ar->ar_rrset_errno = ERRSET_FAIL;
+ async_set_state(as, ASR_STATE_HALT);
+ break;
+ }
+ goto next;
+}
+
+/* The rest of this file is taken from the orignal implementation. */
+
+/* $OpenBSD: getrrsetbyname_async.c,v 1.11 2017/02/23 17:04:02 eric Exp $ */
+
+/*
+ * Copyright (c) 2001 Jakob Schlyter. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * Portions Copyright (c) 1999-2001 Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM
+ * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
+ * INTERNET SOFTWARE CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
+ * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
+ * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+ * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#define MAXPACKET 1024*64
+
+struct dns_query {
+ char *name;
+ u_int16_t type;
+ u_int16_t class;
+ struct dns_query *next;
+};
+
+struct dns_rr {
+ char *name;
+ u_int16_t type;
+ u_int16_t class;
+ u_int16_t ttl;
+ u_int16_t size;
+ void *rdata;
+ struct dns_rr *next;
+};
+
+struct dns_response {
+ HEADER header;
+ struct dns_query *query;
+ struct dns_rr *answer;
+ struct dns_rr *authority;
+ struct dns_rr *additional;
+};
+
+static struct dns_response *parse_dns_response(const u_char *, int);
+static struct dns_query *parse_dns_qsection(const u_char *, int,
+ const u_char **, int);
+static struct dns_rr *parse_dns_rrsection(const u_char *, int, const u_char **,
+ int);
+
+static void free_dns_query(struct dns_query *);
+static void free_dns_rr(struct dns_rr *);
+static void free_dns_response(struct dns_response *);
+
+static int count_dns_rr(struct dns_rr *, u_int16_t, u_int16_t);
+
+static void
+get_response(struct asr_result *ar, const char *pkt, int pktlen)
+{
+ struct rrsetinfo *rrset = NULL;
+ struct dns_response *response = NULL;
+ struct dns_rr *rr;
+ struct rdatainfo *rdata;
+ unsigned int index_ans, index_sig;
+
+ /* parse result */
+ response = parse_dns_response(pkt, pktlen);
+ if (response == NULL) {
+ ar->ar_rrset_errno = ERRSET_FAIL;
+ goto fail;
+ }
+
+ if (response->header.qdcount != 1) {
+ ar->ar_rrset_errno = ERRSET_FAIL;
+ goto fail;
+ }
+
+ /* initialize rrset */
+ rrset = calloc(1, sizeof(struct rrsetinfo));
+ if (rrset == NULL) {
+ ar->ar_rrset_errno = ERRSET_NOMEMORY;
+ goto fail;
+ }
+ rrset->rri_rdclass = response->query->class;
+ rrset->rri_rdtype = response->query->type;
+ rrset->rri_ttl = response->answer->ttl;
+ rrset->rri_nrdatas = response->header.ancount;
+
+ /* check for authenticated data */
+ if (response->header.ad == 1)
+ rrset->rri_flags |= RRSET_VALIDATED;
+
+ /* copy name from answer section */
+ rrset->rri_name = strdup(response->answer->name);
+ if (rrset->rri_name == NULL) {
+ ar->ar_rrset_errno = ERRSET_NOMEMORY;
+ goto fail;
+ }
+
+ /* count answers */
+ rrset->rri_nrdatas = count_dns_rr(response->answer, rrset->rri_rdclass,
+ rrset->rri_rdtype);
+ rrset->rri_nsigs = count_dns_rr(response->answer, rrset->rri_rdclass,
+ T_RRSIG);
+
+ /* allocate memory for answers */
+ rrset->rri_rdatas = calloc(rrset->rri_nrdatas,
+ sizeof(struct rdatainfo));
+ if (rrset->rri_rdatas == NULL) {
+ ar->ar_rrset_errno = ERRSET_NOMEMORY;
+ goto fail;
+ }
+
+ /* allocate memory for signatures */
+ rrset->rri_sigs = calloc(rrset->rri_nsigs, sizeof(struct rdatainfo));
+ if (rrset->rri_sigs == NULL) {
+ ar->ar_rrset_errno = ERRSET_NOMEMORY;
+ goto fail;
+ }
+
+ /* copy answers & signatures */
+ for (rr = response->answer, index_ans = 0, index_sig = 0;
+ rr; rr = rr->next) {
+
+ rdata = NULL;
+
+ if (rr->class == rrset->rri_rdclass &&
+ rr->type == rrset->rri_rdtype)
+ rdata = &rrset->rri_rdatas[index_ans++];
+
+ if (rr->class == rrset->rri_rdclass &&
+ rr->type == T_RRSIG)
+ rdata = &rrset->rri_sigs[index_sig++];
+
+ if (rdata) {
+ rdata->rdi_length = rr->size;
+ rdata->rdi_data = malloc(rr->size);
+
+ if (rdata->rdi_data == NULL) {
+ ar->ar_rrset_errno = ERRSET_NOMEMORY;
+ goto fail;
+ }
+ memcpy(rdata->rdi_data, rr->rdata, rr->size);
+ }
+ }
+ free_dns_response(response);
+
+ ar->ar_rrsetinfo = rrset;
+ ar->ar_rrset_errno = ERRSET_SUCCESS;
+ return;
+
+fail:
+ if (rrset != NULL)
+ freerrset(rrset);
+ if (response != NULL)
+ free_dns_response(response);
+}
+
+/*
+ * DNS response parsing routines
+ */
+static struct dns_response *
+parse_dns_response(const u_char *answer, int size)
+{
+ struct dns_response *resp;
+ const u_char *cp;
+
+ /* allocate memory for the response */
+ resp = calloc(1, sizeof(*resp));
+ if (resp == NULL)
+ return (NULL);
+
+ /* initialize current pointer */
+ cp = answer;
+
+ /* copy header */
+ memcpy(&resp->header, cp, HFIXEDSZ);
+ cp += HFIXEDSZ;
+
+ /* fix header byte order */
+ resp->header.qdcount = ntohs(resp->header.qdcount);
+ resp->header.ancount = ntohs(resp->header.ancount);
+ resp->header.nscount = ntohs(resp->header.nscount);
+ resp->header.arcount = ntohs(resp->header.arcount);
+
+ /* there must be at least one query */
+ if (resp->header.qdcount < 1) {
+ free_dns_response(resp);
+ return (NULL);
+ }
+
+ /* parse query section */
+ resp->query = parse_dns_qsection(answer, size, &cp,
+ resp->header.qdcount);
+ if (resp->header.qdcount && resp->query == NULL) {
+ free_dns_response(resp);
+ return (NULL);
+ }
+
+ /* parse answer section */
+ resp->answer = parse_dns_rrsection(answer, size, &cp,
+ resp->header.ancount);
+ if (resp->header.ancount && resp->answer == NULL) {
+ free_dns_response(resp);
+ return (NULL);
+ }
+
+ /* parse authority section */
+ resp->authority = parse_dns_rrsection(answer, size, &cp,
+ resp->header.nscount);
+ if (resp->header.nscount && resp->authority == NULL) {
+ free_dns_response(resp);
+ return (NULL);
+ }
+
+ /* parse additional section */
+ resp->additional = parse_dns_rrsection(answer, size, &cp,
+ resp->header.arcount);
+ if (resp->header.arcount && resp->additional == NULL) {
+ free_dns_response(resp);
+ return (NULL);
+ }
+
+ return (resp);
+}
+
+static struct dns_query *
+parse_dns_qsection(const u_char *answer, int size, const u_char **cp, int count)
+{
+ struct dns_query *head, *curr, *prev;
+ int i, length;
+ char name[MAXDNAME];
+
+ for (i = 1, head = NULL, prev = NULL; i <= count; i++, prev = curr) {
+
+ /* allocate and initialize struct */
+ curr = calloc(1, sizeof(struct dns_query));
+ if (curr == NULL) {
+ free_dns_query(head);
+ return (NULL);
+ }
+ if (head == NULL)
+ head = curr;
+ if (prev != NULL)
+ prev->next = curr;
+
+ /* name */
+ length = dn_expand(answer, answer + size, *cp, name,
+ sizeof(name));
+ if (length < 0) {
+ free_dns_query(head);
+ return (NULL);
+ }
+ curr->name = strdup(name);
+ if (curr->name == NULL) {
+ free_dns_query(head);
+ return (NULL);
+ }
+ *cp += length;
+
+ /* type */
+ curr->type = _getshort(*cp);
+ *cp += INT16SZ;
+
+ /* class */
+ curr->class = _getshort(*cp);
+ *cp += INT16SZ;
+ }
+
+ return (head);
+}
+
+static struct dns_rr *
+parse_dns_rrsection(const u_char *answer, int size, const u_char **cp,
+ int count)
+{
+ struct dns_rr *head, *curr, *prev;
+ int i, length;
+ char name[MAXDNAME];
+
+ for (i = 1, head = NULL, prev = NULL; i <= count; i++, prev = curr) {
+
+ /* allocate and initialize struct */
+ curr = calloc(1, sizeof(struct dns_rr));
+ if (curr == NULL) {
+ free_dns_rr(head);
+ return (NULL);
+ }
+ if (head == NULL)
+ head = curr;
+ if (prev != NULL)
+ prev->next = curr;
+
+ /* name */
+ length = dn_expand(answer, answer + size, *cp, name,
+ sizeof(name));
+ if (length < 0) {
+ free_dns_rr(head);
+ return (NULL);
+ }
+ curr->name = strdup(name);
+ if (curr->name == NULL) {
+ free_dns_rr(head);
+ return (NULL);
+ }
+ *cp += length;
+
+ /* type */
+ curr->type = _getshort(*cp);
+ *cp += INT16SZ;
+
+ /* class */
+ curr->class = _getshort(*cp);
+ *cp += INT16SZ;
+
+ /* ttl */
+ curr->ttl = _getlong(*cp);
+ *cp += INT32SZ;
+
+ /* rdata size */
+ curr->size = _getshort(*cp);
+ *cp += INT16SZ;
+
+ /* rdata itself */
+ curr->rdata = malloc(curr->size);
+ if (curr->rdata == NULL) {
+ free_dns_rr(head);
+ return (NULL);
+ }
+ memcpy(curr->rdata, *cp, curr->size);
+ *cp += curr->size;
+ }
+
+ return (head);
+}
+
+static void
+free_dns_query(struct dns_query *p)
+{
+ if (p == NULL)
+ return;
+
+ if (p->name)
+ free(p->name);
+ free_dns_query(p->next);
+ free(p);
+}
+
+static void
+free_dns_rr(struct dns_rr *p)
+{
+ if (p == NULL)
+ return;
+
+ if (p->name)
+ free(p->name);
+ if (p->rdata)
+ free(p->rdata);
+ free_dns_rr(p->next);
+ free(p);
+}
+
+static void
+free_dns_response(struct dns_response *p)
+{
+ if (p == NULL)
+ return;
+
+ free_dns_query(p->query);
+ free_dns_rr(p->answer);
+ free_dns_rr(p->authority);
+ free_dns_rr(p->additional);
+ free(p);
+}
+
+static int
+count_dns_rr(struct dns_rr *p, u_int16_t class, u_int16_t type)
+{
+ int n = 0;
+
+ while (p) {
+ if (p->class == class && p->type == type)
+ n++;
+ p = p->next;
+ }
+
+ return (n);
+}
diff --git a/openbsd-compat/libasr/libasr.la b/openbsd-compat/libasr/libasr.la
new file mode 100644
index 00000000..71346e8a
--- /dev/null
+++ b/openbsd-compat/libasr/libasr.la
@@ -0,0 +1,41 @@
+# libasr.la - a libtool library file
+# Generated by libtool (GNU libtool) 2.4.6
+#
+# Please DO NOT delete this file!
+# It is necessary for linking the library.
+
+# The name that we can dlopen(3).
+dlname='libasr.0.dylib'
+
+# Names of this library.
+library_names='libasr.0.dylib libasr.dylib'
+
+# The name of the static archive.
+old_library='libasr.a'
+
+# Linker flags that cannot go in dependency_libs.
+inherited_linker_flags=' '
+
+# Libraries that this one depends upon.
+dependency_libs=' -L/usr/local/Cellar/openssl@1.1/1.1.1d//lib -L/usr/local/lib -lz -lcrypto -lssl -levent -lresolv'
+
+# Names of additional weak libraries provided by this library
+weak_library_names=''
+
+# Version information for libasr.
+current=0
+age=0
+revision=3
+
+# Is this an already installed library?
+installed=no
+
+# Should we warn about portability when linking against -modules?
+shouldnotlink=no
+
+# Files to dlopen/dlpreopen
+dlopen=''
+dlpreopen=''
+
+# Directory that this library needs to be installed in:
+libdir='/tmp/lib'
diff --git a/openbsd-compat/libasr/res_debug.c b/openbsd-compat/libasr/res_debug.c
new file mode 100644
index 00000000..ca9c5ee0
--- /dev/null
+++ b/openbsd-compat/libasr/res_debug.c
@@ -0,0 +1,2 @@
+/* $OpenBSD: res_debug.c,v 1.1 2012/09/08 11:08:21 eric Exp $ */
+/* NOTHING */
diff --git a/openbsd-compat/libasr/res_init.c b/openbsd-compat/libasr/res_init.c
new file mode 100644
index 00000000..04243c47
--- /dev/null
+++ b/openbsd-compat/libasr/res_init.c
@@ -0,0 +1,103 @@
+/* $OpenBSD: res_init.c,v 1.11 2019/06/17 05:54:45 otto Exp $ */
+/*
+ * Copyright (c) 2012 Eric Faurot <eric@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <arpa/nameser.h>
+#include <netinet/in.h>
+#include <netdb.h>
+
+#include <asr.h>
+#include <resolv.h>
+#include <string.h>
+
+#include "asr_private.h"
+#include "thread_private.h"
+
+
+struct __res_state _res;
+struct __res_state_ext _res_ext;
+
+int h_errno;
+
+int
+res_init(void)
+{
+ static void *resinit_mutex;
+ struct asr_ctx *ac;
+ int i;
+
+ ac = _asr_use_resolver(NULL);
+
+ /*
+ * The first thread to call res_init() will setup the global _res
+ * structure from the async context, not overriding fields set early
+ * by the user.
+ */
+ _MUTEX_LOCK(&resinit_mutex);
+ if (!(_res.options & RES_INIT)) {
+ if (_res.retry == 0)
+ _res.retry = ac->ac_nsretries;
+ if (_res.retrans == 0)
+ _res.retrans = ac->ac_nstimeout;
+ if (_res.options == 0)
+ _res.options = ac->ac_options;
+ if (_res.lookups[0] == '\0')
+ strlcpy(_res.lookups, ac->ac_db, sizeof(_res.lookups));
+
+ for (i = 0; i < ac->ac_nscount && i < MAXNS; i++) {
+ /*
+ * No need to check for length since we copy to a
+ * struct sockaddr_storage with a size of 256 bytes
+ * and sa_len has only 8 bits.
+ */
+ memcpy(&_res_ext.nsaddr_list[i], ac->ac_ns[i],
+ ac->ac_ns[i]->sa_len);
+ if (ac->ac_ns[i]->sa_len <= sizeof(_res.nsaddr_list[i]))
+ memcpy(&_res.nsaddr_list[i], ac->ac_ns[i],
+ ac->ac_ns[i]->sa_len);
+ else
+ memset(&_res.nsaddr_list[i], 0,
+ sizeof(_res.nsaddr_list[i]));
+ }
+ _res.nscount = i;
+ _res.options |= RES_INIT;
+ }
+ _MUTEX_UNLOCK(&resinit_mutex);
+
+ /*
+ * If the program is not threaded, we want to reflect (some) changes
+ * made by the user to the global _res structure.
+ * This is a bit of a hack: if there is already an async query on
+ * this context, it might change things in its back. It is ok
+ * as long as the user only uses the blocking resolver API.
+ * If needed we could consider cloning the context if there is
+ * a running query.
+ */
+ if (!__isthreaded) {
+ ac->ac_nsretries = _res.retry;
+ ac->ac_nstimeout = _res.retrans;
+ ac->ac_options = _res.options;
+ strlcpy(ac->ac_db, _res.lookups, sizeof(ac->ac_db));
+ ac->ac_dbcount = strlen(ac->ac_db);
+ }
+
+ _asr_ctx_unref(ac);
+
+ return (0);
+}
+DEF_WEAK(res_init);
diff --git a/openbsd-compat/libasr/res_mkquery.c b/openbsd-compat/libasr/res_mkquery.c
new file mode 100644
index 00000000..959ecc47
--- /dev/null
+++ b/openbsd-compat/libasr/res_mkquery.c
@@ -0,0 +1,119 @@
+/* $OpenBSD: res_mkquery.c,v 1.13 2019/01/14 06:49:42 otto Exp $ */
+/*
+ * Copyright (c) 2012 Eric Faurot <eric@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/nameser.h> /* for MAXDNAME */
+#include <netdb.h>
+
+#include <asr.h>
+#include <errno.h>
+#include <resolv.h>
+#include <string.h>
+
+#include "asr_private.h"
+
+/* This function is apparently needed by some ports. */
+int
+res_mkquery(int op, const char *dname, int class, int type,
+ const unsigned char *data, int datalen, const unsigned char *newrr,
+ unsigned char *buf, int buflen)
+{
+ struct asr_ctx *ac;
+ struct asr_pack p;
+ struct asr_dns_header h;
+ char fqdn[MAXDNAME];
+ char dn[MAXDNAME];
+
+ /* we currently only support QUERY */
+ if (op != QUERY || data)
+ return (-1);
+
+ if (dname[0] == '\0' || dname[strlen(dname) - 1] != '.') {
+ if (strlcpy(fqdn, dname, sizeof(fqdn)) >= sizeof(fqdn) ||
+ strlcat(fqdn, ".", sizeof(fqdn)) >= sizeof(fqdn))
+ return (-1);
+ dname = fqdn;
+ }
+
+ if (_asr_dname_from_fqdn(dname, dn, sizeof(dn)) == -1)
+ return (-1);
+
+ ac = _asr_use_resolver(NULL);
+
+ memset(&h, 0, sizeof h);
+ h.id = res_randomid();
+ if (ac->ac_options & RES_RECURSE)
+ h.flags |= RD_MASK;
+#ifdef RES_USE_CD
+ if (ac->ac_options & RES_USE_CD)
+ h.flags |= CD_MASK;
+#endif
+ h.qdcount = 1;
+ if (ac->ac_options & (RES_USE_EDNS0 | RES_USE_DNSSEC))
+ h.arcount = 1;
+
+ _asr_pack_init(&p, buf, buflen);
+ _asr_pack_header(&p, &h);
+ _asr_pack_query(&p, type, class, dn);
+ if (ac->ac_options & (RES_USE_EDNS0 | RES_USE_DNSSEC))
+ _asr_pack_edns0(&p, MAXPACKETSZ,
+ ac->ac_options & RES_USE_DNSSEC);
+
+ _asr_ctx_unref(ac);
+
+ if (p.err)
+ return (-1);
+
+ return (p.offset);
+}
+
+/*
+ * This function is not documented, but used by sendmail.
+ * Put here because it uses asr_private.h too.
+ */
+int
+res_querydomain(const char *name,
+ const char *domain,
+ int class,
+ int type,
+ u_char *answer,
+ int anslen)
+{
+ char fqdn[MAXDNAME], ndom[MAXDNAME];
+ size_t n;
+
+ /* we really want domain to end with a dot for now */
+ if (domain && ((n = strlen(domain)) == 0 || domain[n - 1 ] != '.')) {
+ if (strlcpy(ndom, domain, sizeof(ndom)) >= sizeof(ndom) ||
+ strlcat(ndom, ".", sizeof(ndom)) >= sizeof(ndom)) {
+ h_errno = NETDB_INTERNAL;
+ errno = EINVAL;
+ return (-1);
+ }
+ domain = ndom;
+ }
+
+ if (_asr_make_fqdn(name, domain, fqdn, sizeof fqdn) == 0) {
+ h_errno = NETDB_INTERNAL;
+ errno = EINVAL;
+ return (-1);
+ }
+
+ return (res_query(fqdn, class, type, answer, anslen));
+}
diff --git a/openbsd-compat/libasr/res_query.c b/openbsd-compat/libasr/res_query.c
new file mode 100644
index 00000000..3f891416
--- /dev/null
+++ b/openbsd-compat/libasr/res_query.c
@@ -0,0 +1,112 @@
+/* $OpenBSD: res_query.c,v 1.9 2015/10/05 02:57:16 guenther Exp $ */
+/*
+ * Copyright (c) 2012 Eric Faurot <eric@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netdb.h>
+
+#include <asr.h>
+#include <errno.h>
+#include <resolv.h>
+#include <string.h>
+#include <stdlib.h>
+
+int
+res_query(const char *name, int class, int type, u_char *ans, int anslen)
+{
+ struct asr_query *as;
+ struct asr_result ar;
+ size_t len;
+
+ res_init();
+
+ if (ans == NULL || anslen <= 0) {
+ h_errno = NO_RECOVERY;
+ errno = EINVAL;
+ return (-1);
+ }
+
+ as = res_query_async(name, class, type, NULL);
+ if (as == NULL) {
+ if (errno == EINVAL)
+ h_errno = NO_RECOVERY;
+ else
+ h_errno = NETDB_INTERNAL;
+ return (-1); /* errno set */
+ }
+
+ asr_run_sync(as, &ar);
+
+ if (ar.ar_errno)
+ errno = ar.ar_errno;
+ h_errno = ar.ar_h_errno;
+
+ if (ar.ar_h_errno != NETDB_SUCCESS)
+ return (-1);
+
+ len = anslen;
+ if (ar.ar_datalen < len)
+ len = ar.ar_datalen;
+ memmove(ans, ar.ar_data, len);
+ free(ar.ar_data);
+
+ return (ar.ar_datalen);
+}
+DEF_WEAK(res_query);
+
+int
+res_search(const char *name, int class, int type, u_char *ans, int anslen)
+{
+ struct asr_query *as;
+ struct asr_result ar;
+ size_t len;
+
+ res_init();
+
+ if (ans == NULL || anslen <= 0) {
+ h_errno = NO_RECOVERY;
+ errno = EINVAL;
+ return (-1);
+ }
+
+ as = res_search_async(name, class, type, NULL);
+ if (as == NULL) {
+ if (errno == EINVAL)
+ h_errno = NO_RECOVERY;
+ else
+ h_errno = NETDB_INTERNAL;
+ return (-1); /* errno set */
+ }
+
+ asr_run_sync(as, &ar);
+
+ if (ar.ar_errno)
+ errno = ar.ar_errno;
+ h_errno = ar.ar_h_errno;
+
+ if (ar.ar_h_errno != NETDB_SUCCESS)
+ return (-1);
+
+ len = anslen;
+ if (ar.ar_datalen < len)
+ len = ar.ar_datalen;
+ memmove(ans, ar.ar_data, len);
+ free(ar.ar_data);
+
+ return (ar.ar_datalen);
+}
diff --git a/openbsd-compat/libasr/res_search_async.c b/openbsd-compat/libasr/res_search_async.c
new file mode 100644
index 00000000..6436ab85
--- /dev/null
+++ b/openbsd-compat/libasr/res_search_async.c
@@ -0,0 +1,327 @@
+/* $OpenBSD: res_search_async.c,v 1.21 2017/02/27 10:44:46 jca Exp $ */
+/*
+ * Copyright (c) 2012 Eric Faurot <eric@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "includes.h"
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/uio.h>
+#include <arpa/nameser.h>
+#include <netdb.h>
+
+#include <asr.h>
+#include <errno.h>
+#include <resolv.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "asr_private.h"
+
+static int res_search_async_run(struct asr_query *, struct asr_result *);
+static size_t domcat(const char *, const char *, char *, size_t);
+
+/*
+ * Unlike res_query_async(), this function returns a valid packet only if
+ * h_errno is NETDB_SUCCESS.
+ */
+struct asr_query *
+res_search_async(const char *name, int class, int type, void *asr)
+{
+ struct asr_ctx *ac;
+ struct asr_query *as;
+
+ DPRINT("asr: res_search_async(\"%s\", %i, %i)\n", name, class, type);
+
+ ac = _asr_use_resolver(asr);
+ as = _res_search_async_ctx(name, class, type, ac);
+ _asr_ctx_unref(ac);
+
+ return (as);
+}
+DEF_WEAK(res_search_async);
+
+struct asr_query *
+_res_search_async_ctx(const char *name, int class, int type, struct asr_ctx *ac)
+{
+ struct asr_query *as;
+
+ DPRINT("asr: res_search_async_ctx(\"%s\", %i, %i)\n", name, class,
+ type);
+
+ if ((as = _asr_async_new(ac, ASR_SEARCH)) == NULL)
+ goto err; /* errno set */
+ as->as_run = res_search_async_run;
+ if ((as->as.search.name = strdup(name)) == NULL)
+ goto err; /* errno set */
+
+ as->as.search.class = class;
+ as->as.search.type = type;
+
+ return (as);
+ err:
+ if (as)
+ _asr_async_free(as);
+ return (NULL);
+}
+
+#define HERRNO_UNSET -2
+
+static int
+res_search_async_run(struct asr_query *as, struct asr_result *ar)
+{
+ int r;
+ char fqdn[MAXDNAME];
+
+ next:
+ switch (as->as_state) {
+
+ case ASR_STATE_INIT:
+
+ if (as->as.search.name[0] == '\0') {
+ ar->ar_h_errno = NO_DATA;
+ async_set_state(as, ASR_STATE_HALT);
+ break;
+ }
+
+ as->as.search.saved_h_errno = HERRNO_UNSET;
+ async_set_state(as, ASR_STATE_NEXT_DOMAIN);
+ break;
+
+ case ASR_STATE_NEXT_DOMAIN:
+ /*
+ * Reset flags to be able to identify the case in
+ * STATE_SUBQUERY.
+ */
+ as->as_dom_flags = 0;
+
+ r = _asr_iter_domain(as, as->as.search.name, fqdn, sizeof(fqdn));
+ if (r == -1) {
+ async_set_state(as, ASR_STATE_NOT_FOUND);
+ break;
+ }
+ if (r == 0) {
+ ar->ar_errno = EINVAL;
+ ar->ar_h_errno = NO_RECOVERY;
+ ar->ar_datalen = -1;
+ ar->ar_data = NULL;
+ async_set_state(as, ASR_STATE_HALT);
+ break;
+ }
+ as->as_subq = _res_query_async_ctx(fqdn,
+ as->as.search.class, as->as.search.type, as->as_ctx);
+ if (as->as_subq == NULL) {
+ ar->ar_errno = errno;
+ if (errno == EINVAL)
+ ar->ar_h_errno = NO_RECOVERY;
+ else
+ ar->ar_h_errno = NETDB_INTERNAL;
+ ar->ar_datalen = -1;
+ ar->ar_data = NULL;
+ async_set_state(as, ASR_STATE_HALT);
+ break;
+ }
+ async_set_state(as, ASR_STATE_SUBQUERY);
+ break;
+
+ case ASR_STATE_SUBQUERY:
+
+ if ((r = asr_run(as->as_subq, ar)) == ASYNC_COND)
+ return (ASYNC_COND);
+ as->as_subq = NULL;
+
+ if (ar->ar_h_errno == NETDB_SUCCESS) {
+ async_set_state(as, ASR_STATE_HALT);
+ break;
+ }
+
+ /*
+ * The original res_search() does this in the domain search
+ * loop, but only for ECONNREFUSED. I think we can do better
+ * because technically if we get an errno, it means
+ * we couldn't reach any nameserver, so there is no point
+ * in trying further.
+ */
+ if (ar->ar_errno) {
+ async_set_state(as, ASR_STATE_HALT);
+ break;
+ }
+
+ free(ar->ar_data);
+
+ /*
+ * The original resolver does something like this.
+ */
+ if (as->as_dom_flags & ASYNC_DOM_NDOTS)
+ as->as.search.saved_h_errno = ar->ar_h_errno;
+
+ if (as->as_dom_flags & ASYNC_DOM_DOMAIN) {
+ if (ar->ar_h_errno == NO_DATA)
+ as->as_flags |= ASYNC_NODATA;
+ else if (ar->ar_h_errno == TRY_AGAIN)
+ as->as_flags |= ASYNC_AGAIN;
+ }
+
+ async_set_state(as, ASR_STATE_NEXT_DOMAIN);
+ break;
+
+ case ASR_STATE_NOT_FOUND:
+
+ if (as->as.search.saved_h_errno != HERRNO_UNSET)
+ ar->ar_h_errno = as->as.search.saved_h_errno;
+ else if (as->as_flags & ASYNC_NODATA)
+ ar->ar_h_errno = NO_DATA;
+ else if (as->as_flags & ASYNC_AGAIN)
+ ar->ar_h_errno = TRY_AGAIN;
+ /*
+ * Else, we got the ar_h_errno value set by res_query_async()
+ * for the last domain.
+ */
+ ar->ar_datalen = -1;
+ ar->ar_data = NULL;
+ async_set_state(as, ASR_STATE_HALT);
+ break;
+
+ case ASR_STATE_HALT:
+
+ return (ASYNC_DONE);
+
+ default:
+ ar->ar_errno = EOPNOTSUPP;
+ ar->ar_h_errno = NETDB_INTERNAL;
+ async_set_state(as, ASR_STATE_HALT);
+ break;
+ }
+ goto next;
+}
+
+/*
+ * Concatenate a name and a domain name. The result has no trailing dot.
+ * Return the resulting string length, or 0 in case of error.
+ */
+static size_t
+domcat(const char *name, const char *domain, char *buf, size_t buflen)
+{
+ size_t r;
+
+ r = _asr_make_fqdn(name, domain, buf, buflen);
+ if (r == 0)
+ return (0);
+ buf[r - 1] = '\0';
+
+ return (r - 1);
+}
+
+enum {
+ DOM_INIT,
+ DOM_DOMAIN,
+ DOM_DONE
+};
+
+/*
+ * Implement the search domain strategy.
+ *
+ * This function works as a generator that constructs complete domains in
+ * buffer "buf" of size "len" for the given host name "name", according to the
+ * search rules defined by the resolving context. It is supposed to be called
+ * multiple times (with the same name) to generate the next possible domain
+ * name, if any.
+ *
+ * It returns -1 if all possibilities have been exhausted, 0 if there was an
+ * error generating the next name, or the resulting name length.
+ */
+int
+_asr_iter_domain(struct asr_query *as, const char *name, char * buf, size_t len)
+{
+ const char *c;
+ int dots;
+
+ switch (as->as_dom_step) {
+
+ case DOM_INIT:
+ /* First call */
+
+ /*
+ * If "name" is an FQDN, that's the only result and we
+ * don't try anything else.
+ */
+ if (strlen(name) && name[strlen(name) - 1] == '.') {
+ DPRINT("asr: iter_domain(\"%s\") fqdn\n", name);
+ as->as_dom_flags |= ASYNC_DOM_FQDN;
+ as->as_dom_step = DOM_DONE;
+ return (domcat(name, NULL, buf, len));
+ }
+
+ /*
+ * Otherwise, we iterate through the specified search domains.
+ */
+ as->as_dom_step = DOM_DOMAIN;
+ as->as_dom_idx = 0;
+
+ /*
+ * If "name" as enough dots, use it as-is first, as indicated
+ * in resolv.conf(5).
+ */
+ dots = 0;
+ for (c = name; *c; c++)
+ dots += (*c == '.');
+ if (dots >= as->as_ctx->ac_ndots) {
+ DPRINT("asr: iter_domain(\"%s\") ndots\n", name);
+ as->as_dom_flags |= ASYNC_DOM_NDOTS;
+ if (strlcpy(buf, name, len) >= len)
+ return (0);
+ return (strlen(buf));
+ }
+ /* Otherwise, starts using the search domains */
+ /* FALLTHROUGH */
+
+ case DOM_DOMAIN:
+ if (as->as_dom_idx < as->as_ctx->ac_domcount &&
+ (as->as_ctx->ac_options & RES_DNSRCH || (
+ as->as_ctx->ac_options & RES_DEFNAMES &&
+ as->as_dom_idx == 0 &&
+ strchr(name, '.') == NULL))) {
+ DPRINT("asr: iter_domain(\"%s\") domain \"%s\"\n",
+ name, as->as_ctx->ac_dom[as->as_dom_idx]);
+ as->as_dom_flags |= ASYNC_DOM_DOMAIN;
+ return (domcat(name,
+ as->as_ctx->ac_dom[as->as_dom_idx++], buf, len));
+ }
+
+ /* No more domain to try. */
+
+ as->as_dom_step = DOM_DONE;
+
+ /*
+ * If the name was not tried as an absolute name before,
+ * do it now.
+ */
+ if (!(as->as_dom_flags & ASYNC_DOM_NDOTS)) {
+ DPRINT("asr: iter_domain(\"%s\") as is\n", name);
+ as->as_dom_flags |= ASYNC_DOM_ASIS;
+ if (strlcpy(buf, name, len) >= len)
+ return (0);
+ return (strlen(buf));
+ }
+ /* Otherwise, we are done. */
+
+ case DOM_DONE:
+ default:
+ DPRINT("asr: iter_domain(\"%s\") done\n", name);
+ return (-1);
+ }
+}
diff --git a/openbsd-compat/libasr/res_send.c b/openbsd-compat/libasr/res_send.c
new file mode 100644
index 00000000..32c94081
--- /dev/null
+++ b/openbsd-compat/libasr/res_send.c
@@ -0,0 +1,61 @@
+/* $OpenBSD: res_send.c,v 1.8 2014/03/26 18:13:15 eric Exp $ */
+/*
+ * Copyright (c) 2012 Eric Faurot <eric@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netdb.h>
+
+#include <asr.h>
+#include <errno.h>
+#include <resolv.h>
+#include <string.h>
+#include <stdlib.h>
+
+int
+res_send(const u_char *buf, int buflen, u_char *ans, int anslen)
+{
+ struct asr_query *as;
+ struct asr_result ar;
+ size_t len;
+
+ res_init();
+
+ if (ans == NULL || anslen <= 0) {
+ errno = EINVAL;
+ return (-1);
+ }
+
+ as = res_send_async(buf, buflen, NULL);
+ if (as == NULL)
+ return (-1); /* errno set */
+
+ asr_run_sync(as, &ar);
+
+ if (ar.ar_errno) {
+ errno = ar.ar_errno;
+ return (-1);
+ }
+
+ len = anslen;
+ if (ar.ar_datalen < len)
+ len = ar.ar_datalen;
+ memmove(ans, ar.ar_data, len);
+ free(ar.ar_data);
+
+ return (ar.ar_datalen);
+}
diff --git a/openbsd-compat/libasr/res_send_async.c b/openbsd-compat/libasr/res_send_async.c
new file mode 100644
index 00000000..7eeeef48
--- /dev/null
+++ b/openbsd-compat/libasr/res_send_async.c
@@ -0,0 +1,806 @@
+/* $OpenBSD: res_send_async.c,v 1.39 2019/09/28 11:21:07 eric Exp $ */
+/*
+ * Copyright (c) 2012 Eric Faurot <eric@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "includes.h"
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/uio.h>
+#include <netinet/in.h>
+#include <arpa/nameser.h>
+#ifdef HAVE_ARPA_NAMESER_COMPAT_H
+#include <arpa/nameser_compat.h>
+#endif
+#include <netdb.h>
+
+#include <asr.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <poll.h>
+#include <resolv.h> /* for res_random */
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "asr_private.h"
+
+#define OP_QUERY (0)
+
+static int res_send_async_run(struct asr_query *, struct asr_result *);
+static int sockaddr_connect(const struct sockaddr *, int);
+static int udp_send(struct asr_query *);
+static int udp_recv(struct asr_query *);
+static int tcp_write(struct asr_query *);
+static int tcp_read(struct asr_query *);
+static int validate_packet(struct asr_query *);
+static int setup_query(struct asr_query *, const char *, const char *, int, int);
+static int ensure_ibuf(struct asr_query *, size_t);
+static int iter_ns(struct asr_query *);
+
+#define AS_NS_SA(p) ((p)->as_ctx->ac_ns[(p)->as.dns.nsidx - 1])
+
+
+struct asr_query *
+res_send_async(const unsigned char *buf, int buflen, void *asr)
+{
+ struct asr_ctx *ac;
+ struct asr_query *as;
+ struct asr_unpack p;
+ struct asr_dns_header h;
+ struct asr_dns_query q;
+
+ DPRINT_PACKET("asr: res_send_async()", buf, buflen);
+
+ ac = _asr_use_resolver(asr);
+ if ((as = _asr_async_new(ac, ASR_SEND)) == NULL) {
+ _asr_ctx_unref(ac);
+ return (NULL); /* errno set */
+ }
+ as->as_run = res_send_async_run;
+
+ as->as_flags |= ASYNC_EXTOBUF;
+ as->as.dns.obuf = (unsigned char *)buf;
+ as->as.dns.obuflen = buflen;
+ as->as.dns.obufsize = buflen;
+
+ _asr_unpack_init(&p, buf, buflen);
+ _asr_unpack_header(&p, &h);
+ _asr_unpack_query(&p, &q);
+ if (p.err) {
+ errno = EINVAL;
+ goto err;
+ }
+ as->as.dns.reqid = h.id;
+ as->as.dns.type = q.q_type;
+ as->as.dns.class = q.q_class;
+ as->as.dns.dname = strdup(q.q_dname);
+ if (as->as.dns.dname == NULL)
+ goto err; /* errno set */
+
+ _asr_ctx_unref(ac);
+ return (as);
+ err:
+ if (as)
+ _asr_async_free(as);
+ _asr_ctx_unref(ac);
+ return (NULL);
+}
+DEF_WEAK(res_send_async);
+
+/*
+ * Unlike res_query(), this version will actually return the packet
+ * if it has received a valid one (errno == 0) even if h_errno is
+ * not NETDB_SUCCESS. So the packet *must* be freed if necessary.
+ */
+struct asr_query *
+res_query_async(const char *name, int class, int type, void *asr)
+{
+ struct asr_ctx *ac;
+ struct asr_query *as;
+
+ DPRINT("asr: res_query_async(\"%s\", %i, %i)\n", name, class, type);
+
+ ac = _asr_use_resolver(asr);
+ as = _res_query_async_ctx(name, class, type, ac);
+ _asr_ctx_unref(ac);
+
+ return (as);
+}
+DEF_WEAK(res_query_async);
+
+struct asr_query *
+_res_query_async_ctx(const char *name, int class, int type, struct asr_ctx *a_ctx)
+{
+ struct asr_query *as;
+
+ DPRINT("asr: res_query_async_ctx(\"%s\", %i, %i)\n", name, class, type);
+
+ if ((as = _asr_async_new(a_ctx, ASR_SEND)) == NULL)
+ return (NULL); /* errno set */
+ as->as_run = res_send_async_run;
+
+ /* This adds a "." to name if it doesn't already has one.
+ * That's how res_query() behaves (through res_mkquery").
+ */
+ if (setup_query(as, name, NULL, class, type) == -1)
+ goto err; /* errno set */
+
+ return (as);
+
+ err:
+ if (as)
+ _asr_async_free(as);
+
+ return (NULL);
+}
+
+static int
+res_send_async_run(struct asr_query *as, struct asr_result *ar)
+{
+ next:
+ switch (as->as_state) {
+
+ case ASR_STATE_INIT:
+
+ if (as->as_ctx->ac_nscount == 0) {
+ ar->ar_errno = ECONNREFUSED;
+ async_set_state(as, ASR_STATE_HALT);
+ break;
+ }
+
+ async_set_state(as, ASR_STATE_NEXT_NS);
+ break;
+
+ case ASR_STATE_NEXT_NS:
+
+ if (iter_ns(as) == -1) {
+ ar->ar_errno = ETIMEDOUT;
+ async_set_state(as, ASR_STATE_HALT);
+ break;
+ }
+
+ if (as->as_ctx->ac_options & RES_USEVC ||
+ as->as.dns.obuflen > PACKETSZ)
+ async_set_state(as, ASR_STATE_TCP_WRITE);
+ else
+ async_set_state(as, ASR_STATE_UDP_SEND);
+ break;
+
+ case ASR_STATE_UDP_SEND:
+
+ if (udp_send(as) == -1) {
+ async_set_state(as, ASR_STATE_NEXT_NS);
+ break;
+ }
+ async_set_state(as, ASR_STATE_UDP_RECV);
+ ar->ar_cond = ASR_WANT_READ;
+ ar->ar_fd = as->as_fd;
+ ar->ar_timeout = as->as_timeout;
+ return (ASYNC_COND);
+ break;
+
+ case ASR_STATE_UDP_RECV:
+
+ if (udp_recv(as) == -1) {
+ if (errno == ENOMEM) {
+ ar->ar_errno = errno;
+ async_set_state(as, ASR_STATE_HALT);
+ break;
+ }
+ if (errno != EOVERFLOW) {
+ /* Fail or timeout */
+ async_set_state(as, ASR_STATE_NEXT_NS);
+ break;
+ }
+ if (as->as_ctx->ac_options & RES_IGNTC)
+ async_set_state(as, ASR_STATE_PACKET);
+ else
+ async_set_state(as, ASR_STATE_TCP_WRITE);
+ } else
+ async_set_state(as, ASR_STATE_PACKET);
+ break;
+
+ case ASR_STATE_TCP_WRITE:
+
+ switch (tcp_write(as)) {
+ case -1: /* fail or timeout */
+ async_set_state(as, ASR_STATE_NEXT_NS);
+ break;
+ case 0:
+ async_set_state(as, ASR_STATE_TCP_READ);
+ ar->ar_cond = ASR_WANT_READ;
+ ar->ar_fd = as->as_fd;
+ ar->ar_timeout = as->as_timeout;
+ return (ASYNC_COND);
+ case 1:
+ ar->ar_cond = ASR_WANT_WRITE;
+ ar->ar_fd = as->as_fd;
+ ar->ar_timeout = as->as_timeout;
+ return (ASYNC_COND);
+ }
+ break;
+
+ case ASR_STATE_TCP_READ:
+
+ switch (tcp_read(as)) {
+ case -1: /* Fail or timeout */
+ if (errno == ENOMEM) {
+ ar->ar_errno = errno;
+ async_set_state(as, ASR_STATE_HALT);
+ } else
+ async_set_state(as, ASR_STATE_NEXT_NS);
+ break;
+ case 0:
+ async_set_state(as, ASR_STATE_PACKET);
+ break;
+ case 1:
+ ar->ar_cond = ASR_WANT_READ;
+ ar->ar_fd = as->as_fd;
+ ar->ar_timeout = as->as_timeout;
+ return (ASYNC_COND);
+ }
+ break;
+
+ case ASR_STATE_PACKET:
+
+ memmove(&ar->ar_ns, AS_NS_SA(as), SA_LEN(AS_NS_SA(as)));
+ ar->ar_datalen = as->as.dns.ibuflen;
+ ar->ar_data = as->as.dns.ibuf;
+ as->as.dns.ibuf = NULL;
+ ar->ar_errno = 0;
+ ar->ar_rcode = as->as.dns.rcode;
+ async_set_state(as, ASR_STATE_HALT);
+ break;
+
+ case ASR_STATE_HALT:
+
+ if (ar->ar_errno) {
+ ar->ar_h_errno = TRY_AGAIN;
+ ar->ar_count = 0;
+ ar->ar_datalen = -1;
+ ar->ar_data = NULL;
+ } else if (as->as.dns.ancount) {
+ ar->ar_h_errno = NETDB_SUCCESS;
+ ar->ar_count = as->as.dns.ancount;
+ } else {
+ ar->ar_count = 0;
+ switch (as->as.dns.rcode) {
+ case NXDOMAIN:
+ ar->ar_h_errno = HOST_NOT_FOUND;
+ break;
+ case SERVFAIL:
+ ar->ar_h_errno = TRY_AGAIN;
+ break;
+ case NOERROR:
+ ar->ar_h_errno = NO_DATA;
+ break;
+ default:
+ ar->ar_h_errno = NO_RECOVERY;
+ }
+ }
+ return (ASYNC_DONE);
+
+ default:
+
+ ar->ar_errno = EOPNOTSUPP;
+ ar->ar_h_errno = NETDB_INTERNAL;
+ async_set_state(as, ASR_STATE_HALT);
+ break;
+ }
+ goto next;
+}
+
+static int
+sockaddr_connect(const struct sockaddr *sa, int socktype)
+{
+ int errno_save, sock, flags;
+
+ if ((sock = socket(sa->sa_family, socktype, 0)) == -1)
+ goto fail;
+
+ if ((flags = fcntl(sock, F_GETFL, 0)) == -1)
+ goto fail;
+
+ flags |= O_NONBLOCK;
+
+ if ((flags = fcntl(sock, F_SETFL, flags)) == -1)
+ goto fail;
+
+ if (connect(sock, sa, SA_LEN(sa)) == -1) {
+ /*
+ * In the TCP case, the caller will be asked to poll for
+ * POLLOUT so that we start writing the packet in tcp_write()
+ * when the connection is established, or fail there on error.
+ */
+ if (errno == EINPROGRESS)
+ return (sock);
+ goto fail;
+ }
+
+ return (sock);
+
+ fail:
+
+ if (sock != -1) {
+ errno_save = errno;
+ close(sock);
+ errno = errno_save;
+ }
+
+ return (-1);
+}
+
+/*
+ * Prepare the DNS packet for the query type "type", class "class" and domain
+ * name created by the concatenation on "name" and "dom".
+ * Return 0 on success, set errno and return -1 on error.
+ */
+static int
+setup_query(struct asr_query *as, const char *name, const char *dom,
+ int class, int type)
+{
+ struct asr_pack p;
+ struct asr_dns_header h;
+ char fqdn[MAXDNAME];
+ char dname[MAXDNAME];
+
+ if (as->as_flags & ASYNC_EXTOBUF) {
+ errno = EINVAL;
+ DPRINT("attempting to write in user packet");
+ return (-1);
+ }
+
+ if (_asr_make_fqdn(name, dom, fqdn, sizeof(fqdn)) > sizeof(fqdn)) {
+ errno = EINVAL;
+ DPRINT("asr_make_fqdn: name too long\n");
+ return (-1);
+ }
+
+ if (_asr_dname_from_fqdn(fqdn, dname, sizeof(dname)) == -1) {
+ errno = EINVAL;
+ DPRINT("asr_dname_from_fqdn: invalid\n");
+ return (-1);
+ }
+
+ if (as->as.dns.obuf == NULL) {
+ as->as.dns.obufsize = PACKETSZ;
+ as->as.dns.obuf = malloc(as->as.dns.obufsize);
+ if (as->as.dns.obuf == NULL)
+ return (-1); /* errno set */
+ }
+ as->as.dns.obuflen = 0;
+
+ memset(&h, 0, sizeof h);
+ h.id = res_randomid();
+ if (as->as_ctx->ac_options & RES_RECURSE)
+ h.flags |= RD_MASK;
+#ifdef RES_USE_CD
+ if (as->as_ctx->ac_options & RES_USE_CD)
+ h.flags |= CD_MASK;
+#endif
+ h.qdcount = 1;
+ if (as->as_ctx->ac_options & (RES_USE_EDNS0 | RES_USE_DNSSEC))
+ h.arcount = 1;
+
+ _asr_pack_init(&p, as->as.dns.obuf, as->as.dns.obufsize);
+ _asr_pack_header(&p, &h);
+ _asr_pack_query(&p, type, class, dname);
+ if (as->as_ctx->ac_options & (RES_USE_EDNS0 | RES_USE_DNSSEC))
+ _asr_pack_edns0(&p, MAXPACKETSZ,
+ as->as_ctx->ac_options & RES_USE_DNSSEC);
+ if (p.err) {
+ DPRINT("error packing query");
+ errno = EINVAL;
+ return (-1);
+ }
+
+ /* Remember the parameters. */
+ as->as.dns.reqid = h.id;
+ as->as.dns.type = type;
+ as->as.dns.class = class;
+ if (as->as.dns.dname)
+ free(as->as.dns.dname);
+ as->as.dns.dname = strdup(dname);
+ if (as->as.dns.dname == NULL) {
+ DPRINT("strdup");
+ return (-1); /* errno set */
+ }
+ as->as.dns.obuflen = p.offset;
+
+ DPRINT_PACKET("asr_setup_query", as->as.dns.obuf, as->as.dns.obuflen);
+
+ return (0);
+}
+
+/*
+ * Create a connect UDP socket and send the output packet.
+ *
+ * Return 0 on success, or -1 on error (errno set).
+ */
+static int
+udp_send(struct asr_query *as)
+{
+ ssize_t n;
+ int save_errno;
+#ifdef DEBUG
+ char buf[256];
+#endif
+
+ DPRINT("asr: [%p] connecting to %s UDP\n", as,
+ _asr_print_sockaddr(AS_NS_SA(as), buf, sizeof buf));
+
+ as->as_fd = sockaddr_connect(AS_NS_SA(as), SOCK_DGRAM);
+ if (as->as_fd == -1)
+ return (-1); /* errno set */
+
+ n = send(as->as_fd, as->as.dns.obuf, as->as.dns.obuflen, 0);
+ if (n == -1) {
+ save_errno = errno;
+ close(as->as_fd);
+ errno = save_errno;
+ as->as_fd = -1;
+ return (-1);
+ }
+
+ return (0);
+}
+
+/*
+ * Try to receive a valid packet from the current UDP socket.
+ *
+ * Return 0 if a full packet could be read, or -1 on error (errno set).
+ */
+static int
+udp_recv(struct asr_query *as)
+{
+ ssize_t n;
+ int save_errno;
+
+ if (ensure_ibuf(as, MAXPACKETSZ) == -1) {
+ save_errno = errno;
+ close(as->as_fd);
+ errno = save_errno;
+ as->as_fd = -1;
+ return (-1);
+ }
+
+ n = recv(as->as_fd, as->as.dns.ibuf, as->as.dns.ibufsize, 0);
+ save_errno = errno;
+ close(as->as_fd);
+ errno = save_errno;
+ as->as_fd = -1;
+ if (n == -1)
+ return (-1);
+
+ as->as.dns.ibuflen = n;
+
+ DPRINT_PACKET("asr_udp_recv()", as->as.dns.ibuf, as->as.dns.ibuflen);
+
+ if (validate_packet(as) == -1)
+ return (-1); /* errno set */
+
+ return (0);
+}
+
+/*
+ * Write the output packet to the TCP socket.
+ *
+ * Return 0 when all bytes have been sent, 1 there is no buffer space on the
+ * socket or it is not connected yet, or -1 on error (errno set).
+ */
+static int
+tcp_write(struct asr_query *as)
+{
+ struct msghdr msg;
+ struct iovec iov[2];
+ uint16_t len;
+ ssize_t n;
+ size_t offset;
+ int i;
+#ifdef DEBUG
+ char buf[256];
+#endif
+
+ /* First try to connect if not already */
+ if (as->as_fd == -1) {
+ DPRINT("asr: [%p] connecting to %s TCP\n", as,
+ _asr_print_sockaddr(AS_NS_SA(as), buf, sizeof buf));
+ as->as_fd = sockaddr_connect(AS_NS_SA(as), SOCK_STREAM);
+ if (as->as_fd == -1)
+ return (-1); /* errno set */
+/*
+ * Some systems (MacOS X) have SO_NOSIGPIPE instead of MSG_NOSIGNAL.
+ * If neither is available the system is probably broken. We might
+ * want to detect this at configure time.
+ */
+#ifdef SO_NOSIGPIPE
+ i = 1;
+ if (setsockopt(as->as_fd, SOL_SOCKET, SO_NOSIGPIPE, (void *)&i,
+ sizeof(i)) == -1)
+ return (-1); /* errno set */
+#endif
+ as->as.dns.datalen = 0; /* bytes sent */
+ return (1);
+ }
+
+ i = 0;
+
+ /* Prepend de packet length if not sent already. */
+ if (as->as.dns.datalen < sizeof(len)) {
+ offset = 0;
+ len = htons(as->as.dns.obuflen);
+ iov[i].iov_base = (char *)(&len) + as->as.dns.datalen;
+ iov[i].iov_len = sizeof(len) - as->as.dns.datalen;
+ i++;
+ } else
+ offset = as->as.dns.datalen - sizeof(len);
+
+ iov[i].iov_base = as->as.dns.obuf + offset;
+ iov[i].iov_len = as->as.dns.obuflen - offset;
+ i++;
+
+ memset(&msg, 0, sizeof msg);
+ msg.msg_iov = iov;
+ msg.msg_iovlen = i;
+
+ send_again:
+/* See above. */
+#ifndef MSG_NOSIGNAL
+#define MSG_NOSIGNAL 0
+#endif
+ n = sendmsg(as->as_fd, &msg, MSG_NOSIGNAL);
+ if (n == -1) {
+ if (errno == EINTR)
+ goto send_again;
+ goto close; /* errno set */
+ }
+
+ as->as.dns.datalen += n;
+
+ if (as->as.dns.datalen == as->as.dns.obuflen + sizeof(len)) {
+ /* All sent. Prepare for TCP read */
+ as->as.dns.datalen = 0;
+ return (0);
+ }
+
+ /* More data to write */
+ return (1);
+
+close:
+ close(as->as_fd);
+ as->as_fd = -1;
+ return (-1);
+}
+
+/*
+ * Try to read a valid packet from the current TCP socket.
+ *
+ * Return 0 if a full packet could be read, 1 if more data is needed and the
+ * socket must be read again, or -1 on error (errno set).
+ */
+static int
+tcp_read(struct asr_query *as)
+{
+ ssize_t n;
+ size_t offset, len;
+ char *pos;
+ int save_errno, nfds;
+ struct pollfd pfd;
+
+ /* We must read the packet len first */
+ if (as->as.dns.datalen < sizeof(as->as.dns.pktlen)) {
+
+ pos = (char *)(&as->as.dns.pktlen) + as->as.dns.datalen;
+ len = sizeof(as->as.dns.pktlen) - as->as.dns.datalen;
+
+ read_again0:
+ n = read(as->as_fd, pos, len);
+ if (n == -1) {
+ if (errno == EINTR)
+ goto read_again0;
+ goto close; /* errno set */
+ }
+ if (n == 0) {
+ errno = ECONNRESET;
+ goto close;
+ }
+ as->as.dns.datalen += n;
+ if (as->as.dns.datalen < sizeof(as->as.dns.pktlen))
+ return (1); /* need more data */
+
+ as->as.dns.ibuflen = ntohs(as->as.dns.pktlen);
+ if (ensure_ibuf(as, as->as.dns.ibuflen) == -1)
+ goto close; /* errno set */
+
+ pfd.fd = as->as_fd;
+ pfd.events = POLLIN;
+ poll_again:
+ nfds = poll(&pfd, 1, 0);
+ if (nfds == -1) {
+ if (errno == EINTR)
+ goto poll_again;
+ goto close; /* errno set */
+ }
+ if (nfds == 0)
+ return (1); /* no more data available */
+ }
+
+ offset = as->as.dns.datalen - sizeof(as->as.dns.pktlen);
+ pos = as->as.dns.ibuf + offset;
+ len = as->as.dns.ibuflen - offset;
+
+ read_again:
+ n = read(as->as_fd, pos, len);
+ if (n == -1) {
+ if (errno == EINTR)
+ goto read_again;
+ goto close; /* errno set */
+ }
+ if (n == 0) {
+ errno = ECONNRESET;
+ goto close;
+ }
+ as->as.dns.datalen += n;
+
+ /* See if we got all the advertised bytes. */
+ if (as->as.dns.datalen != as->as.dns.ibuflen + sizeof(as->as.dns.pktlen))
+ return (1);
+
+ DPRINT_PACKET("asr_tcp_read()", as->as.dns.ibuf, as->as.dns.ibuflen);
+
+ if (validate_packet(as) == -1)
+ goto close; /* errno set */
+
+ errno = 0;
+close:
+ save_errno = errno;
+ close(as->as_fd);
+ errno = save_errno;
+ as->as_fd = -1;
+ return (errno ? -1 : 0);
+}
+
+/*
+ * Make sure the input buffer is at least "n" bytes long, and allocate or
+ * extend it if necessary. Return 0 on success, or set errno and return -1.
+ */
+static int
+ensure_ibuf(struct asr_query *as, size_t n)
+{
+ char *t;
+
+ if (as->as.dns.ibufsize >= n)
+ return (0);
+
+ t = recallocarray(as->as.dns.ibuf, as->as.dns.ibufsize, n, 1);
+ if (t == NULL)
+ return (-1); /* errno set */
+ as->as.dns.ibuf = t;
+ as->as.dns.ibufsize = n;
+
+ return (0);
+}
+
+/*
+ * Check if the received packet is valid.
+ * Return 0 on success, or set errno and return -1.
+ */
+static int
+validate_packet(struct asr_query *as)
+{
+ struct asr_unpack p;
+ struct asr_dns_header h;
+ struct asr_dns_query q;
+ struct asr_dns_rr rr;
+ int r;
+
+ _asr_unpack_init(&p, as->as.dns.ibuf, as->as.dns.ibuflen);
+
+ _asr_unpack_header(&p, &h);
+ if (p.err)
+ goto inval;
+
+ if (h.id != as->as.dns.reqid) {
+ DPRINT("incorrect reqid\n");
+ goto inval;
+ }
+ if (h.qdcount != 1)
+ goto inval;
+ /* Should be zero, we could allow this */
+ if ((h.flags & Z_MASK) != 0)
+ goto inval;
+ /* Actually, it depends on the request but we only use OP_QUERY */
+ if (OPCODE(h.flags) != OP_QUERY)
+ goto inval;
+ /* Must be a response */
+ if ((h.flags & QR_MASK) == 0)
+ goto inval;
+
+ as->as.dns.rcode = RCODE(h.flags);
+ as->as.dns.ancount = h.ancount;
+
+ _asr_unpack_query(&p, &q);
+ if (p.err)
+ goto inval;
+
+ if (q.q_type != as->as.dns.type ||
+ q.q_class != as->as.dns.class ||
+ strcasecmp(q.q_dname, as->as.dns.dname)) {
+ DPRINT("incorrect type/class/dname '%s' != '%s'\n",
+ q.q_dname, as->as.dns.dname);
+ goto inval;
+ }
+
+ /* Check for truncation */
+ if (h.flags & TC_MASK && !(as->as_ctx->ac_options & RES_IGNTC)) {
+ DPRINT("truncated\n");
+ errno = EOVERFLOW;
+ return (-1);
+ }
+
+ /* Validate the rest of the packet */
+ for (r = h.ancount + h.nscount + h.arcount; r; r--)
+ _asr_unpack_rr(&p, &rr);
+
+ /* Report any error found when unpacking the RRs. */
+ if (p.err) {
+ DPRINT("unpack: %s\n", strerror(p.err));
+ errno = p.err;
+ return (-1);
+ }
+
+ if (p.offset != as->as.dns.ibuflen) {
+ DPRINT("trailing garbage\n");
+ errno = EMSGSIZE;
+ return (-1);
+ }
+
+ return (0);
+
+ inval:
+ errno = EINVAL;
+ return (-1);
+}
+
+/*
+ * Set the async context nameserver index to the next nameserver, cycling
+ * over the list until the maximum retry counter is reached. Return 0 on
+ * success, or -1 if all nameservers were used.
+ */
+static int
+iter_ns(struct asr_query *as)
+{
+ for (;;) {
+ if (as->as.dns.nsloop >= as->as_ctx->ac_nsretries)
+ return (-1);
+
+ as->as.dns.nsidx += 1;
+ if (as->as.dns.nsidx <= as->as_ctx->ac_nscount)
+ break;
+ as->as.dns.nsidx = 0;
+ as->as.dns.nsloop++;
+ DPRINT("asr: iter_ns(): cycle %i\n", as->as.dns.nsloop);
+ }
+
+ as->as_timeout = 1000 * (as->as_ctx->ac_nstimeout << as->as.dns.nsloop);
+ if (as->as.dns.nsloop > 0)
+ as->as_timeout /= as->as_ctx->ac_nscount;
+ if (as->as_timeout < 1000)
+ as->as_timeout = 1000;
+
+ return (0);
+}
diff --git a/openbsd-compat/libasr/sethostent.c b/openbsd-compat/libasr/sethostent.c
new file mode 100644
index 00000000..61fa3e2f
--- /dev/null
+++ b/openbsd-compat/libasr/sethostent.c
@@ -0,0 +1,36 @@
+/* $OpenBSD: sethostent.c,v 1.2 2018/04/28 15:09:35 schwarze Exp $ */
+/*
+ * Copyright (c) 2012 Eric Faurot <eric@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <netdb.h>
+#include <stddef.h>
+
+void
+sethostent(int stayopen)
+{
+}
+
+void
+endhostent(void)
+{
+}
+
+struct hostent *
+gethostent(void)
+{
+ h_errno = NETDB_INTERNAL;
+ return NULL;
+}
diff --git a/openbsd-compat/libasr/thread_private.h b/openbsd-compat/libasr/thread_private.h
new file mode 100644
index 00000000..23951975
--- /dev/null
+++ b/openbsd-compat/libasr/thread_private.h
@@ -0,0 +1,8 @@
+/*
+ *
+ */
+#define __is_threaded (0)
+#define _THREAD_PRIVATE_MUTEX(x)
+#define _THREAD_PRIVATE_MUTEX_LOCK(x)
+#define _THREAD_PRIVATE_MUTEX_UNLOCK(x)
+#define _THREAD_PRIVATE(a, b, c) (c)
diff --git a/openbsd-compat/openbsd-compat.h b/openbsd-compat/openbsd-compat.h
index 6c73e5b5..dcb643f1 100644
--- a/openbsd-compat/openbsd-compat.h
+++ b/openbsd-compat/openbsd-compat.h
@@ -96,15 +96,13 @@ const char *inet_ntop(int af, const void *src, char *dst, socklen_t size);
char *strsep(char **stringp, const char *delim);
#endif
-#ifndef HAVE_SETPROCTITLE
+#ifdef NEED_SETPROCTITLE
void setproctitle(const char *fmt, ...);
void compat_init_setproctitle(int argc, char *argv[]);
#endif
#if !defined(HAVE_GETOPT) || !defined(HAVE_GETOPT_OPTRESET)
int BSDgetopt(int argc, char * const *argv, const char *opts);
-char *BSDoptarg; /* argument associated with option */
-int BSDoptind; /* index into parent argv vector */
#endif
/* Home grown routines */
@@ -122,15 +120,19 @@ int getpeereid(int , uid_t *, gid_t *);
unsigned int arc4random(void);
#endif
-#if defined(HAVE_ARC4RANDOM_STIR)
+#if 0
+#if defined(LIBRESSL_VERSION_NUMBER)
+# define arc4random_stir()
+#elif defined(HAVE_ARC4RANDOM_STIR)
void arc4random_stir(void);
-#elif defined(HAVE_ARC4RANDOM) || defined(LIBRESSL_VERSION_NUMBER)
+#elif defined(HAVE_ARC4RANDOM)
/* Recent system/libressl implementation; no need for explicit stir */
# define arc4random_stir()
#else
/* openbsd-compat/arc4random.c provides arc4random_stir() */
void arc4random_stir(void);
#endif
+#endif
#if !defined(HAVE_ARC4RANDOM_BUF) || defined(LIBRESSL_VERSION_NUMBER)
void arc4random_buf(void *, size_t);
@@ -332,6 +334,8 @@ const char *strerror(int);
int usleep(unsigned int useconds);
#endif
+int pipe2(int pipefd[2], int flags);
+
char *get_progname(char *);
diff --git a/openbsd-compat/pipe2.c b/openbsd-compat/pipe2.c
new file mode 100644
index 00000000..fd5c3f49
--- /dev/null
+++ b/openbsd-compat/pipe2.c
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2020 Gilles Chehade <gilles@poolp.org>
+ * Copyright (C) 2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "includes.h"
+
+#include <fcntl.h>
+#include <unistd.h>
+
+int
+pipe2(int pipefd[2], int flags)
+{
+ if (pipe(pipefd) == -1)
+ return -1;
+
+ if ((flags & O_NONBLOCK) &&
+ (fcntl(pipefd[0], F_SETFL, O_NONBLOCK) == -1 ||
+ fcntl(pipefd[1], F_SETFL, O_NONBLOCK) == -1)) {
+ close(pipefd[0]);
+ close(pipefd[1]);
+ return -1;
+ }
+
+ if ((flags & O_CLOEXEC) &&
+ (fcntl(pipefd[0], F_SETFD, FD_CLOEXEC) == -1 ||
+ fcntl(pipefd[1], F_SETFD, FD_CLOEXEC) == -1)) {
+ close(pipefd[0]);
+ close(pipefd[1]);
+ return -1;
+ }
+
+ return 0;
+}
diff --git a/openbsd-compat/res_hnok.c b/openbsd-compat/res_hnok.c
new file mode 100644
index 00000000..a4b54baf
--- /dev/null
+++ b/openbsd-compat/res_hnok.c
@@ -0,0 +1,169 @@
+/* $OpenBSD: res_comp.c,v 1.14 2008/04/16 22:35:23 deraadt Exp $ */
+
+/*
+ * ++Copyright++ 1985, 1993
+ * -
+ * Copyright (c) 1985, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * -
+ * Portions Copyright (c) 1993 by Digital Equipment Corporation.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies, and that
+ * the name of Digital Equipment Corporation not be used in advertising or
+ * publicity pertaining to distribution of the document or software without
+ * specific, written prior permission.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL
+ * WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DIGITAL EQUIPMENT
+ * CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
+ * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
+ * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+ * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+ * SOFTWARE.
+ * -
+ * --Copyright--
+ */
+
+/* OPENBSD ORIGINAL: lib/libc/net/res_comp.c */
+
+#include "includes.h"
+
+/*
+ * Verify that a domain name uses an acceptable character set.
+ */
+
+/*
+ * Note the conspicuous absence of ctype macros in these definitions. On
+ * non-ASCII hosts, we can't depend on string literals or ctype macros to
+ * tell us anything about network-format data. The rest of the BIND system
+ * is not careful about this, but for some reason, we're doing it right here.
+ */
+#define PERIOD 0x2e
+#define hyphenchar(c) ((c) == 0x2d)
+#define bslashchar(c) ((c) == 0x5c)
+#define underscorechar(c) ((c) == 0x5f)
+#define periodchar(c) ((c) == PERIOD)
+#define asterchar(c) ((c) == 0x2a)
+#define alphachar(c) (((c) >= 0x41 && (c) <= 0x5a) \
+ || ((c) >= 0x61 && (c) <= 0x7a))
+#define digitchar(c) ((c) >= 0x30 && (c) <= 0x39)
+
+#define borderchar(c) (alphachar(c) || digitchar(c))
+#define middlechar(c) (borderchar(c) || hyphenchar(c) || underscorechar(c))
+#define domainchar(c) ((c) > 0x20 && (c) < 0x7f)
+
+int
+res_hnok(const char *dn)
+{
+ int pch = PERIOD, ch = *dn++;
+
+ while (ch != '\0') {
+ int nch = *dn++;
+
+ if (periodchar(ch)) {
+ ;
+ } else if (periodchar(pch)) {
+ if (!borderchar(ch))
+ return (0);
+ } else if (periodchar(nch) || nch == '\0') {
+ if (!borderchar(ch))
+ return (0);
+ } else {
+ if (!middlechar(ch))
+ return (0);
+ }
+ pch = ch, ch = nch;
+ }
+ return (1);
+}
+
+#if 0
+
+/*
+ * hostname-like (A, MX, WKS) owners can have "*" as their first label
+ * but must otherwise be as a host name.
+ */
+int
+res_ownok(const char *dn)
+{
+ if (asterchar(dn[0])) {
+ if (periodchar(dn[1]))
+ return (res_hnok(dn+2));
+ if (dn[1] == '\0')
+ return (1);
+ }
+ return (res_hnok(dn));
+}
+
+/*
+ * SOA RNAMEs and RP RNAMEs can have any printable character in their first
+ * label, but the rest of the name has to look like a host name.
+ */
+int
+res_mailok(const char *dn)
+{
+ int ch, escaped = 0;
+
+ /* "." is a valid missing representation */
+ if (*dn == '\0')
+ return(1);
+
+ /* otherwise <label>.<hostname> */
+ while ((ch = *dn++) != '\0') {
+ if (!domainchar(ch))
+ return (0);
+ if (!escaped && periodchar(ch))
+ break;
+ if (escaped)
+ escaped = 0;
+ else if (bslashchar(ch))
+ escaped = 1;
+ }
+ if (periodchar(ch))
+ return (res_hnok(dn));
+ return(0);
+}
+
+/*
+ * This function is quite liberal, since RFC 1034's character sets are only
+ * recommendations.
+ */
+int
+res_dnok(const char *dn)
+{
+ int ch;
+
+ while ((ch = *dn++) != '\0')
+ if (!domainchar(ch))
+ return (0);
+ return (1);
+}
+
+#endif /* if 0 */
diff --git a/openbsd-compat/res_randomid.c b/openbsd-compat/res_randomid.c
new file mode 100644
index 00000000..c848c41d
--- /dev/null
+++ b/openbsd-compat/res_randomid.c
@@ -0,0 +1,13 @@
+#include "includes.h"
+
+#include <time.h>
+
+unsigned int
+res_randomid(void)
+{
+ struct timespec ts;
+
+ /* This is from musl C library */
+ clock_gettime(CLOCK_MONOTONIC, &ts);
+ return ts.tv_nsec + ts.tv_nsec / 65536UL & 0xffff;
+}
diff --git a/openbsd-compat/setproctitle.c b/openbsd-compat/setproctitle.c
index eef70c14..71e1595e 100644
--- a/openbsd-compat/setproctitle.c
+++ b/openbsd-compat/setproctitle.c
@@ -68,8 +68,7 @@ static size_t argv_env_len = 0;
void
compat_init_setproctitle(int argc, char *argv[])
{
-#if !defined(HAVE_SETPROCTITLE) && \
- defined(SPT_TYPE) && SPT_TYPE == SPT_REUSEARGV
+#if defined(SPT_TYPE) && SPT_TYPE == SPT_REUSEARGV
extern char **environ;
char *lastargv = NULL;
char **envp = environ;
@@ -156,8 +155,6 @@ setproctitle(const char *fmt, ...)
pst.pst_command = ptitle;
pstat(PSTAT_SETCMD, pst, strlen(ptitle), 0, 0);
#elif SPT_TYPE == SPT_REUSEARGV
-/* debug("setproctitle: copy \"%s\" into len %d",
- buf, argv_env_len); */
len = strlcpy(argv_start, ptitle, argv_env_len);
for(; len < argv_env_len; len++)
argv_start[len] = SPT_PADCHAR;
diff --git a/regress/config/Makefile b/regress/config/Makefile
deleted file mode 100644
index ba2dded8..00000000
--- a/regress/config/Makefile
+++ /dev/null
@@ -1,17 +0,0 @@
-FILES = test0.conf
-FILES+= test1.conf
-FILES+= test2.conf
-FILES+= test3.conf
-FILES+= test4.conf
-FILES+= test5.conf
-FILES+= test6.conf
-FILES+= test7.conf
-FILES+= test8.conf
-FILES+= test9.conf
-FILES+= test10.conf
-FILES+= test11.conf
-
-test:
-.for FILE in $(FILES)
- smtpd -n -f $(FILE)
-.endfor
diff --git a/regress/config/include.conf b/regress/config/include.conf
deleted file mode 100644
index 667bc640..00000000
--- a/regress/config/include.conf
+++ /dev/null
@@ -1 +0,0 @@
-table sources { 127.0.0.1 }
diff --git a/regress/config/test0.conf b/regress/config/test0.conf
deleted file mode 100644
index cbd2a2f1..00000000
--- a/regress/config/test0.conf
+++ /dev/null
@@ -1,3 +0,0 @@
-listen on lo0
-
-accept for local deliver to mbox
diff --git a/regress/config/test1.conf b/regress/config/test1.conf
deleted file mode 100644
index 8983f6c8..00000000
--- a/regress/config/test1.conf
+++ /dev/null
@@ -1,5 +0,0 @@
-listen on lo0
-
-include "include.conf"
-
-accept from <sources> for local deliver to mbox
diff --git a/regress/config/test10.conf b/regress/config/test10.conf
deleted file mode 100644
index b40480d6..00000000
--- a/regress/config/test10.conf
+++ /dev/null
@@ -1,6 +0,0 @@
-listen on lo0
-
-accept from any for local deliver to mbox
-accept from any for local deliver to maildir
-accept from any for local deliver to maildir "~/Maildir"
-accept from any for local deliver to mda "/bin/cat"
diff --git a/regress/config/test11.conf b/regress/config/test11.conf
deleted file mode 100644
index 3b43f72d..00000000
--- a/regress/config/test11.conf
+++ /dev/null
@@ -1,25 +0,0 @@
-listen on lo0
-
-accept for any relay
-accept for any relay as example
-accept for any relay as "@example.org"
-accept for any relay as example@example.org
-
-accept for any relay backup mx1.example.org
-accept for any relay backup mx1.example.org as example
-accept for any relay backup mx1.example.org as "@example.org"
-accept for any relay backup mx1.example.org as example@example.org
-
-accept for any relay via mx1.example.org
-accept for any relay via smtp://mx1.example.org
-accept for any relay via smtp://mx1.example.org:25
-accept for any relay via tls://mx1.example.org:25
-accept for any relay via smtps://mx1.example.org
-
-accept for any relay via mx1.example.org as example@example.org
-accept for any relay via smtp://mx1.example.org as example@example.org
-accept for any relay via smtp://mx1.example.org:25 as example@example.org
-accept for any relay via tls://mx1.example.org:25 as example@example.org
-accept for any relay via smtps://mx1.example.org as example@example.org
-
-
diff --git a/regress/config/test2.conf b/regress/config/test2.conf
deleted file mode 100644
index dc67a4e4..00000000
--- a/regress/config/test2.conf
+++ /dev/null
@@ -1,5 +0,0 @@
-listen on lo0
-
-expire 4d
-
-accept for local deliver to mbox
diff --git a/regress/config/test3.conf b/regress/config/test3.conf
deleted file mode 100644
index 8ca91bf4..00000000
--- a/regress/config/test3.conf
+++ /dev/null
@@ -1,5 +0,0 @@
-listen on lo0
-
-hostname test.org
-
-accept for local deliver to mbox
diff --git a/regress/config/test4.conf b/regress/config/test4.conf
deleted file mode 100644
index 6e5f4b04..00000000
--- a/regress/config/test4.conf
+++ /dev/null
@@ -1,4 +0,0 @@
-listen on lo0 port 25
-listen on lo0 port smtp
-
-accept for local deliver to mbox
diff --git a/regress/config/test5.conf b/regress/config/test5.conf
deleted file mode 100644
index dc1c42ad..00000000
--- a/regress/config/test5.conf
+++ /dev/null
@@ -1,9 +0,0 @@
-listen on lo0 port 25
-listen on lo0 port smtp
-
-table aliases_dynamic "/etc/mail/aliases"
-table aliases_static { root => test }
-
-accept for local alias <aliases_dynamic> deliver to mbox
-accept for local alias <aliases_static> deliver to mbox
-accept for local alias { k => v } deliver to mbox
diff --git a/regress/config/test6.conf b/regress/config/test6.conf
deleted file mode 100644
index a2a6ded2..00000000
--- a/regress/config/test6.conf
+++ /dev/null
@@ -1,5 +0,0 @@
-listen on lo0
-
-max-message-size 1G
-
-accept for local deliver to mbox
diff --git a/regress/config/test7.conf b/regress/config/test7.conf
deleted file mode 100644
index 274273bd..00000000
--- a/regress/config/test7.conf
+++ /dev/null
@@ -1,12 +0,0 @@
-listen on lo0
-
-table sources { 192.168.1.2 }
-
-accept from any for local deliver to mbox
-accept from local for local deliver to mbox
-accept from 192.168.1.0/24 for local deliver to mbox
-accept from 192.168.1.1 for local deliver to mbox
-accept from { 192.168.1.1 } for local deliver to mbox
-accept from { 192.168.1.1, 192.168.1.2 } for local deliver to mbox
-accept from <sources> for local deliver to mbox
-
diff --git a/regress/config/test8.conf b/regress/config/test8.conf
deleted file mode 100644
index 9cf0c269..00000000
--- a/regress/config/test8.conf
+++ /dev/null
@@ -1,13 +0,0 @@
-listen on lo0
-
-table aliases "/etc/mail/aliases"
-table domains { example.org, example.com }
-
-accept from any for any deliver to mbox
-accept from any for local deliver to mbox
-accept from any for domain example.org deliver to mbox
-accept from any for domain example.org alias <aliases> deliver to mbox
-accept from any for domain <domains> deliver to mbox
-accept from any for domain <domains> alias <aliases> deliver to mbox
-accept from any for domain { example.org, example.com } deliver to mbox
-accept from any for domain { example.org, example.com } alias <aliases> deliver to mbox
diff --git a/regress/config/test9.conf b/regress/config/test9.conf
deleted file mode 100644
index 7bcb1fcb..00000000
--- a/regress/config/test9.conf
+++ /dev/null
@@ -1,8 +0,0 @@
-listen on lo0
-
-table vusers "/etc/mail/aliases"
-table domains { example.org, example.com }
-accept from any for domain "*" virtual <vusers> deliver to mbox
-accept from any for domain example.org virtual <vusers> deliver to mbox
-accept from any for domain <domains> virtual <vusers> deliver to mbox
-accept from any for domain { example.org, example.com } virtual <vusers> deliver to mbox
diff --git a/regress/expand/Makefile b/regress/expand/Makefile
deleted file mode 100644
index aa1781c9..00000000
--- a/regress/expand/Makefile
+++ /dev/null
@@ -1,6 +0,0 @@
-PROG= expand
-NOMAN= 1
-
-LDFLAGS+= -lutil
-
-.include <bsd.prog.mk>
diff --git a/regress/expand/expand.c b/regress/expand/expand.c
deleted file mode 100644
index bfed154b..00000000
--- a/regress/expand/expand.c
+++ /dev/null
@@ -1,129 +0,0 @@
-/* $OpenBSD: expand.c,v 1.18 2012/10/10 18:02:37 eric Exp $ */
-
-/*
- * Copyright (c) 2009 Gilles Chehade <gilles@poolp.org>
- * Copyright (c) 2012 Eric Faurot <eric@openbsd.org>
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <err.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <util.h>
-
-#define MAX_LINE_SIZE 2048
-
-static char *
-strip(char *s)
-{
- size_t l;
-
- while (*s == ' ' || *s == '\t')
- s++;
-
- for (l = strlen(s); l; l--) {
- if (s[l-1] != ' ' && s[l-1] != '\t')
- break;
- s[l-1] = '\0';
- }
-
- return (s);
-}
-
-static int
-expand_line_split(char **line, char **ret)
-{
- static char buffer[MAX_LINE_SIZE];
- int esc, i, dq, sq;
- char *s;
-
- bzero(buffer, sizeof buffer);
- esc = dq = sq = i = 0;
- for (s = *line; (*s) && (i < (int)sizeof(buffer)); ++s) {
- if (esc) {
- buffer[i++] = *s;
- esc = 0;
- continue;
- }
- if (*s == '\\') {
- esc = 1;
- continue;
- }
- if (*s == ',' && !dq && !sq) {
- *ret = buffer;
- *line = s+1;
- return (1);
- }
-
- buffer[i++] = *s;
- esc = 0;
-
- if (*s == '"' && !sq)
- dq ^= 1;
- if (*s == '\'' && !dq)
- sq ^= 1;
- }
-
- if (esc || dq || sq || i == sizeof(buffer))
- return (-1);
-
- *ret = buffer;
- *line = s;
- return (i ? 1 : 0);
-}
-
-int
-expand_line(const char *s)
-{
- char buffer[MAX_LINE_SIZE];
- char *p, *subrcpt;
- int ret;
-
- bzero(buffer, sizeof buffer);
- if (strlcpy(buffer, s, sizeof buffer) >= sizeof buffer)
- return 0;
-
- p = buffer;
- while ((ret = expand_line_split(&p, &subrcpt)) > 0) {
- printf(" -> [%s]", subrcpt);
- printf(" (%s)\n", strip(subrcpt));
- }
-
- if (ret >= 0)
- return 1;
- return 0;
-}
-
-int
-main(int argc, char **argv)
-{
- FILE *fp;
- char *line;
- size_t len;
- size_t lineno;
-
- fp = fopen(argv[1], "r");
- if (fp == NULL)
- err(1, "fopen");
-
- while ((line = fparseln(fp, &len, &lineno, NULL, 0)) != NULL) {
- printf("=> [%s]\n", line);
- if (! expand_line(line))
- printf("error!\n");
- free(line);
- }
-
- return (0);
-}
diff --git a/regress/expand/test0 b/regress/expand/test0
deleted file mode 100644
index 456f6a67..00000000
--- a/regress/expand/test0
+++ /dev/null
@@ -1,7 +0,0 @@
-#
-/dev/null # test
-/dev/null, eric, foo, bar, |/some/mda "test,comma"
-a b \, \" \' d
-a"bc'd", e'fg"h'i, "zer,tyu", 'qsd,fgh'
-lsf qls mksq jmlq ksjmlsq kf
-lskdjf ,,fsk ,foskfs,,,fsdf,s,f,sf
diff --git a/regress/filters/Makefile b/regress/filters/Makefile
deleted file mode 100644
index dcbb48d2..00000000
--- a/regress/filters/Makefile
+++ /dev/null
@@ -1,9 +0,0 @@
-CFLAGS = -I../../smtpd/
-LDFLAGS = -L../../smtpd/libsmtpdfilter -lsmtpdfilter -levent -lutil
-
-SRCS = filter.c
-
-INSTALLPATH= /usr/libexec/smtpd
-
-all:
- $(CC) -o $(INSTALLPATH)/filter $(CFLAGS) $(SRCS) $(LDFLAGS)
diff --git a/regress/smtp/test.base b/regress/smtp/test.base
deleted file mode 100755
index 02e76cdb..00000000
--- a/regress/smtp/test.base
+++ /dev/null
@@ -1,56 +0,0 @@
-#! /usr/bin/smtpscript
-#
-# This file is simply used to test the grammar and the behaviour of
-# the scripting engine. Not SMTP related.
-#
-
-test-case no-autoconnect {
-}
-
-test-case no-autoconnect expect fail {
- fail "on purpose"
-}
-
-test-case no-autoconnect {
- fail "ahaha! I failed"
-}
-
-test-case no-autoconnect skip {
- fail "should be skipped!"
-}
-
-test-case no-autoconnect {
- sleep 500
-}
-
-test-case no-autoconnect {
- connect "localhost" port 25
-}
-
-proc proc0 %foo %bar {
- sleep 1
- sleep 2
- sleep 3
-}
-
-proc proc1 {
- noop
-}
-
-test-case no-autoconnect {
-
- repeat 2 repeat 3 repeat 4 sleep 7
- sleep 8
- {
- call proc0
- sleep 10
- }
-
- repeat 5 random {
- call proc1
- { sleep 1 }
- { sleep 2 }
- }
-
- random {}
-}
diff --git a/regress/smtp/test.mailfrom b/regress/smtp/test.mailfrom
deleted file mode 100644
index 0420f1c2..00000000
--- a/regress/smtp/test.mailfrom
+++ /dev/null
@@ -1,110 +0,0 @@
-proc init-helo {
- expect smtp ok
- writeln "HELO regress"
- expect smtp helo
-}
-
-# Allow emtpy MAIL FROM address (bounce messages)
-test-case name "mailfrom.empty" {
- call init-helo
- writeln "MAIL FROM: <>"
- expect smtp ok
-}
-
-# Allow emtpy MAIL FROM address (bounce messages)
-test-case name "mailfrom.at-sign" {
- call init-helo
- writeln "MAIL FROM: <@>"
- expect smtp ok
-}
-
-# Reject address without an '@'
-test-case name "mailfrom.no-at-sign" {
- call init-helo
- writeln "MAIL FROM: <a>"
- expect smtp permfail
-}
-
-# Reject address with empty domain
-test-case name "mailfrom.no-domain" {
- call init-helo
- writeln "MAIL FROM: <a@>"
- expect smtp permfail
-}
-
-# Reject address with empty user
-test-case name "mailfrom.no-user" {
- call init-helo
- writeln "MAIL FROM: <@a>"
- expect smtp permfail
-}
-
-# Accept address after source-route stripping
-test-case name "mailfrom.src-route" {
- call init-helo
- writeln "MAIL FROM: <whatever:a@a>"
- expect smtp ok
-}
-
-# Reject address with empty user after source-route stripping
-test-case name "mailfrom.src-route-no-user" {
- call init-helo
- writeln "MAIL FROM: <a:@a>"
- expect smtp permfail
-}
-
-# Reject space as user
-test-case name "mailfrom.space-as-user" {
- call init-helo
- writeln "MAIL FROM: < @a>"
- expect smtp permfail
-}
-
-# Reject space as domain
-test-case name "mailfrom.space-as-domain" {
- call init-helo
- writeln "MAIL FROM: <a@ >"
- expect smtp permfail
-}
-
-# Reject unsupported options
-test-case name "mailfrom.option" {
- call init-helo
- writeln "MAIL FROM: <user@domain> OPT"
- expect smtp permfail
-}
-
-# Allow SIZE=*
-test-case name "mailfrom.option-size" {
- call init-helo
- writeln "MAIL FROM: <user@domain> SIZE=3005"
- expect smtp ok
-}
-
-# Allow BODY=7BIT
-test-case name "mailfrom.option-7bit" {
- call init-helo
- writeln "MAIL FROM: <user@domain> BODY=7BIT"
- expect smtp ok
-}
-
-# Allow BODY=8BITMIME
-test-case name "mailfrom.option-8bitmime" {
- call init-helo
- writeln "MAIL FROM: <user@domain> BODY=8BITMIME"
- expect smtp ok
-}
-
-# Allow multiple options
-test-case name "mailfrom.option-8bitmime-size" {
- call init-helo
- writeln "MAIL FROM: <user@domain> BODY=8BITMIME SIZE=100"
- expect smtp ok
-}
-
-# Allow AUTH= (ignored)
-test-case name "mailfrom.option-auth" {
- call init-helo
- writeln "MAIL FROM: <user@domain> AUTH=WHATEVER"
- expect smtp ok
-}
diff --git a/regress/smtp/test.rcptto b/regress/smtp/test.rcptto
deleted file mode 100644
index f6cfd888..00000000
--- a/regress/smtp/test.rcptto
+++ /dev/null
@@ -1,112 +0,0 @@
-proc init-helo-mailfrom {
- expect smtp ok
- writeln "HELO regress"
- expect smtp helo
- writeln "MAIL FROM: <>"
- expect smtp ok
-}
-
-# RCPT TO can't be empty
-test-case name "rcptto.empty" {
- call init-helo-mailfrom
- writeln "RCPT TO: <>"
- expect smtp permfail
-}
-
-# RCPT TO can't be empty
-test-case name "rcptto.at-sign" {
- call init-helo-mailfrom
- writeln "RCPT TO: <@>"
- expect smtp permfail
-}
-
-# Reject address without a '@'
-test-case name "rcptto.no-at-sign" {
- call init-helo-mailfrom
- writeln "RCPT TO: <a>"
- expect smtp permfail
-}
-
-# Reject address with empty domain
-test-case name "rcptto.no-domain" {
- call init-helo-mailfrom
- writeln "RCPT TO: <a@>"
- expect smtp permfail
-}
-
-# Reject address with empty user
-test-case name "rcptto.no-user" {
- call init-helo-mailfrom
- writeln "RCPT TO: <@a>"
- expect smtp permfail
-}
-
-# Accept address after source-route stripping
-test-case name "rcptto.src-route" {
- call init-helo-mailfrom
- writeln "RCPT TO: <whatever:a@a>"
- expect smtp ok
-}
-
-# Reject address with empty user after source-route stripping
-test-case name "rcptto.src-route-no-user" {
- call init-helo-mailfrom
- writeln "RCPT TO: <a:@a>"
- expect smtp permfail
-}
-
-# Reject address with space as user
-test-case name "rcptto.space-as-user" {
- call init-helo-mailfrom
- writeln "RCPT TO: < @a>"
- expect smtp permfail
-}
-
-# Reject address with space as domain
-test-case name "rcptto.space-as-domain" {
- call init-helo-mailfrom
- writeln "RCPT TO: <a@ >"
- expect smtp permfail
-}
-
-# Reject options
-test-case name "rcptto.option" {
- call init-helo-mailfrom
- writeln "RCPT TO: <user@domain> OPT"
- expect smtp permfail
-}
-
-# Reject options
-test-case name "rcptto.option-size" {
- call init-helo-mailfrom
- writeln "RCPT TO: <user@domain> SIZE=3005"
- expect smtp permfail
-}
-
-# Reject options
-test-case name "rcptto.option-7bit" {
- call init-helo-mailfrom
- writeln "RCPT TO: <user@domain> BODY=7BIT"
- expect smtp permfail
-}
-
-# Reject options
-test-case name "rcptto.option-8bitmime" {
- call init-helo-mailfrom
- writeln "RCPT TO: <user@domain> BODY=8BITMIME"
- expect smtp permfail
-}
-
-# Reject options
-test-case name "rcptto.option-8bitmime-size" {
- call init-helo-mailfrom
- writeln "RCPT TO: <user@domain> BODY=8BITMIME"
- expect smtp permfail
-}
-
-# Reject options
-test-case name "rcptto.option-auth" {
- call init-helo-mailfrom
- writeln "RCPT TO: <user@domain> AUTH=WHATEVER"
- expect smtp permfail
-}
diff --git a/regress/smtp/test.smtp0 b/regress/smtp/test.smtp0
deleted file mode 100755
index c56d002e..00000000
--- a/regress/smtp/test.smtp0
+++ /dev/null
@@ -1,89 +0,0 @@
-#! /usr/bin/smtpscript
-#
-# Simple test cases for the SMTP server.
-#
-
-# Make sure we get an smtp welcome message
-test-case {
- expect smtp
-}
-
-# Make sure that the server accept QUIT and disconnect immediatly
-test-case {
- expect smtp
- writeln "QUIT"
- expect smtp ok
- expect disconnect
-}
-
-# Make sure that the server rejects an empty HELO
-test-case {
- expect smtp
- writeln "HELO"
- expect smtp permfail
-}
-
-# Make sure that the server rejects an empty EHLO
-test-case {
- expect smtp
- writeln "EHLO"
- expect smtp permfail
-}
-
-# Make sure that the server accepts HELO
-test-case {
- expect smtp
- writeln "HELO myself"
- expect smtp ok
-}
-
-# Make sure that the server accepts EHLO
-test-case {
- expect smtp
- writeln "EHLO myself"
- expect smtp helo
-}
-
-# Make sure that the server doesn't accept mail before HELO
-test-case {
- expect smtp
- writeln "MAIL FROM: <me@localhost>"
- expect smtp permfail
-}
-
-# Simple mail transfer test, without pipelining
-test-case {
- expect smtp
- writeln "EHLO fkf"
- expect smtp helo
- writeln "MAIL FROM: <test@blabla>"
- expect smtp ok
- writeln "RCPT TO: <gilles
-@localhost>"
- expect smtp ok
-# writeln "DATA"
-# expect smtp ok
-# writeln "foo"
-# writeln "."
-# expect smtp ok
-# writeln "QUIT"
-# expect smtp
-# expect disconnect
-}
-
-# Simple mail transfer test, with pipelining
-test-case {
- expect smtp
- writeln "EHLO fkf"
- expect smtp helo
- writeln "MAIL FROM: <test@blabla>"
- writeln "RCPT TO: <test@localhost>"
- writeln "DATA"
- repeat 3 expect smtp ok
- writeln "foo"
- writeln "."
- expect smtp ok
- writeln "QUIT"
- expect smtp
- expect disconnect
-}
diff --git a/regress/smtp/test.smtp1 b/regress/smtp/test.smtp1
deleted file mode 100755
index 9d4d8c43..00000000
--- a/regress/smtp/test.smtp1
+++ /dev/null
@@ -1,88 +0,0 @@
-#! /usr/bin/smtpscript
-#
-# Simple test cases for the SMTP server.
-#
-
-# Make sure we get an smtp welcome message
-test-case {
- expect smtp
-}
-
-# Make sure that the server accept QUIT and disconnect immediatly
-test-case {
- expect smtp
- writeln "QUIT"
- expect smtp ok
- expect disconnect
-}
-
-# Make sure that the server rejects an empty HELO
-test-case {
- expect smtp
- writeln "HELO"
- expect smtp permfail
-}
-
-# Make sure that the server rejects an empty EHLO
-test-case {
- expect smtp
- writeln "EHLO"
- expect smtp permfail
-}
-
-# Make sure that the server accepts HELO
-test-case {
- expect smtp
- writeln "HELO myself"
- expect smtp ok
-}
-
-# Make sure that the server accepts EHLO
-test-case {
- expect smtp
- writeln "EHLO myself"
- expect smtp helo
-}
-
-# Make sure that the server doesn't accept mail before HELO
-test-case {
- expect smtp
- writeln "MAIL FROM: <me@localhost>"
- expect smtp permfail
-}
-
-# Simple mail transfer test, without pipelining
-test-case {
- expect smtp
- writeln "EHLO fkf"
- expect smtp helo
- writeln "MAIL FROM: <test@blabla>"
- expect smtp ok
- writeln "RCPT TO: <gilles@localhost>"
- expect smtp ok
- writeln "DATA"
- expect smtp ok
- writeln "foo"
- writeln "."
- expect smtp ok
- writeln "QUIT"
- expect smtp
- expect disconnect
-}
-
-# Simple mail transfer test, with pipelining
-test-case {
- expect smtp
- writeln "EHLO fkf"
- expect smtp helo
- writeln "MAIL FROM: <test@blabla>"
- writeln "RCPT TO: <gilles@localhost>"
- writeln "DATA"
- repeat 3 expect smtp ok
- writeln "foo"
- writeln "."
- expect smtp ok
- writeln "QUIT"
- expect smtp
- expect disconnect
-}
diff --git a/regress/smtp/test.smtp2 b/regress/smtp/test.smtp2
deleted file mode 100755
index 1d5fb841..00000000
--- a/regress/smtp/test.smtp2
+++ /dev/null
@@ -1,50 +0,0 @@
-#! /usr/bin/smtpscript
-#
-# Make sure that the SMTP limits are ok
-#
-
-# Make sure the server disconnects on the 5th useless command
-test-case {
- expect smtp
- repeat 4 {
- writeln "FOO"
- expect smtp permfail
- }
- writeln "FOO"
- expect disconnect
-}
-
-# 1000 RCPT per message
-test-case {
- expect smtp
- writeln "EHLO fkf"
- expect smtp helo
- writeln "MAIL FROM: <test@blabla>"
- expect smtp ok
- repeat 1000 {
- writeln "RCPT TO: <test@localhost>"
- expect smtp ok
- }
- writeln "RCPT TO: <test@localhost>"
- expect smtp tempfail
-}
-
-# 100 messages per session
-test-case {
- expect smtp
- writeln "EHLO fkf"
- expect smtp helo
- repeat 100 {
- writeln "MAIL FROM: <test@blabla>"
- expect smtp ok
- writeln "RCPT TO: <test@foo.bar>"
- expect smtp ok
- writeln "DATA"
- expect smtp ok
- writeln "foo"
- writeln "."
- expect smtp ok
- }
- writeln "MAIL FROM: <test@blabla>"
- expect smtp tempfail
-}
diff --git a/regress/smtp/test.smtp4 b/regress/smtp/test.smtp4
deleted file mode 100755
index a2c5299e..00000000
--- a/regress/smtp/test.smtp4
+++ /dev/null
@@ -1,155 +0,0 @@
-#! /usr/bin/smtpscript
-#
-# Make sure that broken sessions are handled as they should
-#
-
-test-case {
- expect smtp
- writeln "EHLO"
- expect smtp permfail
-}
-
-test-case {
- expect smtp
- writeln "HELO"
- expect smtp permfail
-}
-
-test-case {
- expect smtp
- writeln "HELO "
- expect smtp permfail
-}
-
-test-case {
- expect smtp
- writeln " HELO "
- expect smtp permfail
-}
-
-test-case {
- expect smtp
- writeln "HELO l"
- expect smtp helo
-}
-
-
-test-case {
- expect smtp
- writeln "HELO l"
- expect smtp ok
-}
-
-test-case {
- expect smtp
- writeln "EHLO l"
- expect smtp helo
-}
-
-test-case {
- expect smtp
- writeln "EHLO l"
- expect smtp helo
- writeln "RCPT TO:"
- expect smtp permfail
-}
-
-test-case {
- expect smtp
- writeln "EHLO l"
- expect smtp helo
- writeln "DATA"
- expect smtp permfail
-}
-
-test-case {
- expect smtp
- writeln "EHLO l"
- expect smtp helo
- writeln "MAIL FROM"
- expect smtp permfail
-}
-
-test-case {
- expect smtp
- writeln "EHLO l"
- expect smtp helo
- writeln "MAIL FROM:<opensmtpd@opensmtpd.org>"
- expect smtp ok
- writeln "DATA"
- expect smtp permfail
-}
-
-test-case {
- expect smtp
- writeln "EHLO l"
- expect smtp helo
- writeln "MAIL FROM:<opensmtpd@opensmtpd.org>"
- expect smtp ok
- writeln "RCPT TO:"
- expect smtp permfail
-}
-
-test-case {
- expect smtp
- writeln "EHLO l"
- expect smtp helo
- writeln "MAIL FROM:<opensmtpd@opensmtpd.org>"
- expect smtp ok
- writeln "RCPT TO:<root@localhost>"
- expect smtp ok
- writeln "RSET"
- expect smtp ok
- writeln "DATA"
- expect smtp permfail
-}
-
-test-case {
- expect smtp
- writeln "EHLO l"
- expect smtp helo
- writeln "MAIL FROM:<opensmtpd@opensmtpd.org>"
- expect smtp ok
- writeln "RCPT TO:<root@localhost>"
- expect smtp ok
- writeln "RSET"
- expect smtp ok
- writeln "MAIL FROM:<opensmtpd@opensmtpd.org>"
- expect smtp ok
- writeln "DATA"
- expect smtp permfail
-}
-
-test-case {
- expect smtp ok
- writeln "EHLO l"
- expect smtp helo
- writeln "MAIL FROM:<opensmtpd@opensmtpd.org>"
- expect smtp ok
- writeln "RCPT TO:<root@localhost>"
- expect smtp ok
- writeln "RSET"
- expect smtp ok
- writeln "MAIL FROM:<opensmtpd@opensmtpd.org>"
- expect smtp ok
- writeln "RCPT TO:<root@localhost>"
- expect smtp ok
- writeln "DATA"
- expect smtp ok
-}
-
-test-case {
- expect smtp ok
- writeln "EHLO l"
- expect smtp helo
- writeln "MAIL FROM:<opensmtpd@opensmtpd.org> SIZE=1000"
- expect smtp ok
-}
-
-test-case {
- expect smtp ok
- writeln "HELO l"
- expect smtp ok
- writeln "MAIL FROM:<opensmtpd@opensmtpd.org> SIZE=1000"
- expect smtp ok
-}
diff --git a/regress/smtp/test.tls b/regress/smtp/test.tls
deleted file mode 100644
index f7ebdf19..00000000
--- a/regress/smtp/test.tls
+++ /dev/null
@@ -1,10 +0,0 @@
-test-case name "starttls" {
- expect smtp ok
- writeln "EHLO regress"
- expect smtp helo
- writeln "STARTTLS"
- expect smtp ok
- starttls
- writeln "EHLO regress"
- expect smtp helo
-}
diff --git a/smtpd/lka_proc.c b/smtpd/lka_proc.c
deleted file mode 100644
index a49f007d..00000000
--- a/smtpd/lka_proc.c
+++ /dev/null
@@ -1,190 +0,0 @@
-/* $OpenBSD: lka_proc.c,v 1.12 2019/09/30 13:27:12 gilles Exp $ */
-
-/*
- * Copyright (c) 2018 Gilles Chehade <gilles@poolp.org>
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include "includes.h"
-
-#include <sys/types.h>
-#include <sys/queue.h>
-#include <sys/tree.h>
-#include <sys/socket.h>
-
-#include <netinet/in.h>
-
-#include <errno.h>
-#include <event.h>
-#include <imsg.h>
-#include <inttypes.h>
-#include <limits.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include "smtpd.h"
-#include "log.h"
-
-static int inited = 0;
-static struct dict processors;
-
-struct processor_instance {
- char *name;
- struct io *io;
- struct io *errfd;
- int ready;
-};
-
-static void processor_io(struct io *, int, void *);
-static void processor_errfd(struct io *, int, void *);
-void lka_filter_process_response(const char *, const char *);
-
-int
-lka_proc_ready(void)
-{
- void *iter;
- struct processor_instance *pi;
-
- iter = NULL;
- while (dict_iter(&processors, &iter, NULL, (void **)&pi))
- if (!pi->ready)
- return 0;
- return 1;
-}
-
-static void
-lka_proc_config(struct processor_instance *pi)
-{
- io_printf(pi->io, "config|smtpd-version|%s\n", SMTPD_VERSION);
- io_printf(pi->io, "config|smtp-session-timeout|%d\n", SMTPD_SESSION_TIMEOUT);
- io_printf(pi->io, "config|ready\n");
-}
-
-void
-lka_proc_forked(const char *name, int fd)
-{
- struct processor_instance *processor;
-
- if (!inited) {
- dict_init(&processors);
- inited = 1;
- }
-
- processor = xcalloc(1, sizeof *processor);
- processor->name = xstrdup(name);
- processor->io = io_new();
-
- io_set_nonblocking(fd);
-
- io_set_fd(processor->io, fd);
- io_set_callback(processor->io, processor_io, processor->name);
- dict_xset(&processors, name, processor);
-}
-
-void
-lka_proc_errfd(const char *name, int fd)
-{
- struct processor_instance *processor;
-
- processor = dict_xget(&processors, name);
-
- io_set_nonblocking(fd);
-
- processor->errfd = io_new();
- io_set_fd(processor->errfd, fd);
- io_set_callback(processor->errfd, processor_errfd, processor->name);
-
- lka_proc_config(processor);
-}
-
-struct io *
-lka_proc_get_io(const char *name)
-{
- struct processor_instance *processor;
-
- processor = dict_xget(&processors, name);
-
- return processor->io;
-}
-
-static void
-processor_register(const char *name, const char *line)
-{
- struct processor_instance *processor;
-
- processor = dict_xget(&processors, name);
-
- if (strcmp(line, "register|ready") == 0) {
- processor->ready = 1;
- return;
- }
-
- if (strncmp(line, "register|report|", 16) == 0) {
- lka_report_register_hook(name, line+16);
- return;
- }
-
- if (strncmp(line, "register|filter|", 16) == 0) {
- lka_filter_register_hook(name, line+16);
- return;
- }
-
- fatalx("Invalid register line received: %s", line);
-}
-
-static void
-processor_io(struct io *io, int evt, void *arg)
-{
- struct processor_instance *processor;
- const char *name = arg;
- char *line = NULL;
- ssize_t len;
-
- switch (evt) {
- case IO_DATAIN:
- while ((line = io_getline(io, &len)) != NULL) {
- if (strncmp("register|", line, 9) == 0) {
- processor_register(name, line);
- continue;
- }
-
- processor = dict_xget(&processors, name);
- if (!processor->ready)
- fatalx("Non-register message before register|"
- "ready: %s", line);
- else if (strncmp(line, "filter-result|", 14) == 0 ||
- strncmp(line, "filter-dataline|", 16) == 0)
- lka_filter_process_response(name, line);
- else if (strncmp(line, "report|", 7) == 0)
- lka_report_proc(name, line);
- else
- fatalx("Invalid filter message type: %s", line);
- }
- }
-}
-
-static void
-processor_errfd(struct io *io, int evt, void *arg)
-{
- const char *name = arg;
- char *line = NULL;
- ssize_t len;
-
- switch (evt) {
- case IO_DATAIN:
- while ((line = io_getline(io, &len)) != NULL)
- log_warnx("%s: %s", name, line);
- }
-}
diff --git a/smtpd/lka_report.c b/smtpd/lka_report.c
deleted file mode 100644
index f28a8eeb..00000000
--- a/smtpd/lka_report.c
+++ /dev/null
@@ -1,511 +0,0 @@
-/* $OpenBSD: lka_report.c,v 1.34 2019/10/03 05:42:57 gilles Exp $ */
-
-/*
- * Copyright (c) 2018 Gilles Chehade <gilles@poolp.org>
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include "includes.h"
-
-#include <sys/types.h>
-#include <sys/queue.h>
-#include <sys/tree.h>
-#include <sys/socket.h>
-
-#include <netinet/in.h>
-
-#include <errno.h>
-#include <event.h>
-#include <imsg.h>
-#include <inttypes.h>
-#include <limits.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include "smtpd.h"
-#include "log.h"
-
-#define PROTOCOL_VERSION "0.4"
-
-struct reporter_proc {
- TAILQ_ENTRY(reporter_proc) entries;
- const char *name;
-};
-TAILQ_HEAD(reporters, reporter_proc);
-
-static struct dict smtp_in;
-static struct dict smtp_out;
-
-static struct smtp_events {
- const char *event;
-} smtp_events[] = {
- { "link-connect" },
- { "link-disconnect" },
- { "link-greeting" },
- { "link-identify" },
- { "link-tls" },
- { "link-auth" },
-
- { "tx-reset" },
- { "tx-begin" },
- { "tx-mail" },
- { "tx-rcpt" },
- { "tx-envelope" },
- { "tx-data" },
- { "tx-commit" },
- { "tx-rollback" },
-
- { "protocol-client" },
- { "protocol-server" },
-
- { "filter-report" },
- { "filter-response" },
-
- { "timeout" },
-};
-
-static void
-report_smtp_broadcast(uint64_t, const char *, struct timeval *, const char *,
- const char *, ...) __attribute__((__format__ (printf, 5, 6)));
-
-void
-lka_report_init(void)
-{
- struct reporters *tailq;
- size_t i;
-
- dict_init(&smtp_in);
- dict_init(&smtp_out);
-
- for (i = 0; i < nitems(smtp_events); ++i) {
- tailq = xcalloc(1, sizeof (struct reporters));
- TAILQ_INIT(tailq);
- dict_xset(&smtp_in, smtp_events[i].event, tailq);
-
- tailq = xcalloc(1, sizeof (struct reporters));
- TAILQ_INIT(tailq);
- dict_xset(&smtp_out, smtp_events[i].event, tailq);
- }
-}
-
-void
-lka_report_register_hook(const char *name, const char *hook)
-{
- struct dict *subsystem;
- struct reporter_proc *rp;
- struct reporters *tailq;
- void *iter;
- size_t i;
-
- if (strncmp(hook, "smtp-in|", 8) == 0) {
- subsystem = &smtp_in;
- hook += 8;
- }
-#if 0
- /* No smtp-out event has been implemented yet */
- else if (strncmp(hook, "smtp-out|", 9) == 0) {
- subsystem = &smtp_out;
- hook += 9;
- }
-#endif
- else
- fatalx("Invalid message direction: %s", hook);
-
- if (strcmp(hook, "*") == 0) {
- iter = NULL;
- while (dict_iter(subsystem, &iter, NULL, (void **)&tailq)) {
- rp = xcalloc(1, sizeof *rp);
- rp->name = xstrdup(name);
- TAILQ_INSERT_TAIL(tailq, rp, entries);
- }
- return;
- }
-
- for (i = 0; i < nitems(smtp_events); i++)
- if (strcmp(hook, smtp_events[i].event) == 0)
- break;
- if (i == nitems(smtp_events))
- fatalx("Unrecognized report name: %s", hook);
-
- tailq = dict_get(subsystem, hook);
- rp = xcalloc(1, sizeof *rp);
- rp->name = xstrdup(name);
- TAILQ_INSERT_TAIL(tailq, rp, entries);
-}
-
-static void
-report_smtp_broadcast(uint64_t reqid, const char *direction, struct timeval *tv, const char *event,
- const char *format, ...)
-{
- va_list ap;
- struct dict *d;
- struct reporters *tailq;
- struct reporter_proc *rp;
-
- if (strcmp("smtp-in", direction) == 0)
- d = &smtp_in;
-
- else if (strcmp("smtp-out", direction) == 0)
- d = &smtp_out;
-
- else
- fatalx("unexpected direction: %s", direction);
-
- tailq = dict_xget(d, event);
- TAILQ_FOREACH(rp, tailq, entries) {
- if (!lka_filter_proc_in_session(reqid, rp->name))
- continue;
-
- va_start(ap, format);
- if (io_printf(lka_proc_get_io(rp->name),
- "report|%s|%lld.%06ld|%s|%s|%016"PRIx64"%s",
- PROTOCOL_VERSION, (long long int)tv->tv_sec, tv->tv_usec, direction,
- event, reqid, format[0] != '\n' ? "|" : "") == -1 ||
- io_vprintf(lka_proc_get_io(rp->name), format, ap) == -1)
- fatalx("failed to write to processor");
- va_end(ap);
- }
-}
-
-void
-lka_report_smtp_link_connect(const char *direction, struct timeval *tv, uint64_t reqid, const char *rdns,
- int fcrdns,
- const struct sockaddr_storage *ss_src,
- const struct sockaddr_storage *ss_dest)
-{
- char src[NI_MAXHOST + 5];
- char dest[NI_MAXHOST + 5];
- uint16_t src_port = 0;
- uint16_t dest_port = 0;
- const char *fcrdns_str;
-
- if (ss_src->ss_family == AF_INET)
- src_port = ntohs(((const struct sockaddr_in *)ss_src)->sin_port);
- else if (ss_src->ss_family == AF_INET6)
- src_port = ntohs(((const struct sockaddr_in6 *)ss_src)->sin6_port);
-
- if (ss_dest->ss_family == AF_INET)
- dest_port = ntohs(((const struct sockaddr_in *)ss_dest)->sin_port);
- else if (ss_dest->ss_family == AF_INET6)
- dest_port = ntohs(((const struct sockaddr_in6 *)ss_dest)->sin6_port);
-
- if (strcmp(ss_to_text(ss_src), "local") == 0) {
- (void)snprintf(src, sizeof src, "unix:%s", SMTPD_SOCKET);
- (void)snprintf(dest, sizeof dest, "unix:%s", SMTPD_SOCKET);
- } else {
- (void)snprintf(src, sizeof src, "%s:%d", ss_to_text(ss_src), src_port);
- (void)snprintf(dest, sizeof dest, "%s:%d", ss_to_text(ss_dest), dest_port);
- }
-
- switch (fcrdns) {
- case 1:
- fcrdns_str = "pass";
- break;
- case 0:
- fcrdns_str = "fail";
- break;
- default:
- fcrdns_str = "error";
- break;
- }
-
- report_smtp_broadcast(reqid, direction, tv, "link-connect",
- "%s|%s|%s|%s\n", rdns, fcrdns_str, src, dest);
-}
-
-void
-lka_report_smtp_link_disconnect(const char *direction, struct timeval *tv, uint64_t reqid)
-{
- report_smtp_broadcast(reqid, direction, tv, "link-disconnect", "\n");
-}
-
-void
-lka_report_smtp_link_greeting(const char *direction, uint64_t reqid,
- struct timeval *tv, const char *domain)
-{
- report_smtp_broadcast(reqid, direction, tv, "link-greeting", "%s\n",
- domain);
-}
-
-void
-lka_report_smtp_link_auth(const char *direction, struct timeval *tv, uint64_t reqid,
- const char *username, const char *result)
-{
- report_smtp_broadcast(reqid, direction, tv, "link-auth", "%s|%s\n",
- username, result);
-}
-
-void
-lka_report_smtp_link_identify(const char *direction, struct timeval *tv,
- uint64_t reqid, const char *method, const char *heloname)
-{
- report_smtp_broadcast(reqid, direction, tv, "link-identify", "%s|%s\n",
- method, heloname);
-}
-
-void
-lka_report_smtp_link_tls(const char *direction, struct timeval *tv, uint64_t reqid, const char *ciphers)
-{
- report_smtp_broadcast(reqid, direction, tv, "link-tls", "%s\n",
- ciphers);
-}
-
-void
-lka_report_smtp_tx_reset(const char *direction, struct timeval *tv, uint64_t reqid, uint32_t msgid)
-{
- report_smtp_broadcast(reqid, direction, tv, "tx-reset", "%08x\n",
- msgid);
-}
-
-void
-lka_report_smtp_tx_begin(const char *direction, struct timeval *tv, uint64_t reqid, uint32_t msgid)
-{
- report_smtp_broadcast(reqid, direction, tv, "tx-begin", "%08x\n",
- msgid);
-}
-
-void
-lka_report_smtp_tx_mail(const char *direction, struct timeval *tv, uint64_t reqid, uint32_t msgid, const char *address, int ok)
-{
- const char *result;
-
- switch (ok) {
- case 1:
- result = "ok";
- break;
- case 0:
- result = "permfail";
- break;
- default:
- result = "tempfail";
- break;
- }
- report_smtp_broadcast(reqid, direction, tv, "tx-mail", "%08x|%s|%s\n",
- msgid, address, result);
-}
-
-void
-lka_report_smtp_tx_rcpt(const char *direction, struct timeval *tv, uint64_t reqid, uint32_t msgid, const char *address, int ok)
-{
- const char *result;
-
- switch (ok) {
- case 1:
- result = "ok";
- break;
- case 0:
- result = "permfail";
- break;
- default:
- result = "tempfail";
- break;
- }
- report_smtp_broadcast(reqid, direction, tv, "tx-rcpt", "%08x|%s|%s\n",
- msgid, address, result);
-}
-
-void
-lka_report_smtp_tx_envelope(const char *direction, struct timeval *tv, uint64_t reqid, uint32_t msgid, uint64_t evpid)
-{
- report_smtp_broadcast(reqid, direction, tv, "tx-envelope",
- "%08x|%016"PRIx64"\n", msgid, evpid);
-}
-
-void
-lka_report_smtp_tx_data(const char *direction, struct timeval *tv, uint64_t reqid, uint32_t msgid, int ok)
-{
- const char *result;
-
- switch (ok) {
- case 1:
- result = "ok";
- break;
- case 0:
- result = "permfail";
- break;
- default:
- result = "tempfail";
- break;
- }
- report_smtp_broadcast(reqid, direction, tv, "tx-data", "%08x|%s\n",
- msgid, result);
-}
-
-void
-lka_report_smtp_tx_commit(const char *direction, struct timeval *tv, uint64_t reqid, uint32_t msgid, size_t msgsz)
-{
- report_smtp_broadcast(reqid, direction, tv, "tx-commit", "%08x|%zd\n",
- msgid, msgsz);
-}
-
-void
-lka_report_smtp_tx_rollback(const char *direction, struct timeval *tv, uint64_t reqid, uint32_t msgid)
-{
- report_smtp_broadcast(reqid, direction, tv, "tx-rollback", "%08x\n",
- msgid);
-}
-
-void
-lka_report_smtp_protocol_client(const char *direction, struct timeval *tv, uint64_t reqid, const char *command)
-{
- report_smtp_broadcast(reqid, direction, tv, "protocol-client", "%s\n",
- command);
-}
-
-void
-lka_report_smtp_protocol_server(const char *direction, struct timeval *tv, uint64_t reqid, const char *response)
-{
- report_smtp_broadcast(reqid, direction, tv, "protocol-server", "%s\n",
- response);
-}
-
-void
-lka_report_smtp_filter_response(const char *direction, struct timeval *tv, uint64_t reqid,
- int phase, int response, const char *param)
-{
- const char *phase_name;
- const char *response_name;
-
- switch (phase) {
- case FILTER_CONNECT:
- phase_name = "connected";
- break;
- case FILTER_HELO:
- phase_name = "helo";
- break;
- case FILTER_EHLO:
- phase_name = "ehlo";
- break;
- case FILTER_STARTTLS:
- phase_name = "tls";
- break;
- case FILTER_AUTH:
- phase_name = "auth";
- break;
- case FILTER_MAIL_FROM:
- phase_name = "mail-from";
- break;
- case FILTER_RCPT_TO:
- phase_name = "rcpt-to";
- break;
- case FILTER_DATA:
- phase_name = "data";
- break;
- case FILTER_DATA_LINE:
- phase_name = "data-line";
- break;
- case FILTER_RSET:
- phase_name = "rset";
- break;
- case FILTER_QUIT:
- phase_name = "quit";
- break;
- case FILTER_NOOP:
- phase_name = "noop";
- break;
- case FILTER_HELP:
- phase_name = "help";
- break;
- case FILTER_WIZ:
- phase_name = "wiz";
- break;
- case FILTER_COMMIT:
- phase_name = "commit";
- break;
- default:
- phase_name = "";
- }
-
- switch (response) {
- case FILTER_PROCEED:
- response_name = "proceed";
- break;
- case FILTER_JUNK:
- response_name = "junk";
- break;
- case FILTER_REWRITE:
- response_name = "rewrite";
- break;
- case FILTER_REJECT:
- response_name = "reject";
- break;
- case FILTER_DISCONNECT:
- response_name = "disconnect";
- break;
- default:
- response_name = "";
- }
-
- report_smtp_broadcast(reqid, direction, tv, "filter-response",
- "%s|%s%s%s\n", phase_name, response_name, param ? "|" : "",
- param ? param : "");
-}
-
-void
-lka_report_smtp_timeout(const char *direction, struct timeval *tv, uint64_t reqid)
-{
- report_smtp_broadcast(reqid, direction, tv, "timeout", "\n");
-}
-
-void
-lka_report_filter_report(uint64_t reqid, const char *name, int builtin,
- const char *direction, struct timeval *tv, const char *message)
-{
- report_smtp_broadcast(reqid, direction, tv, "filter-report",
- "%s|%s|%s\n", builtin ? "builtin" : "proc",
- name, message);
-}
-
-void
-lka_report_proc(const char *name, const char *line)
-{
- char buffer[LINE_MAX];
- struct timeval tv;
- char *ep, *sp, *direction;
- uint64_t reqid;
-
- if (strlcpy(buffer, line + 7, sizeof(buffer)) >= sizeof(buffer))
- fatalx("Invalid report: line too long: %s", line);
-
- errno = 0;
- tv.tv_sec = strtoll(buffer, &ep, 10);
- if (ep[0] != '.' || errno != 0)
- fatalx("Invalid report: invalid time: %s", line);
- sp = ep + 1;
- tv.tv_usec = strtol(sp, &ep, 10);
- if (ep[0] != '|' || errno != 0)
- fatalx("Invalid report: invalid time: %s", line);
- if (ep - sp != 6)
- fatalx("Invalid report: invalid time: %s", line);
-
- direction = ep + 1;
- if (strncmp(direction, "smtp-in|", 8) == 0) {
- direction[7] = '\0';
- direction += 7;
-#if 0
- } else if (strncmp(direction, "smtp-out|", 9) == 0) {
- direction[8] = '\0';
- direction += 8;
-#endif
- } else
- fatalx("Invalid report: invalid direction: %s", line);
-
- reqid = strtoull(sp, &ep, 16);
- if (ep[0] != '|' || errno != 0)
- fatalx("Invalid report: invalid reqid: %s", line);
- sp = ep + 1;
-
- lka_report_filter_report(reqid, name, 0, direction, &tv, sp);
-}
diff --git a/smtpd/smtpctl/CVS/Entries b/smtpd/smtpctl/CVS/Entries
deleted file mode 100644
index 4c1dedd7..00000000
--- a/smtpd/smtpctl/CVS/Entries
+++ /dev/null
@@ -1,2 +0,0 @@
-/Makefile/1.46/Wed Apr 25 07:51:42 2018//
-D
diff --git a/smtpd/smtpctl/CVS/Repository b/smtpd/smtpctl/CVS/Repository
deleted file mode 100644
index edc50474..00000000
--- a/smtpd/smtpctl/CVS/Repository
+++ /dev/null
@@ -1 +0,0 @@
-src/usr.sbin/smtpd/smtpctl
diff --git a/smtpd/smtpctl/CVS/Root b/smtpd/smtpctl/CVS/Root
deleted file mode 100644
index 8a89d1a7..00000000
--- a/smtpd/smtpctl/CVS/Root
+++ /dev/null
@@ -1 +0,0 @@
-gilles@cvs.openbsd.org:/cvs
diff --git a/smtpd/smtpd/CVS/Repository b/smtpd/smtpd/CVS/Repository
deleted file mode 100644
index 11be251c..00000000
--- a/smtpd/smtpd/CVS/Repository
+++ /dev/null
@@ -1 +0,0 @@
-src/usr.sbin/smtpd/smtpd
diff --git a/smtpd/smtpd/CVS/Root b/smtpd/smtpd/CVS/Root
deleted file mode 100644
index 8a89d1a7..00000000
--- a/smtpd/smtpd/CVS/Root
+++ /dev/null
@@ -1 +0,0 @@
-gilles@cvs.openbsd.org:/cvs
diff --git a/tests/certificate_test/smtpd.conf b/tests/certificate_test/smtpd.conf
deleted file mode 100644
index 34887103..00000000
--- a/tests/certificate_test/smtpd.conf
+++ /dev/null
@@ -1,13 +0,0 @@
-pki_domain = "localhost"
-pki $pki_domain cert "/etc/ssl/private/sites/fullchain.cer"
-pki $pki_domain key "/etc/ssl/private/sites/site.key"
-
-#Encrypted password is "password"
-table passwords {"user"="$6$tf940h4BpywpeKID$pWYiqoWywVPybeHaEcqHSRBD/7UxBmYhx7iHvxj/B3LBxCWwnFx7.3JwMISsN9EpPMwEZELvbNehVLl0IvvZo/"}
-
-listen on localhost tls hostname $pki_domain pki $pki_domain auth-optional <passwords>
-listen on localhost port 465 smtps hostname $pki_domain pki $pki_domain auth-optional <passwords>
-listen on localhost port 587 tls-require hostname $pki_domain pki $pki_domain auth <passwords>
-
-action "local" maildir "/tmp/"
-match auth from any for any action "local"
diff --git a/tests/certificate_test/test.sh b/tests/certificate_test/test.sh
deleted file mode 100755
index 1eb50b40..00000000
--- a/tests/certificate_test/test.sh
+++ /dev/null
@@ -1,20 +0,0 @@
-#!/bin/sh
-set -euxo pipefail
-BASEDIR=$(dirname $0)
-
-# Setup TLS
-mkdir -p /etc/ssl/private/sites/
-openssl genrsa -out /etc/ssl/private/sites/site.key 4096
-openssl req -new -x509 -key /etc/ssl/private/sites/site.key -out /etc/ssl/private/sites/fullchain.cer -subj "/CN='localhost'"
-chmod 600 /etc/ssl/private/sites/site.key
-chmod 644 /etc/ssl/private/sites/fullchain.cer
-
-smtpd -dv -f "$BASEDIR/smtpd.conf" &
-
-#Wait for smtpd to be ready to receive connections
-sleep 3
-
-#OpenSSL is crazy and will treat a capital "R" or "Q" as a command without the -quiet flag
-#OpenSMTPD doesn't support pipelining, so wait 0.1 seconds between lines
-awk '{print $0; system("sleep .1");}' "$BASEDIR/../test_email.txt" | \
- openssl s_client -quiet -connect localhost:25 -starttls smtp
diff --git a/tests/test_all.sh b/tests/test_all.sh
deleted file mode 100755
index ca619479..00000000
--- a/tests/test_all.sh
+++ /dev/null
@@ -1,6 +0,0 @@
-#!/bin/sh
-set -euxo pipefail
-BASEDIR=$(dirname $0)
-
-echo "Testing TLS"
-"$BASEDIR/certificate_test/test.sh"
diff --git a/tests/test_email.txt b/tests/test_email.txt
deleted file mode 100644
index 4fd3acfd..00000000
--- a/tests/test_email.txt
+++ /dev/null
@@ -1,13 +0,0 @@
-HELO localhost
-AUTH LOGIN
-dXNlcg==
-cGFzc3dvcmQ=
-MAIL FROM:<_smtpd@localhost>
-RCPT TO:<_smtpd@localhost>
-DATA
-Subject: Test Email
-
-It works
-
-.
-QUIT
diff --git a/smtpd/Makefile b/usr.sbin/smtpd/Makefile
index a3dbc9d1..a3dbc9d1 100644
--- a/smtpd/Makefile
+++ b/usr.sbin/smtpd/Makefile
diff --git a/smtpd/aliases.5 b/usr.sbin/smtpd/aliases.5
index 802ca9a6..7c250c81 100644
--- a/smtpd/aliases.5
+++ b/usr.sbin/smtpd/aliases.5
@@ -1,4 +1,4 @@
-.\" $OpenBSD: aliases.5,v 1.15 2018/05/24 11:38:24 gilles Exp $
+.\" $OpenBSD: aliases.5,v 1.16 2020/04/23 21:28:10 jmc Exp $
.\"
.\" Copyright (c) 2012 Gilles Chehade <gilles@poolp.org>
.\"
@@ -14,7 +14,7 @@
.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
.\"
-.Dd $Mdocdate: May 24 2018 $
+.Dd $Mdocdate: April 23 2020 $
.Dt ALIASES 5
.Os
.Sh NAME
@@ -77,7 +77,7 @@ will only use the part that precedes
.Sq +
as a
.Em key .
-.It Ar error : Ns Ar code Ar message
+.It Ar error : Ns Ar code message
A status code and message to return.
The code must be 3 digits,
starting 4XX (TempFail) or 5XX (PermFail).
diff --git a/smtpd/aliases.c b/usr.sbin/smtpd/aliases.c
index cd47d684..0f8a5c1e 100644
--- a/smtpd/aliases.c
+++ b/usr.sbin/smtpd/aliases.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: aliases.c,v 1.77 2018/12/28 12:47:28 eric Exp $ */
+/* $OpenBSD: aliases.c,v 1.78 2020/04/28 21:46:43 eric Exp $ */
/*
* Copyright (c) 2008 Gilles Chehade <gilles@poolp.org>
@@ -171,6 +171,10 @@ aliases_virtual_get(struct expand *expand, const struct mailaddr *maddr)
if (ret)
goto expand;
+ /* Do not try catch-all entries if there is no domain */
+ if (domain[0] == '\0')
+ return 0;
+
if (!bsnprintf(buf, sizeof(buf), "@%s", domain))
return 0;
/* Failed ? We lookup for catch all for virtual domain */
diff --git a/smtpd/bounce.c b/usr.sbin/smtpd/bounce.c
index 41526d8d..4a4a0992 100644
--- a/smtpd/bounce.c
+++ b/usr.sbin/smtpd/bounce.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: bounce.c,v 1.80 2018/12/08 08:01:15 sunil Exp $ */
+/* $OpenBSD: bounce.c,v 1.82 2020/04/24 11:34:07 eric Exp $ */
/*
* Copyright (c) 2009 Gilles Chehade <gilles@poolp.org>
@@ -186,7 +186,7 @@ bounce_add(uint64_t evpid)
line = evp.errorline;
if (strlen(line) > 4 && (*line == '1' || *line == '6'))
line += 4;
- (void)snprintf(buf, sizeof(buf), "%s@%s: %s\n", evp.dest.user,
+ (void)snprintf(buf, sizeof(buf), "%s@%s: %s", evp.dest.user,
evp.dest.domain, line);
be = xmalloc(sizeof *be);
@@ -198,8 +198,7 @@ bounce_add(uint64_t evpid)
be->esc_class = evp.esc_class;
be->esc_code = evp.esc_code;
TAILQ_INSERT_TAIL(&msg->envelopes, be, entry);
- buf[strcspn(buf, "\n")] = '\0';
- log_debug("debug: bounce: adding report %16"PRIx64": %s", be->id, buf);
+ log_debug("debug: bounce: adding report %16"PRIx64": %s", be->id, be->report);
msg->timeout = time(NULL) + 1;
TAILQ_INSERT_TAIL(&pending, msg, entry);
@@ -313,7 +312,7 @@ bounce_send(struct bounce_session *s, const char *fmt, ...)
log_trace(TRACE_BOUNCE, "bounce: %p: >>> %s", s, p);
- io_xprintf(s->io, "%s\n", p);
+ io_xprintf(s->io, "%s\r\n", p);
free(p);
}
@@ -345,27 +344,27 @@ bounce_duration(long long int d)
}
#define NOTICE_INTRO \
- " Hi!\n\n" \
- " This is the MAILER-DAEMON, please DO NOT REPLY to this email.\n"
+ " Hi!\r\n\r\n" \
+ " This is the MAILER-DAEMON, please DO NOT REPLY to this email.\r\n"
const char *notice_error =
- " An error has occurred while attempting to deliver a message for\n"
- " the following list of recipients:\n\n";
+ " An error has occurred while attempting to deliver a message for\r\n"
+ " the following list of recipients:\r\n\r\n";
const char *notice_warning =
- " A message is delayed for more than %s for the following\n"
- " list of recipients:\n\n";
+ " A message is delayed for more than %s for the following\r\n"
+ " list of recipients:\r\n\r\n";
const char *notice_warning2 =
- " Please note that this is only a temporary failure report.\n"
- " The message is kept in the queue for up to %s.\n"
- " You DO NOT NEED to re-send the message to these recipients.\n\n";
+ " Please note that this is only a temporary failure report.\r\n"
+ " The message is kept in the queue for up to %s.\r\n"
+ " You DO NOT NEED to re-send the message to these recipients.\r\n\r\n";
const char *notice_success =
- " Your message was successfully delivered to these recipients.\n\n";
+ " Your message was successfully delivered to these recipients.\r\n\r\n";
const char *notice_relay =
- " Your message was relayed to these recipients.\n\n";
+ " Your message was relayed to these recipients.\r\n\r\n";
static int
bounce_next_message(struct bounce_session *s)
@@ -453,16 +452,16 @@ bounce_next(struct bounce_session *s)
/* Construct an appropriate notice. */
io_xprintf(s->io,
- "Subject: Delivery status notification: %s\n"
- "From: Mailer Daemon <MAILER-DAEMON@%s>\n"
- "To: %s\n"
- "Date: %s\n"
- "MIME-Version: 1.0\n"
+ "Subject: Delivery status notification: %s\r\n"
+ "From: Mailer Daemon <MAILER-DAEMON@%s>\r\n"
+ "To: %s\r\n"
+ "Date: %s\r\n"
+ "MIME-Version: 1.0\r\n"
"Content-Type: multipart/mixed;"
- "boundary=\"%16" PRIu64 "/%s\"\n"
- "\n"
- "This is a MIME-encapsulated message.\n"
- "\n",
+ "boundary=\"%16" PRIu64 "/%s\"\r\n"
+ "\r\n"
+ "This is a MIME-encapsulated message.\r\n"
+ "\r\n",
action_str(&s->msg->bounce),
s->smtpname,
s->msg->to,
@@ -471,12 +470,12 @@ bounce_next(struct bounce_session *s)
s->smtpname);
io_xprintf(s->io,
- "--%16" PRIu64 "/%s\n"
- "Content-Description: Notification\n"
- "Content-Type: text/plain; charset=us-ascii\n"
- "\n"
+ "--%16" PRIu64 "/%s\r\n"
+ "Content-Description: Notification\r\n"
+ "Content-Type: text/plain; charset=us-ascii\r\n"
+ "\r\n"
NOTICE_INTRO
- "\n",
+ "\r\n",
s->boundary, s->smtpname);
switch (s->msg->bounce.type) {
@@ -497,35 +496,36 @@ bounce_next(struct bounce_session *s)
TAILQ_FOREACH(evp, &s->msg->envelopes, entry) {
io_xprint(s->io, evp->report);
+ io_xprint(s->io, "\r\n");
}
- io_xprint(s->io, "\n");
+ io_xprint(s->io, "\r\n");
if (s->msg->bounce.type == B_DELAYED)
io_xprintf(s->io, notice_warning2,
bounce_duration(s->msg->bounce.ttl));
io_xprintf(s->io,
- " Below is a copy of the original message:\n"
- "\n");
+ " Below is a copy of the original message:\r\n"
+ "\r\n");
io_xprintf(s->io,
- "--%16" PRIu64 "/%s\n"
- "Content-Description: Delivery Report\n"
- "Content-Type: message/delivery-status\n"
- "\n",
+ "--%16" PRIu64 "/%s\r\n"
+ "Content-Description: Delivery Report\r\n"
+ "Content-Type: message/delivery-status\r\n"
+ "\r\n",
s->boundary, s->smtpname);
io_xprintf(s->io,
- "Reporting-MTA: dns; %s\n"
- "\n",
+ "Reporting-MTA: dns; %s\r\n"
+ "\r\n",
s->smtpname);
TAILQ_FOREACH(evp, &s->msg->envelopes, entry) {
io_xprintf(s->io,
- "Final-Recipient: rfc822; %s@%s\n"
- "Action: %s\n"
- "Status: %s\n"
- "\n",
+ "Final-Recipient: rfc822; %s@%s\r\n"
+ "Action: %s\r\n"
+ "Status: %s\r\n"
+ "\r\n",
evp->dest.user,
evp->dest.domain,
action_str(&s->msg->bounce),
@@ -540,10 +540,10 @@ bounce_next(struct bounce_session *s)
case BOUNCE_DATA_MESSAGE:
io_xprintf(s->io,
- "--%16" PRIu64 "/%s\n"
- "Content-Description: Message headers\n"
- "Content-Type: text/rfc822-headers\n"
- "\n",
+ "--%16" PRIu64 "/%s\r\n"
+ "Content-Description: Message headers\r\n"
+ "Content-Type: text/rfc822-headers\r\n"
+ "\r\n",
s->boundary, s->smtpname);
n = io_queued(s->io);
@@ -557,14 +557,14 @@ bounce_next(struct bounce_session *s)
fclose(s->msgfp);
s->msgfp = NULL;
io_xprintf(s->io,
- "\n--%16" PRIu64 "/%s--\n", s->boundary,
+ "\r\n--%16" PRIu64 "/%s--\r\n", s->boundary,
s->smtpname);
bounce_send(s, ".");
s->state = BOUNCE_DATA_END;
return (0);
}
line[len - 1] = '\0';
- io_xprintf(s->io, "%s%s\n",
+ io_xprintf(s->io, "%s%s\r\n",
(len == 2 && line[0] == '.') ? "." : "", line);
}
free(line);
@@ -579,7 +579,7 @@ bounce_next(struct bounce_session *s)
}
io_xprintf(s->io,
- "\n--%16" PRIu64 "/%s--\n", s->boundary, s->smtpname);
+ "\r\n--%16" PRIu64 "/%s--\r\n", s->boundary, s->smtpname);
log_trace(TRACE_BOUNCE, "bounce: %p: >>> [... %zu bytes ...]",
s, io_queued(s->io) - n);
@@ -730,6 +730,10 @@ bounce_io(struct io *io, int evt, void *arg)
if (line == NULL)
break;
+ /* Strip trailing '\r' */
+ if (len && line[len - 1] == '\r')
+ line[--len] = '\0';
+
log_trace(TRACE_BOUNCE, "bounce: %p: <<< %s", s, line);
if ((error = parse_smtp_response(line, len, &msg, &cont))) {
diff --git a/smtpd/ca.c b/usr.sbin/smtpd/ca.c
index a27db87a..b36033b4 100644
--- a/smtpd/ca.c
+++ b/usr.sbin/smtpd/ca.c
@@ -65,13 +65,11 @@ static int rsae_init(RSA *);
static int rsae_finish(RSA *);
static int rsae_keygen(RSA *, int, BIGNUM *, BN_GENCB *);
-#if defined(SUPPORT_ECDSA)
static ECDSA_SIG *ecdsae_do_sign(const unsigned char *, int, const BIGNUM *,
const BIGNUM *, EC_KEY *);
static int ecdsae_sign_setup(EC_KEY *, BN_CTX *, BIGNUM **, BIGNUM **);
static int ecdsae_do_verify(const unsigned char *, int, const ECDSA_SIG *,
EC_KEY *);
-#endif
static uint64_t reqid = 0;
@@ -230,17 +228,13 @@ void
ca_imsg(struct mproc *p, struct imsg *imsg)
{
RSA *rsa = NULL;
-#if defined(SUPPORT_ECDSA)
EC_KEY *ecdsa = NULL;
-#endif
const void *from = NULL;
unsigned char *to = NULL;
struct msg m;
const char *pkiname;
size_t flen, tlen, padding;
-#if defined(SUPPORT_ECDSA)
int buf_len;
-#endif
struct pki *pki;
int ret = 0;
uint64_t id;
@@ -313,7 +307,6 @@ ca_imsg(struct mproc *p, struct imsg *imsg)
RSA_free(rsa);
return;
-#if defined(SUPPORT_ECDSA)
case IMSG_CA_ECDSA_SIGN:
m_msg(&m, imsg);
m_get_id(&m, &id);
@@ -329,6 +322,7 @@ ca_imsg(struct mproc *p, struct imsg *imsg)
buf_len = ECDSA_size(ecdsa);
if ((to = calloc(1, buf_len)) == NULL)
fatalx("ca_imsg: calloc");
+
ret = ECDSA_sign(0, from, flen, to, &buf_len, ecdsa);
m_create(p, imsg->hdr.type, 0, 0, -1);
m_add_id(p, id);
@@ -339,7 +333,6 @@ ca_imsg(struct mproc *p, struct imsg *imsg)
free(to);
EC_KEY_free(ecdsa);
return;
-#endif
}
errx(1, "ca_imsg: unexpected %s imsg", imsg_to_str(imsg->hdr.type));
}
@@ -510,11 +503,11 @@ rsae_keygen(RSA *rsa, int bits, BIGNUM *e, BN_GENCB *cb)
}
-#if defined(SUPPORT_ECDSA)
/*
* ECDSA privsep engine (called from unprivileged processes)
*/
+#if defined(SUPPORT_ECDSA)
const ECDSA_METHOD *ecdsa_default = NULL;
static ECDSA_METHOD *ecdsae_method = NULL;
@@ -538,6 +531,11 @@ ECDSA_METHOD_new_temporary(const char *name, int flags)
ecdsa->flags = flags;
return ecdsa;
}
+#else
+const EC_KEY_METHOD *ecdsa_default = NULL;
+
+static EC_KEY_METHOD *ecdsae_method = NULL;
+#endif
static ECDSA_SIG *
ecdsae_send_enc_imsg(const unsigned char *dgst, int dgst_len,
@@ -554,8 +552,13 @@ ecdsae_send_enc_imsg(const unsigned char *dgst, int dgst_len,
uint64_t id;
ECDSA_SIG *sig = NULL;
+#if defined(SUPPORT_ECDSA)
if ((pkiname = ECDSA_get_ex_data(eckey, 0)) == NULL)
return (0);
+#else
+ if ((pkiname = EC_KEY_get_ex_data(eckey, 0)) == NULL)
+ return (0);
+#endif
/*
* Send a synchronous imsg because we cannot defer the ECDSA
@@ -613,6 +616,7 @@ ecdsae_send_enc_imsg(const unsigned char *dgst, int dgst_len,
return (sig);
}
+#if defined(SUPPORT_ECDSA)
ECDSA_SIG *
ecdsae_do_sign(const unsigned char *dgst, int dgst_len,
const BIGNUM *inv, const BIGNUM *rp, EC_KEY *eckey)
@@ -621,6 +625,7 @@ ecdsae_do_sign(const unsigned char *dgst, int dgst_len,
if (ECDSA_get_ex_data(eckey, 0) != NULL)
return (ecdsae_send_enc_imsg(dgst, dgst_len, inv, rp, eckey));
return (ecdsa_default->ecdsa_do_sign(dgst, dgst_len, inv, rp, eckey));
+
}
int
@@ -638,6 +643,114 @@ ecdsae_do_verify(const unsigned char *dgst, int dgst_len,
log_debug("debug: %s: %s", proc_name(smtpd_process), __func__);
return (ecdsa_default->ecdsa_do_verify(dgst, dgst_len, sig, eckey));
}
+#else
+int
+ecdsae_keygen(EC_KEY *eckey)
+{
+ int (*keygen)(EC_KEY *key);
+ EC_KEY_METHOD_get_keygen(ecdsa_default,
+ &keygen);
+ return keygen(eckey);
+}
+
+int
+ecdsae_compute_key(unsigned char **psec, size_t *pseclen,
+ const EC_POINT *pub_key, const EC_KEY *ecdh)
+{
+ int (*ckey)(unsigned char **psec,
+ size_t *pseclen,
+ const EC_POINT *pub_key,
+ const EC_KEY *ecdh);
+ EC_KEY_METHOD_get_compute_key(ecdsa_default,
+ &ckey);
+ return ckey(psec, pseclen, pub_key, ecdh);
+}
+
+int
+ecdsae_sign(int type, const unsigned char *dgst, int dlen, unsigned char *sig,
+ unsigned int *siglen, const BIGNUM *kinv, const BIGNUM *r, EC_KEY *eckey)
+{
+ int (*sign)(int type, const unsigned char *dgst,
+ int dlen, unsigned char *sig,
+ unsigned int *siglen,
+ const BIGNUM *kinv, const BIGNUM *r,
+ EC_KEY *eckey);
+
+ log_debug("debug: %s: %s", proc_name(smtpd_process), __func__);
+ EC_KEY_METHOD_get_sign(ecdsa_default,
+ &sign,
+ NULL,
+ NULL);
+ return (sign(type, dgst, dlen, sig, siglen, kinv, r, eckey));
+}
+
+
+ECDSA_SIG *
+ecdsae_do_sign(const unsigned char *dgst, int dgst_len,
+ const BIGNUM *inv, const BIGNUM *rp, EC_KEY *eckey)
+{
+ ECDSA_SIG *(*psign_sig)(const unsigned char *dgst,
+ int dgst_len,
+ const BIGNUM *in_kinv,
+ const BIGNUM *in_r,
+ EC_KEY *eckey);
+
+ log_debug("debug: %s: %s", proc_name(smtpd_process), __func__);
+ if (EC_KEY_get_ex_data(eckey, 0) != NULL)
+ return (ecdsae_send_enc_imsg(dgst, dgst_len, inv, rp, eckey));
+ EC_KEY_METHOD_get_sign(ecdsa_default,
+ NULL,
+ NULL,
+ &psign_sig);
+ return (psign_sig(dgst, dgst_len, inv, rp, eckey));
+}
+
+int
+ecdsae_sign_setup(EC_KEY *eckey, BN_CTX *ctx, BIGNUM **kinv,
+ BIGNUM **r)
+{
+ int (*psign_setup)(EC_KEY *eckey, BN_CTX *ctx_in,
+ BIGNUM **kinvp, BIGNUM **rp);
+
+ log_debug("debug: %s: %s", proc_name(smtpd_process), __func__);
+ EC_KEY_METHOD_get_sign(ecdsa_default,
+ NULL,
+ &psign_setup,
+ NULL);
+ return (psign_setup(eckey, ctx, kinv, r));
+}
+
+int
+ecdsae_verify(int type, const unsigned char *dgst, int dgst_len,
+ const unsigned char *sigbuf, int sig_len, EC_KEY *eckey)
+{
+ int (*verify)(int type, const unsigned
+ char *dgst, int dgst_len,
+ const unsigned char *sigbuf,
+ int sig_len, EC_KEY *eckey);
+
+ log_debug("debug: %s: %s", proc_name(smtpd_process), __func__);
+ EC_KEY_METHOD_get_verify(ecdsa_default,
+ &verify,
+ NULL);
+ return (verify(type, dgst, dgst_len, sigbuf, sig_len, eckey));
+}
+
+int
+ecdsae_do_verify(const unsigned char *dgst, int dgst_len,
+ const ECDSA_SIG *sig, EC_KEY *eckey)
+{
+ int (*pverify_sig)(const unsigned char *dgst,
+ int dgst_len,
+ const ECDSA_SIG *sig,
+ EC_KEY *eckey);
+
+ log_debug("debug: %s: %s", proc_name(smtpd_process), __func__);
+ EC_KEY_METHOD_get_verify(ecdsa_default,
+ NULL,
+ &pverify_sig);
+ return (pverify_sig(dgst, dgst_len, sig, eckey));
+}
#endif
static void
@@ -765,13 +878,66 @@ ecdsa_engine_init(void)
ssl_error(errstr);
fatalx("%s", errstr);
}
+#else
+static void
+ecdsa_engine_init(void)
+{
+ ENGINE *e;
+ const char *errstr, *name;
+
+ if ((ecdsae_method = EC_KEY_METHOD_new(NULL)) == NULL) {
+ errstr = "EC_KEY_new";
+ goto fail;
+ }
+
+ EC_KEY_METHOD_set_keygen(ecdsae_method, ecdsae_keygen);
+ EC_KEY_METHOD_set_compute_key(ecdsae_method, ecdsae_compute_key);
+ EC_KEY_METHOD_set_sign(ecdsae_method, ecdsae_sign, ecdsae_sign_setup,
+ ecdsae_do_sign);
+ EC_KEY_METHOD_set_verify(ecdsae_method, ecdsae_verify, ecdsae_do_verify);
+
+ if ((e = ENGINE_get_default_EC()) == NULL) {
+ if ((e = ENGINE_new()) == NULL) {
+ errstr = "ENGINE_new";
+ goto fail;
+ }
+ if (!ENGINE_set_name(e, "ECDSA privsep engine")) {
+ errstr = "ENGINE_set_name";
+ goto fail;
+ }
+ if ((ecdsa_default = EC_KEY_get_default_method()) == NULL) {
+ errstr = "EC_KEY_get_default_method";
+ goto fail;
+ }
+ } else if ((ecdsa_default = ENGINE_get_EC(e)) == NULL) {
+ errstr = "ENGINE_get_EC";
+ goto fail;
+ }
+
+ if ((name = ENGINE_get_name(e)) == NULL)
+ name = "unknown ECDSA engine";
+
+ log_debug("debug: %s: using %s", __func__, name);
+
+ if (!ENGINE_set_EC(e, ecdsae_method)) {
+ errstr = "ENGINE_set_EC";
+ goto fail;
+ }
+ if (!ENGINE_set_default_EC(e)) {
+ errstr = "ENGINE_set_default_EC";
+ goto fail;
+ }
+ return;
+
+ fail:
+ ssl_error(errstr);
+ fatalx("%s", errstr);
+}
#endif
void
ca_engine_init(void)
{
rsa_engine_init();
-#if defined(SUPPORT_ECDSA)
ecdsa_engine_init();
-#endif
}
diff --git a/smtpd/cert.c b/usr.sbin/smtpd/cert.c
index 79b1df91..79b1df91 100644
--- a/smtpd/cert.c
+++ b/usr.sbin/smtpd/cert.c
diff --git a/smtpd/compress_backend.c b/usr.sbin/smtpd/compress_backend.c
index 1b974662..1b974662 100644
--- a/smtpd/compress_backend.c
+++ b/usr.sbin/smtpd/compress_backend.c
diff --git a/smtpd/compress_gzip.c b/usr.sbin/smtpd/compress_gzip.c
index dd60aeec..dd60aeec 100644
--- a/smtpd/compress_gzip.c
+++ b/usr.sbin/smtpd/compress_gzip.c
diff --git a/smtpd/config.c b/usr.sbin/smtpd/config.c
index 20743527..8fe983d6 100644
--- a/smtpd/config.c
+++ b/usr.sbin/smtpd/config.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: config.c,v 1.50 2019/09/20 17:46:05 gilles Exp $ */
+/* $OpenBSD: config.c,v 1.51 2019/12/18 10:00:39 gilles Exp $ */
/*
* Copyright (c) 2008 Pierre-Yves Ritschard <pyr@openbsd.org>
@@ -91,7 +91,7 @@ config_default(void)
conf->sc_ssl_dict = calloc(1, sizeof(*conf->sc_ssl_dict));
conf->sc_limits_dict = calloc(1, sizeof(*conf->sc_limits_dict));
conf->sc_mda_wrappers = calloc(1, sizeof(*conf->sc_mda_wrappers));
- conf->sc_processors_dict = calloc(1, sizeof(*conf->sc_processors_dict));
+ conf->sc_filter_processes_dict = calloc(1, sizeof(*conf->sc_filter_processes_dict));
conf->sc_dispatcher_bounce = calloc(1, sizeof(*conf->sc_dispatcher_bounce));
conf->sc_filters_dict = calloc(1, sizeof(*conf->sc_filters_dict));
limits = calloc(1, sizeof(*limits));
@@ -105,7 +105,7 @@ config_default(void)
conf->sc_ssl_dict == NULL ||
conf->sc_limits_dict == NULL ||
conf->sc_mda_wrappers == NULL ||
- conf->sc_processors_dict == NULL ||
+ conf->sc_filter_processes_dict == NULL ||
conf->sc_dispatcher_bounce == NULL ||
conf->sc_filters_dict == NULL ||
limits == NULL)
@@ -118,7 +118,7 @@ config_default(void)
dict_init(conf->sc_ssl_dict);
dict_init(conf->sc_tables_dict);
dict_init(conf->sc_limits_dict);
- dict_init(conf->sc_processors_dict);
+ dict_init(conf->sc_filter_processes_dict);
limit_mta_set_defaults(limits);
@@ -157,7 +157,7 @@ error:
free(conf->sc_ssl_dict);
free(conf->sc_limits_dict);
free(conf->sc_mda_wrappers);
- free(conf->sc_processors_dict);
+ free(conf->sc_filter_processes_dict);
free(conf->sc_dispatcher_bounce);
free(conf->sc_filters_dict);
free(limits);
diff --git a/smtpd/control.c b/usr.sbin/smtpd/control.c
index 0e35bbd1..dbb2840d 100644
--- a/smtpd/control.c
+++ b/usr.sbin/smtpd/control.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: control.c,v 1.123 2018/05/31 21:06:12 gilles Exp $ */
+/* $OpenBSD: control.c,v 1.125 2020/09/23 19:11:50 martijn Exp $ */
/*
* Copyright (c) 2012 Gilles Chehade <gilles@poolp.org>
diff --git a/smtpd/crypto.c b/usr.sbin/smtpd/crypto.c
index 20a422cd..20a422cd 100644
--- a/smtpd/crypto.c
+++ b/usr.sbin/smtpd/crypto.c
diff --git a/smtpd/dict.c b/usr.sbin/smtpd/dict.c
index e660f0a5..e660f0a5 100644
--- a/smtpd/dict.c
+++ b/usr.sbin/smtpd/dict.c
diff --git a/smtpd/dict.h b/usr.sbin/smtpd/dict.h
index c5d47e1a..c5d47e1a 100644
--- a/smtpd/dict.h
+++ b/usr.sbin/smtpd/dict.h
diff --git a/smtpd/dns.c b/usr.sbin/smtpd/dns.c
index a3107e89..a3107e89 100644
--- a/smtpd/dns.c
+++ b/usr.sbin/smtpd/dns.c
diff --git a/smtpd/enqueue.c b/usr.sbin/smtpd/enqueue.c
index 905ba585..0ef694b5 100644
--- a/smtpd/enqueue.c
+++ b/usr.sbin/smtpd/enqueue.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: enqueue.c,v 1.116 2019/07/02 09:36:20 martijn Exp $ */
+/* $OpenBSD: enqueue.c,v 1.118 2020/03/18 20:17:14 eric Exp $ */
/*
* Copyright (c) 2005 Henning Brauer <henning@bulabula.org>
@@ -136,32 +136,51 @@ struct {
char buf[SMTP_LINELEN];
} pstate;
+#define QP_TEST_WRAP(fp, buf, linelen, size) do { \
+ if (((linelen) += (size)) + 1 > 76) { \
+ fprintf((fp), "=\r\n"); \
+ if (buf[0] == '.') \
+ fprintf((fp), "."); \
+ (linelen) = (size); \
+ } \
+} while (0)
+
+/* RFC 2045 section 6.7 */
static void
-qp_encoded_write(FILE *fp, char *buf, size_t len)
+qp_encoded_write(FILE *fp, char *buf)
{
- while (len) {
- if (*buf == '=')
- fprintf(fp, "=3D");
- else if (*buf == ' ' || *buf == '\t') {
- char *p = buf;
-
- while (*p != '\n') {
- if (*p != ' ' && *p != '\t')
- break;
- p++;
- }
- if (*p == '\n')
+ size_t linelen = 0;
+
+ for (;buf[0] != '\0' && buf[0] != '\n'; buf++) {
+ /*
+ * Point 3: Any TAB (HT) or SPACE characters on an encoded line
+ * MUST thus be followed on that line by a printable character.
+ *
+ * Ergo, only encode if the next character is EOL.
+ */
+ if (buf[0] == ' ' || buf[0] == '\t') {
+ if (buf[1] == '\n') {
+ QP_TEST_WRAP(fp, buf, linelen, 3);
fprintf(fp, "=%2X", *buf & 0xff);
- else
+ } else {
+ QP_TEST_WRAP(fp, buf, linelen, 1);
fprintf(fp, "%c", *buf & 0xff);
- }
- else if (!isprint((unsigned char)*buf) && *buf != '\n')
+ }
+ /*
+ * Point 1, with exclusion of point 2, skip EBCDIC NOTE.
+ * Do this after whitespace check, else they would match here.
+ */
+ } else if (!((buf[0] >= 33 && buf[0] <= 60) ||
+ (buf[0] >= 62 && buf[0] <= 126))) {
+ QP_TEST_WRAP(fp, buf, linelen, 3);
fprintf(fp, "=%2X", *buf & 0xff);
- else
+ /* Point 2: 33 through 60 inclusive, and 62 through 126 */
+ } else {
+ QP_TEST_WRAP(fp, buf, linelen, 1);
fprintf(fp, "%c", *buf);
- buf++;
- len--;
+ }
}
+ fprintf(fp, "\r\n");
}
int
@@ -174,7 +193,6 @@ enqueue(int argc, char *argv[], FILE *ofp)
size_t sz = 0, envid_sz = 0;
ssize_t len;
char *line;
- int dotted;
int inheaders = 1;
int save_argc;
char **save_argv;
@@ -312,7 +330,7 @@ enqueue(int argc, char *argv[], FILE *ofp)
if (!get_responses(fout, 1))
goto fail;
- if (!send_line(fout, verbose, "EHLO localhost\n"))
+ if (!send_line(fout, verbose, "EHLO localhost\r\n"))
goto fail;
if (!get_responses(fout, 1))
goto fail;
@@ -320,7 +338,7 @@ enqueue(int argc, char *argv[], FILE *ofp)
if (msg.dsn_envid != NULL)
envid_sz = strlen(msg.dsn_envid);
- if (!send_line(fout, verbose, "MAIL FROM:<%s> %s%s %s%s\n",
+ if (!send_line(fout, verbose, "MAIL FROM:<%s> %s%s %s%s\r\n",
msg.from,
msg.dsn_ret ? "RET=" : "",
msg.dsn_ret ? msg.dsn_ret : "",
@@ -331,7 +349,7 @@ enqueue(int argc, char *argv[], FILE *ofp)
goto fail;
for (i = 0; i < msg.rcpt_cnt; i++) {
- if (!send_line(fout, verbose, "RCPT TO:<%s> %s%s\n",
+ if (!send_line(fout, verbose, "RCPT TO:<%s> %s%s\r\n",
msg.rcpts[i],
msg.dsn_notify ? "NOTIFY=" : "",
msg.dsn_notify ? msg.dsn_notify : ""))
@@ -340,41 +358,41 @@ enqueue(int argc, char *argv[], FILE *ofp)
goto fail;
}
- if (!send_line(fout, verbose, "DATA\n"))
+ if (!send_line(fout, verbose, "DATA\r\n"))
goto fail;
if (!get_responses(fout, 1))
goto fail;
/* add From */
- if (!msg.saw_from && !send_line(fout, 0, "From: %s%s<%s>\n",
+ if (!msg.saw_from && !send_line(fout, 0, "From: %s%s<%s>\r\n",
msg.fromname ? msg.fromname : "", msg.fromname ? " " : "",
msg.from))
goto fail;
/* add Date */
- if (!msg.saw_date && !send_line(fout, 0, "Date: %s\n",
+ if (!msg.saw_date && !send_line(fout, 0, "Date: %s\r\n",
time_to_text(timestamp)))
goto fail;
if (msg.need_linesplit) {
/* we will always need to mime encode for long lines */
if (!msg.saw_mime_version && !send_line(fout, 0,
- "MIME-Version: 1.0\n"))
+ "MIME-Version: 1.0\r\n"))
goto fail;
if (!msg.saw_content_type && !send_line(fout, 0,
- "Content-Type: text/plain; charset=unknown-8bit\n"))
+ "Content-Type: text/plain; charset=unknown-8bit\r\n"))
goto fail;
if (!msg.saw_content_disposition && !send_line(fout, 0,
- "Content-Disposition: inline\n"))
+ "Content-Disposition: inline\r\n"))
goto fail;
if (!msg.saw_content_transfer_encoding && !send_line(fout, 0,
- "Content-Transfer-Encoding: quoted-printable\n"))
+ "Content-Transfer-Encoding: quoted-printable\r\n"))
goto fail;
}
/* add separating newline */
if (msg.noheader) {
- if (!send_line(fout, 0, "\n"))
+ if (!send_line(fout, 0, "\r\n"))
goto fail;
inheaders = 0;
}
@@ -390,12 +408,11 @@ enqueue(int argc, char *argv[], FILE *ofp)
/* newlines have been normalized on first parsing */
if (buf[len-1] != '\n')
errx(EX_SOFTWARE, "expect EOL");
+ len--;
- dotted = 0;
if (buf[0] == '.') {
if (fputc('.', fout) == EOF)
goto fail;
- dotted = 1;
}
line = buf;
@@ -409,7 +426,7 @@ enqueue(int argc, char *argv[], FILE *ofp)
if (msg.saw_content_transfer_encoding || msg.noheader ||
inheaders || !msg.need_linesplit) {
- if (!send_line(fout, 0, "%.*s", (int)len, line))
+ if (!send_line(fout, 0, "%.*s\r\n", (int)len, line))
goto fail;
if (inheaders && buf[0] == '\n')
inheaders = 0;
@@ -417,28 +434,15 @@ enqueue(int argc, char *argv[], FILE *ofp)
}
/* we don't have a content transfer encoding, use our default */
- do {
- if (len < LINESPLIT) {
- qp_encoded_write(fout, line, len);
- break;
- }
- else {
- qp_encoded_write(fout, line,
- LINESPLIT - 2 - dotted);
- if (!send_line(fout, 0, "=\n"))
- goto fail;
- line += LINESPLIT - 2 - dotted;
- len -= LINESPLIT - 2 - dotted;
- }
- } while (len);
+ qp_encoded_write(fout, line);
}
free(buf);
- if (!send_line(fout, verbose, ".\n"))
+ if (!send_line(fout, verbose, ".\r\n"))
goto fail;
if (!get_responses(fout, 1))
goto fail;
- if (!send_line(fout, verbose, "QUIT\n"))
+ if (!send_line(fout, verbose, "QUIT\r\n"))
goto fail;
if (!get_responses(fout, 1))
goto fail;
diff --git a/smtpd/envelope.c b/usr.sbin/smtpd/envelope.c
index c02a0e75..35d98b79 100644
--- a/smtpd/envelope.c
+++ b/usr.sbin/smtpd/envelope.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: envelope.c,v 1.46 2019/09/19 16:00:59 gilles Exp $ */
+/* $OpenBSD: envelope.c,v 1.47 2019/11/25 14:18:32 gilles Exp $ */
/*
* Copyright (c) 2013 Eric Faurot <eric@openbsd.org>
@@ -179,6 +179,7 @@ envelope_dump_buffer(const struct envelope *ep, char *dest, size_t len)
envelope_ascii_dump(ep, &dest, &len, "smtpname");
envelope_ascii_dump(ep, &dest, &len, "helo");
envelope_ascii_dump(ep, &dest, &len, "hostname");
+ envelope_ascii_dump(ep, &dest, &len, "username");
envelope_ascii_dump(ep, &dest, &len, "errorline");
envelope_ascii_dump(ep, &dest, &len, "sockaddr");
envelope_ascii_dump(ep, &dest, &len, "sender");
@@ -408,6 +409,9 @@ ascii_load_field(const char *field, struct envelope *ep, char *buf)
if (strcasecmp("dest", field) == 0)
return ascii_load_mailaddr(&ep->dest, buf);
+ if (strcasecmp("username", field) == 0)
+ return ascii_load_string(ep->username, buf, sizeof(ep->username));
+
if (strcasecmp("errorline", field) == 0)
return ascii_load_string(ep->errorline, buf,
sizeof ep->errorline);
@@ -654,6 +658,12 @@ ascii_dump_field(const char *field, const struct envelope *ep,
if (strcasecmp(field, "dest") == 0)
return ascii_dump_mailaddr(&ep->dest, buf, len);
+ if (strcasecmp(field, "username") == 0) {
+ if (ep->username[0])
+ return ascii_dump_string(ep->username, buf, len);
+ return 1;
+ }
+
if (strcasecmp(field, "errorline") == 0)
return ascii_dump_string(ep->errorline, buf, len);
diff --git a/smtpd/esc.c b/usr.sbin/smtpd/esc.c
index 64a44c79..64a44c79 100644
--- a/smtpd/esc.c
+++ b/usr.sbin/smtpd/esc.c
diff --git a/smtpd/expand.c b/usr.sbin/smtpd/expand.c
index a4306fc0..a4306fc0 100644
--- a/smtpd/expand.c
+++ b/usr.sbin/smtpd/expand.c
diff --git a/smtpd/filter.c b/usr.sbin/smtpd/filter.c
index 614486b7..614486b7 100644
--- a/smtpd/filter.c
+++ b/usr.sbin/smtpd/filter.c
diff --git a/smtpd/forward.5 b/usr.sbin/smtpd/forward.5
index 5a68f229..5a68f229 100644
--- a/smtpd/forward.5
+++ b/usr.sbin/smtpd/forward.5
diff --git a/smtpd/forward.c b/usr.sbin/smtpd/forward.c
index 7494c6ce..7494c6ce 100644
--- a/smtpd/forward.c
+++ b/usr.sbin/smtpd/forward.c
diff --git a/smtpd/iobuf.c b/usr.sbin/smtpd/iobuf.c
index ceb2ac01..dec10660 100644
--- a/smtpd/iobuf.c
+++ b/usr.sbin/smtpd/iobuf.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: iobuf.c,v 1.12 2019/10/03 07:03:23 gilles Exp $ */
+/* $OpenBSD: iobuf.c,v 1.13 2020/04/24 11:34:07 eric Exp $ */
/*
* Copyright (c) 2012 Eric Faurot <eric@openbsd.org>
*
@@ -176,10 +176,9 @@ iobuf_getline(struct iobuf *iobuf, size_t *rlen)
* the next call to iobuf_normalize() or iobuf_extend().
*/
iobuf_drop(iobuf, i + 1);
- len = (i && buf[i - 1] == '\r') ? i - 1 : i;
- buf[len] = '\0';
+ buf[i] = '\0';
if (rlen)
- *rlen = len;
+ *rlen = i;
return (buf);
}
diff --git a/smtpd/iobuf.h b/usr.sbin/smtpd/iobuf.h
index c454d0a1..c454d0a1 100644
--- a/smtpd/iobuf.h
+++ b/usr.sbin/smtpd/iobuf.h
diff --git a/smtpd/ioev.c b/usr.sbin/smtpd/ioev.c
index e0a8a096..e0a8a096 100644
--- a/smtpd/ioev.c
+++ b/usr.sbin/smtpd/ioev.c
diff --git a/smtpd/ioev.h b/usr.sbin/smtpd/ioev.h
index f155a7fc..f155a7fc 100644
--- a/smtpd/ioev.h
+++ b/usr.sbin/smtpd/ioev.h
diff --git a/smtpd/libressl.c b/usr.sbin/smtpd/libressl.c
index 57d74389..57d74389 100644
--- a/smtpd/libressl.c
+++ b/usr.sbin/smtpd/libressl.c
diff --git a/smtpd/limit.c b/usr.sbin/smtpd/limit.c
index 25e7a026..25e7a026 100644
--- a/smtpd/limit.c
+++ b/usr.sbin/smtpd/limit.c
diff --git a/smtpd/lka.c b/usr.sbin/smtpd/lka.c
index 6d1d3cf3..6ac21245 100644
--- a/smtpd/lka.c
+++ b/usr.sbin/smtpd/lka.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: lka.c,v 1.240 2019/08/28 15:50:36 martijn Exp $ */
+/* $OpenBSD: lka.c,v 1.243 2019/12/21 10:23:37 gilles Exp $ */
/*
* Copyright (c) 2008 Pierre-Yves Ritschard <pyr@openbsd.org>
@@ -98,6 +98,7 @@ lka_imsg(struct mproc *p, struct imsg *imsg)
int filter_phase;
const char *filter_param;
uint32_t msgid;
+ uint32_t subsystems;
uint64_t evpid;
size_t msgsz;
int ok;
@@ -278,6 +279,7 @@ lka_imsg(struct mproc *p, struct imsg *imsg)
case IMSG_MTA_LOOKUP_SMARTHOST:
m_msg(&m, imsg);
m_get_id(&m, &reqid);
+ m_get_string(&m, &domain);
m_get_string(&m, &tablename);
m_end(&m);
@@ -291,7 +293,11 @@ lka_imsg(struct mproc *p, struct imsg *imsg)
m_add_int(p, LKA_TEMPFAIL);
}
else {
- ret = table_fetch(table, K_RELAYHOST, &lk);
+ if (domain == NULL)
+ ret = table_fetch(table, K_RELAYHOST, &lk);
+ else
+ ret = table_lookup(table, K_RELAYHOST, domain, &lk);
+
if (ret == -1)
m_add_int(p, LKA_TEMPFAIL);
else if (ret == 0)
@@ -368,13 +374,14 @@ lka_imsg(struct mproc *p, struct imsg *imsg)
case IMSG_LKA_PROCESSOR_FORK:
m_msg(&m, imsg);
m_get_string(&m, &procname);
+ m_get_u32(&m, &subsystems);
m_end(&m);
m_create(p, IMSG_LKA_PROCESSOR_ERRFD, 0, 0, -1);
m_add_string(p, procname);
m_close(p);
- lka_proc_forked(procname, imsg->fd);
+ lka_proc_forked(procname, subsystems, imsg->fd);
return;
case IMSG_LKA_PROCESSOR_ERRFD:
@@ -611,13 +618,9 @@ lka_imsg(struct mproc *p, struct imsg *imsg)
m_msg(&m, imsg);
m_get_id(&m, &reqid);
m_get_string(&m, &filter_name);
- m_get_sockaddr(&m, (struct sockaddr *)&ss_src);
- m_get_sockaddr(&m, (struct sockaddr *)&ss_dest);
- m_get_string(&m, &rdns);
- m_get_int(&m, &fcrdns);
m_end(&m);
- lka_filter_begin(reqid, filter_name, &ss_src, &ss_dest, rdns, fcrdns);
+ lka_filter_begin(reqid, filter_name);
return;
case IMSG_FILTER_SMTP_END:
diff --git a/smtpd/lka_filter.c b/usr.sbin/smtpd/lka_filter.c
index 056d9e8f..7931ede4 100644
--- a/smtpd/lka_filter.c
+++ b/usr.sbin/smtpd/lka_filter.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: lka_filter.c,v 1.50 2019/09/11 20:06:26 gilles Exp $ */
+/* $OpenBSD: lka_filter.c,v 1.63 2020/09/16 11:19:42 martijn Exp $ */
/*
* Copyright (c) 2018 Gilles Chehade <gilles@poolp.org>
@@ -37,7 +37,7 @@
#include "smtpd.h"
#include "log.h"
-#define PROTOCOL_VERSION "0.4"
+#define PROTOCOL_VERSION "0.6"
struct filter;
struct filter_session;
@@ -56,6 +56,8 @@ static int filter_builtins_connect(struct filter_session *, struct filter *, uin
static int filter_builtins_helo(struct filter_session *, struct filter *, uint64_t, const char *);
static int filter_builtins_mail_from(struct filter_session *, struct filter *, uint64_t, const char *);
static int filter_builtins_rcpt_to(struct filter_session *, struct filter *, uint64_t, const char *);
+static int filter_builtins_data(struct filter_session *, struct filter *, uint64_t, const char *);
+static int filter_builtins_commit(struct filter_session *, struct filter *, uint64_t, const char *);
static void filter_result_proceed(uint64_t);
static void filter_result_junk(uint64_t);
@@ -80,6 +82,7 @@ struct filter_session {
int fcrdns;
char *helo;
+ char *username;
char *mail_from;
enum filter_phase phase;
@@ -97,14 +100,14 @@ static struct filter_exec {
{ FILTER_AUTH, "auth", filter_builtins_notimpl },
{ FILTER_MAIL_FROM, "mail-from", filter_builtins_mail_from },
{ FILTER_RCPT_TO, "rcpt-to", filter_builtins_rcpt_to },
- { FILTER_DATA, "data", filter_builtins_notimpl },
+ { FILTER_DATA, "data", filter_builtins_data },
{ FILTER_DATA_LINE, "data-line", filter_builtins_notimpl },
{ FILTER_RSET, "rset", filter_builtins_notimpl },
{ FILTER_QUIT, "quit", filter_builtins_notimpl },
{ FILTER_NOOP, "noop", filter_builtins_notimpl },
{ FILTER_HELP, "help", filter_builtins_notimpl },
{ FILTER_WIZ, "wiz", filter_builtins_notimpl },
- { FILTER_COMMIT, "commit", filter_builtins_notimpl },
+ { FILTER_COMMIT, "commit", filter_builtins_commit },
};
struct filter {
@@ -128,13 +131,210 @@ struct filter_chain {
TAILQ_HEAD(, filter_entry) chain[nitems(filter_execs)];
};
-static struct dict smtp_in;
+static struct dict filter_smtp_in;
static struct tree sessions;
-static int inited;
+static int filters_inited;
static struct dict filter_chains;
+struct reporter_proc {
+ TAILQ_ENTRY(reporter_proc) entries;
+ const char *name;
+};
+TAILQ_HEAD(reporters, reporter_proc);
+
+static struct dict report_smtp_in;
+static struct dict report_smtp_out;
+
+static struct smtp_events {
+ const char *event;
+} smtp_events[] = {
+ { "link-connect" },
+ { "link-disconnect" },
+ { "link-greeting" },
+ { "link-identify" },
+ { "link-tls" },
+ { "link-auth" },
+
+ { "tx-reset" },
+ { "tx-begin" },
+ { "tx-mail" },
+ { "tx-rcpt" },
+ { "tx-envelope" },
+ { "tx-data" },
+ { "tx-commit" },
+ { "tx-rollback" },
+
+ { "protocol-client" },
+ { "protocol-server" },
+
+ { "filter-report" },
+ { "filter-response" },
+
+ { "timeout" },
+};
+
+static int processors_inited = 0;
+static struct dict processors;
+
+struct processor_instance {
+ char *name;
+ struct io *io;
+ struct io *errfd;
+ int ready;
+ uint32_t subsystems;
+};
+
+static void processor_io(struct io *, int, void *);
+static void processor_errfd(struct io *, int, void *);
+void lka_filter_process_response(const char *, const char *);
+
+int
+lka_proc_ready(void)
+{
+ void *iter;
+ struct processor_instance *pi;
+
+ iter = NULL;
+ while (dict_iter(&processors, &iter, NULL, (void **)&pi))
+ if (!pi->ready)
+ return 0;
+ return 1;
+}
+
+static void
+lka_proc_config(struct processor_instance *pi)
+{
+ io_printf(pi->io, "config|smtpd-version|%s\n", SMTPD_VERSION);
+ io_printf(pi->io, "config|smtp-session-timeout|%d\n", SMTPD_SESSION_TIMEOUT);
+ if (pi->subsystems & FILTER_SUBSYSTEM_SMTP_IN)
+ io_printf(pi->io, "config|subsystem|smtp-in\n");
+ if (pi->subsystems & FILTER_SUBSYSTEM_SMTP_OUT)
+ io_printf(pi->io, "config|subsystem|smtp-out\n");
+ io_printf(pi->io, "config|admd|%s\n",
+ env->sc_admd != NULL ? env->sc_admd : env->sc_hostname);
+ io_printf(pi->io, "config|ready\n");
+}
+
+void
+lka_proc_forked(const char *name, uint32_t subsystems, int fd)
+{
+ struct processor_instance *processor;
+
+ if (!processors_inited) {
+ dict_init(&processors);
+ processors_inited = 1;
+ }
+
+ processor = xcalloc(1, sizeof *processor);
+ processor->name = xstrdup(name);
+ processor->io = io_new();
+ processor->subsystems = subsystems;
+
+ io_set_nonblocking(fd);
+
+ io_set_fd(processor->io, fd);
+ io_set_callback(processor->io, processor_io, processor->name);
+ dict_xset(&processors, name, processor);
+}
+
+void
+lka_proc_errfd(const char *name, int fd)
+{
+ struct processor_instance *processor;
+
+ processor = dict_xget(&processors, name);
+
+ io_set_nonblocking(fd);
+
+ processor->errfd = io_new();
+ io_set_fd(processor->errfd, fd);
+ io_set_callback(processor->errfd, processor_errfd, processor->name);
+
+ lka_proc_config(processor);
+}
+
+struct io *
+lka_proc_get_io(const char *name)
+{
+ struct processor_instance *processor;
+
+ processor = dict_xget(&processors, name);
+
+ return processor->io;
+}
+
+static void
+processor_register(const char *name, const char *line)
+{
+ struct processor_instance *processor;
+
+ processor = dict_xget(&processors, name);
+
+ if (strcmp(line, "register|ready") == 0) {
+ processor->ready = 1;
+ return;
+ }
+
+ if (strncmp(line, "register|report|", 16) == 0) {
+ lka_report_register_hook(name, line+16);
+ return;
+ }
+
+ if (strncmp(line, "register|filter|", 16) == 0) {
+ lka_filter_register_hook(name, line+16);
+ return;
+ }
+
+ fatalx("Invalid register line received: %s", line);
+}
+
+static void
+processor_io(struct io *io, int evt, void *arg)
+{
+ struct processor_instance *processor;
+ const char *name = arg;
+ char *line = NULL;
+ ssize_t len;
+
+ switch (evt) {
+ case IO_DATAIN:
+ while ((line = io_getline(io, &len)) != NULL) {
+ if (strncmp("register|", line, 9) == 0) {
+ processor_register(name, line);
+ continue;
+ }
+
+ processor = dict_xget(&processors, name);
+ if (!processor->ready)
+ fatalx("Non-register message before register|"
+ "ready: %s", line);
+ else if (strncmp(line, "filter-result|", 14) == 0 ||
+ strncmp(line, "filter-dataline|", 16) == 0)
+ lka_filter_process_response(name, line);
+ else if (strncmp(line, "report|", 7) == 0)
+ lka_report_proc(name, line);
+ else
+ fatalx("Invalid filter message type: %s", line);
+ }
+ }
+}
+
+static void
+processor_errfd(struct io *io, int evt, void *arg)
+{
+ const char *name = arg;
+ char *line = NULL;
+ ssize_t len;
+
+ switch (evt) {
+ case IO_DATAIN:
+ while ((line = io_getline(io, &len)) != NULL)
+ log_warnx("%s: %s", name, line);
+ }
+}
+
void
lka_filter_init(void)
{
@@ -217,7 +417,7 @@ lka_filter_register_hook(const char *name, const char *hook)
size_t i;
if (strncasecmp(hook, "smtp-in|", 8) == 0) {
- subsystem = &smtp_in;
+ subsystem = &filter_smtp_in;
hook += 8;
}
else
@@ -294,7 +494,7 @@ lka_filter_proc_in_session(uint64_t reqid, const char *proc)
return 0;
filter = dict_get(&filters, fs->filter_name);
- if (filter->proc == NULL && filter->chain == NULL)
+ if (filter == NULL || (filter->proc == NULL && filter->chain == NULL))
return 0;
if (filter->proc)
@@ -309,27 +509,18 @@ lka_filter_proc_in_session(uint64_t reqid, const char *proc)
}
void
-lka_filter_begin(uint64_t reqid,
- const char *filter_name,
- const struct sockaddr_storage *ss_src,
- const struct sockaddr_storage *ss_dest,
- const char *rdns,
- int fcrdns)
+lka_filter_begin(uint64_t reqid, const char *filter_name)
{
struct filter_session *fs;
- if (!inited) {
+ if (!filters_inited) {
tree_init(&sessions);
- inited = 1;
+ filters_inited = 1;
}
fs = xcalloc(1, sizeof (struct filter_session));
fs->id = reqid;
fs->filter_name = xstrdup(filter_name);
- fs->ss_src = *ss_src;
- fs->ss_dest = *ss_dest;
- fs->rdns = xstrdup(rdns);
- fs->fcrdns = fcrdns;
tree_xset(&sessions, fs->id, fs);
log_trace(TRACE_FILTERS, "%016"PRIx64" filters session-begin", reqid);
@@ -344,6 +535,7 @@ lka_filter_end(uint64_t reqid)
free(fs->rdns);
free(fs->helo);
free(fs->mail_from);
+ free(fs->username);
free(fs->lastparam);
free(fs);
log_trace(TRACE_FILTERS, "%016"PRIx64" filters session-end", reqid);
@@ -443,22 +635,22 @@ lka_filter_process_response(const char *name, const char *line)
fatalx("Missing reqid: %s", line);
ep[0] = '\0';
- token = strtoull(qid, &ep, 16);
+ reqid = strtoull(qid, &ep, 16);
if (qid[0] == '\0' || *ep != '\0')
- fatalx("Invalid token: %s", line);
- if (errno == ERANGE && token == ULLONG_MAX)
- fatal("Invalid token: %s", line);
+ fatalx("Invalid reqid: %s", line);
+ if (errno == ERANGE && reqid == ULLONG_MAX)
+ fatal("Invalid reqid: %s", line);
qid = ep+1;
if ((ep = strchr(qid, '|')) == NULL)
fatal("Missing directive: %s", line);
ep[0] = '\0';
- reqid = strtoull(qid, &ep, 16);
+ token = strtoull(qid, &ep, 16);
if (qid[0] == '\0' || *ep != '\0')
- fatalx("Invalid reqid: %s", line);
- if (errno == ERANGE && reqid == ULLONG_MAX)
- fatal("Invalid reqid: %s", line);
+ fatalx("Invalid token: %s", line);
+ if (errno == ERANGE && token == ULLONG_MAX)
+ fatal("Invalid token: %s", line);
response = ep+1;
@@ -606,6 +798,14 @@ filter_protocol_internal(struct filter_session *fs, uint64_t *token, uint64_t re
gettimeofday(&tv, NULL);
lka_report_filter_report(fs->id, filter->name, 1,
"smtp-in", &tv, filter->config->report);
+ } else if (filter->config->bypass) {
+ log_trace(TRACE_FILTERS, "%016"PRIx64" filters protocol phase=%s, "
+ "resume=%s, action=bypass, filter=%s, query=%s",
+ fs->id, phase_name, resume ? "y" : "n",
+ filter->name,
+ param);
+ filter_result_proceed(reqid);
+ return;
} else {
log_trace(TRACE_FILTERS, "%016"PRIx64" filters protocol phase=%s, "
"resume=%s, action=reject, filter=%s, query=%s, response=%s",
@@ -655,7 +855,7 @@ filter_data_internal(struct filter_session *fs, uint64_t token, uint64_t reqid,
/* no filter_entry, we either had none or reached end of chain */
if (filter_entry == NULL) {
- io_printf(fs->io, "%s\r\n", line);
+ io_printf(fs->io, "%s\n", line);
return;
}
@@ -692,7 +892,6 @@ filter_protocol(uint64_t reqid, enum filter_phase phase, const char *param)
param = nparam;
break;
case FILTER_STARTTLS:
- case FILTER_AUTH:
/* TBD */
break;
default:
@@ -916,6 +1115,47 @@ filter_check_helo_regex(struct filter *filter, const char *key)
}
static int
+filter_check_auth(struct filter *filter, const char *username)
+{
+ int ret = 0;
+
+ if (!filter->config->auth)
+ return 0;
+
+ ret = username ? 1 : 0;
+
+ return filter->config->not_auth < 0 ? !ret : ret;
+}
+
+static int
+filter_check_auth_table(struct filter *filter, enum table_service kind, const char *key)
+{
+ int ret = 0;
+
+ if (filter->config->auth_table == NULL)
+ return 0;
+
+ if (key && table_match(filter->config->auth_table, kind, key) > 0)
+ ret = 1;
+
+ return filter->config->not_auth_table < 0 ? !ret : ret;
+}
+
+static int
+filter_check_auth_regex(struct filter *filter, const char *key)
+{
+ int ret = 0;
+
+ if (filter->config->auth_regex == NULL)
+ return 0;
+
+ if (key && table_match(filter->config->auth_regex, K_REGEX, key) > 0)
+ ret = 1;
+ return filter->config->not_auth_regex < 0 ? !ret : ret;
+}
+
+
+static int
filter_check_mail_from_table(struct filter *filter, enum table_service kind, const char *key)
{
int ret = 0;
@@ -1017,6 +1257,10 @@ filter_builtins_global(struct filter_session *fs, struct filter *filter, uint64_
filter_check_src_regex(filter, ss_to_text(&fs->ss_src)) ||
filter_check_helo_table(filter, K_DOMAIN, fs->helo) ||
filter_check_helo_regex(filter, fs->helo) ||
+ filter_check_auth(filter, fs->username) ||
+ filter_check_auth_table(filter, K_STRING, fs->username) ||
+ filter_check_auth_table(filter, K_CREDENTIALS, fs->username) ||
+ filter_check_auth_regex(filter, fs->username) ||
filter_check_mail_from_table(filter, K_MAILADDR, fs->mail_from) ||
filter_check_mail_from_regex(filter, fs->mail_from);
}
@@ -1046,3 +1290,459 @@ filter_builtins_rcpt_to(struct filter_session *fs, struct filter *filter, uint64
filter_check_rcpt_to_table(filter, K_MAILADDR, param) ||
filter_check_rcpt_to_regex(filter, param);
}
+
+static int
+filter_builtins_data(struct filter_session *fs, struct filter *filter, uint64_t reqid, const char *param)
+{
+ return filter_builtins_global(fs, filter, reqid);
+}
+
+static int
+filter_builtins_commit(struct filter_session *fs, struct filter *filter, uint64_t reqid, const char *param)
+{
+ return filter_builtins_global(fs, filter, reqid);
+}
+
+static void
+report_smtp_broadcast(uint64_t, const char *, struct timeval *, const char *,
+ const char *, ...) __attribute__((__format__ (printf, 5, 6)));
+
+void
+lka_report_init(void)
+{
+ struct reporters *tailq;
+ size_t i;
+
+ dict_init(&report_smtp_in);
+ dict_init(&report_smtp_out);
+
+ for (i = 0; i < nitems(smtp_events); ++i) {
+ tailq = xcalloc(1, sizeof (struct reporters));
+ TAILQ_INIT(tailq);
+ dict_xset(&report_smtp_in, smtp_events[i].event, tailq);
+
+ tailq = xcalloc(1, sizeof (struct reporters));
+ TAILQ_INIT(tailq);
+ dict_xset(&report_smtp_out, smtp_events[i].event, tailq);
+ }
+}
+
+void
+lka_report_register_hook(const char *name, const char *hook)
+{
+ struct dict *subsystem;
+ struct reporter_proc *rp;
+ struct reporters *tailq;
+ void *iter;
+ size_t i;
+
+ if (strncmp(hook, "smtp-in|", 8) == 0) {
+ subsystem = &report_smtp_in;
+ hook += 8;
+ }
+ else if (strncmp(hook, "smtp-out|", 9) == 0) {
+ subsystem = &report_smtp_out;
+ hook += 9;
+ }
+ else
+ fatalx("Invalid message direction: %s", hook);
+
+ if (strcmp(hook, "*") == 0) {
+ iter = NULL;
+ while (dict_iter(subsystem, &iter, NULL, (void **)&tailq)) {
+ rp = xcalloc(1, sizeof *rp);
+ rp->name = xstrdup(name);
+ TAILQ_INSERT_TAIL(tailq, rp, entries);
+ }
+ return;
+ }
+
+ for (i = 0; i < nitems(smtp_events); i++)
+ if (strcmp(hook, smtp_events[i].event) == 0)
+ break;
+ if (i == nitems(smtp_events))
+ fatalx("Unrecognized report name: %s", hook);
+
+ tailq = dict_get(subsystem, hook);
+ rp = xcalloc(1, sizeof *rp);
+ rp->name = xstrdup(name);
+ TAILQ_INSERT_TAIL(tailq, rp, entries);
+}
+
+static void
+report_smtp_broadcast(uint64_t reqid, const char *direction, struct timeval *tv, const char *event,
+ const char *format, ...)
+{
+ va_list ap;
+ struct dict *d;
+ struct reporters *tailq;
+ struct reporter_proc *rp;
+
+ if (strcmp("smtp-in", direction) == 0)
+ d = &report_smtp_in;
+
+ else if (strcmp("smtp-out", direction) == 0)
+ d = &report_smtp_out;
+
+ else
+ fatalx("unexpected direction: %s", direction);
+
+ tailq = dict_xget(d, event);
+ TAILQ_FOREACH(rp, tailq, entries) {
+ if (!lka_filter_proc_in_session(reqid, rp->name))
+ continue;
+
+ va_start(ap, format);
+ if (io_printf(lka_proc_get_io(rp->name),
+ "report|%s|%lld.%06ld|%s|%s|%016"PRIx64"%s",
+ PROTOCOL_VERSION, (long long int)tv->tv_sec, tv->tv_usec, direction,
+ event, reqid, format[0] != '\n' ? "|" : "") == -1 ||
+ io_vprintf(lka_proc_get_io(rp->name), format, ap) == -1)
+ fatalx("failed to write to processor");
+ va_end(ap);
+ }
+}
+
+void
+lka_report_smtp_link_connect(const char *direction, struct timeval *tv, uint64_t reqid, const char *rdns,
+ int fcrdns,
+ const struct sockaddr_storage *ss_src,
+ const struct sockaddr_storage *ss_dest)
+{
+ struct filter_session *fs;
+ char src[NI_MAXHOST + 5];
+ char dest[NI_MAXHOST + 5];
+ uint16_t src_port = 0;
+ uint16_t dest_port = 0;
+ const char *fcrdns_str;
+
+ if (ss_src->ss_family == AF_INET)
+ src_port = ntohs(((const struct sockaddr_in *)ss_src)->sin_port);
+ else if (ss_src->ss_family == AF_INET6)
+ src_port = ntohs(((const struct sockaddr_in6 *)ss_src)->sin6_port);
+
+ if (ss_dest->ss_family == AF_INET)
+ dest_port = ntohs(((const struct sockaddr_in *)ss_dest)->sin_port);
+ else if (ss_dest->ss_family == AF_INET6)
+ dest_port = ntohs(((const struct sockaddr_in6 *)ss_dest)->sin6_port);
+
+ if (strcmp(ss_to_text(ss_src), "local") == 0) {
+ (void)snprintf(src, sizeof src, "unix:%s", SMTPD_SOCKET);
+ (void)snprintf(dest, sizeof dest, "unix:%s", SMTPD_SOCKET);
+ } else {
+ (void)snprintf(src, sizeof src, "%s:%d", ss_to_text(ss_src), src_port);
+ (void)snprintf(dest, sizeof dest, "%s:%d", ss_to_text(ss_dest), dest_port);
+ }
+
+ switch (fcrdns) {
+ case 1:
+ fcrdns_str = "pass";
+ break;
+ case 0:
+ fcrdns_str = "fail";
+ break;
+ default:
+ fcrdns_str = "error";
+ break;
+ }
+
+ fs = tree_xget(&sessions, reqid);
+ fs->rdns = xstrdup(rdns);
+ fs->fcrdns = fcrdns;
+ fs->ss_src = *ss_src;
+ fs->ss_dest = *ss_dest;
+
+ report_smtp_broadcast(reqid, direction, tv, "link-connect",
+ "%s|%s|%s|%s\n", rdns, fcrdns_str, src, dest);
+}
+
+void
+lka_report_smtp_link_disconnect(const char *direction, struct timeval *tv, uint64_t reqid)
+{
+ report_smtp_broadcast(reqid, direction, tv, "link-disconnect", "\n");
+}
+
+void
+lka_report_smtp_link_greeting(const char *direction, uint64_t reqid,
+ struct timeval *tv, const char *domain)
+{
+ report_smtp_broadcast(reqid, direction, tv, "link-greeting", "%s\n",
+ domain);
+}
+
+void
+lka_report_smtp_link_auth(const char *direction, struct timeval *tv, uint64_t reqid,
+ const char *username, const char *result)
+{
+ struct filter_session *fs;
+
+ if (strcmp(result, "pass") == 0) {
+ fs = tree_xget(&sessions, reqid);
+ fs->username = xstrdup(username);
+ }
+ report_smtp_broadcast(reqid, direction, tv, "link-auth", "%s|%s\n",
+ username, result);
+}
+
+void
+lka_report_smtp_link_identify(const char *direction, struct timeval *tv,
+ uint64_t reqid, const char *method, const char *heloname)
+{
+ report_smtp_broadcast(reqid, direction, tv, "link-identify", "%s|%s\n",
+ method, heloname);
+}
+
+void
+lka_report_smtp_link_tls(const char *direction, struct timeval *tv, uint64_t reqid, const char *ciphers)
+{
+ report_smtp_broadcast(reqid, direction, tv, "link-tls", "%s\n",
+ ciphers);
+}
+
+void
+lka_report_smtp_tx_reset(const char *direction, struct timeval *tv, uint64_t reqid, uint32_t msgid)
+{
+ report_smtp_broadcast(reqid, direction, tv, "tx-reset", "%08x\n",
+ msgid);
+}
+
+void
+lka_report_smtp_tx_begin(const char *direction, struct timeval *tv, uint64_t reqid, uint32_t msgid)
+{
+ report_smtp_broadcast(reqid, direction, tv, "tx-begin", "%08x\n",
+ msgid);
+}
+
+void
+lka_report_smtp_tx_mail(const char *direction, struct timeval *tv, uint64_t reqid, uint32_t msgid, const char *address, int ok)
+{
+ const char *result;
+
+ switch (ok) {
+ case 1:
+ result = "ok";
+ break;
+ case 0:
+ result = "permfail";
+ break;
+ default:
+ result = "tempfail";
+ break;
+ }
+ report_smtp_broadcast(reqid, direction, tv, "tx-mail", "%08x|%s|%s\n",
+ msgid, result, address);
+}
+
+void
+lka_report_smtp_tx_rcpt(const char *direction, struct timeval *tv, uint64_t reqid, uint32_t msgid, const char *address, int ok)
+{
+ const char *result;
+
+ switch (ok) {
+ case 1:
+ result = "ok";
+ break;
+ case 0:
+ result = "permfail";
+ break;
+ default:
+ result = "tempfail";
+ break;
+ }
+ report_smtp_broadcast(reqid, direction, tv, "tx-rcpt", "%08x|%s|%s\n",
+ msgid, result, address);
+}
+
+void
+lka_report_smtp_tx_envelope(const char *direction, struct timeval *tv, uint64_t reqid, uint32_t msgid, uint64_t evpid)
+{
+ report_smtp_broadcast(reqid, direction, tv, "tx-envelope",
+ "%08x|%016"PRIx64"\n", msgid, evpid);
+}
+
+void
+lka_report_smtp_tx_data(const char *direction, struct timeval *tv, uint64_t reqid, uint32_t msgid, int ok)
+{
+ const char *result;
+
+ switch (ok) {
+ case 1:
+ result = "ok";
+ break;
+ case 0:
+ result = "permfail";
+ break;
+ default:
+ result = "tempfail";
+ break;
+ }
+ report_smtp_broadcast(reqid, direction, tv, "tx-data", "%08x|%s\n",
+ msgid, result);
+}
+
+void
+lka_report_smtp_tx_commit(const char *direction, struct timeval *tv, uint64_t reqid, uint32_t msgid, size_t msgsz)
+{
+ report_smtp_broadcast(reqid, direction, tv, "tx-commit", "%08x|%zd\n",
+ msgid, msgsz);
+}
+
+void
+lka_report_smtp_tx_rollback(const char *direction, struct timeval *tv, uint64_t reqid, uint32_t msgid)
+{
+ report_smtp_broadcast(reqid, direction, tv, "tx-rollback", "%08x\n",
+ msgid);
+}
+
+void
+lka_report_smtp_protocol_client(const char *direction, struct timeval *tv, uint64_t reqid, const char *command)
+{
+ report_smtp_broadcast(reqid, direction, tv, "protocol-client", "%s\n",
+ command);
+}
+
+void
+lka_report_smtp_protocol_server(const char *direction, struct timeval *tv, uint64_t reqid, const char *response)
+{
+ report_smtp_broadcast(reqid, direction, tv, "protocol-server", "%s\n",
+ response);
+}
+
+void
+lka_report_smtp_filter_response(const char *direction, struct timeval *tv, uint64_t reqid,
+ int phase, int response, const char *param)
+{
+ const char *phase_name;
+ const char *response_name;
+
+ switch (phase) {
+ case FILTER_CONNECT:
+ phase_name = "connected";
+ break;
+ case FILTER_HELO:
+ phase_name = "helo";
+ break;
+ case FILTER_EHLO:
+ phase_name = "ehlo";
+ break;
+ case FILTER_STARTTLS:
+ phase_name = "tls";
+ break;
+ case FILTER_AUTH:
+ phase_name = "auth";
+ break;
+ case FILTER_MAIL_FROM:
+ phase_name = "mail-from";
+ break;
+ case FILTER_RCPT_TO:
+ phase_name = "rcpt-to";
+ break;
+ case FILTER_DATA:
+ phase_name = "data";
+ break;
+ case FILTER_DATA_LINE:
+ phase_name = "data-line";
+ break;
+ case FILTER_RSET:
+ phase_name = "rset";
+ break;
+ case FILTER_QUIT:
+ phase_name = "quit";
+ break;
+ case FILTER_NOOP:
+ phase_name = "noop";
+ break;
+ case FILTER_HELP:
+ phase_name = "help";
+ break;
+ case FILTER_WIZ:
+ phase_name = "wiz";
+ break;
+ case FILTER_COMMIT:
+ phase_name = "commit";
+ break;
+ default:
+ phase_name = "";
+ }
+
+ switch (response) {
+ case FILTER_PROCEED:
+ response_name = "proceed";
+ break;
+ case FILTER_JUNK:
+ response_name = "junk";
+ break;
+ case FILTER_REWRITE:
+ response_name = "rewrite";
+ break;
+ case FILTER_REJECT:
+ response_name = "reject";
+ break;
+ case FILTER_DISCONNECT:
+ response_name = "disconnect";
+ break;
+ default:
+ response_name = "";
+ }
+
+ report_smtp_broadcast(reqid, direction, tv, "filter-response",
+ "%s|%s%s%s\n", phase_name, response_name, param ? "|" : "",
+ param ? param : "");
+}
+
+void
+lka_report_smtp_timeout(const char *direction, struct timeval *tv, uint64_t reqid)
+{
+ report_smtp_broadcast(reqid, direction, tv, "timeout", "\n");
+}
+
+void
+lka_report_filter_report(uint64_t reqid, const char *name, int builtin,
+ const char *direction, struct timeval *tv, const char *message)
+{
+ report_smtp_broadcast(reqid, direction, tv, "filter-report",
+ "%s|%s|%s\n", builtin ? "builtin" : "proc",
+ name, message);
+}
+
+void
+lka_report_proc(const char *name, const char *line)
+{
+ char buffer[LINE_MAX];
+ struct timeval tv;
+ char *ep, *sp, *direction;
+ uint64_t reqid;
+
+ if (strlcpy(buffer, line + 7, sizeof(buffer)) >= sizeof(buffer))
+ fatalx("Invalid report: line too long: %s", line);
+
+ errno = 0;
+ tv.tv_sec = strtoll(buffer, &ep, 10);
+ if (ep[0] != '.' || errno != 0)
+ fatalx("Invalid report: invalid time: %s", line);
+ sp = ep + 1;
+ tv.tv_usec = strtol(sp, &ep, 10);
+ if (ep[0] != '|' || errno != 0)
+ fatalx("Invalid report: invalid time: %s", line);
+ if (ep - sp != 6)
+ fatalx("Invalid report: invalid time: %s", line);
+
+ direction = ep + 1;
+ if (strncmp(direction, "smtp-in|", 8) == 0) {
+ direction[7] = '\0';
+ direction += 7;
+#if 0
+ } else if (strncmp(direction, "smtp-out|", 9) == 0) {
+ direction[8] = '\0';
+ direction += 8;
+#endif
+ } else
+ fatalx("Invalid report: invalid direction: %s", line);
+
+ reqid = strtoull(sp, &ep, 16);
+ if (ep[0] != '|' || errno != 0)
+ fatalx("Invalid report: invalid reqid: %s", line);
+ sp = ep + 1;
+
+ lka_report_filter_report(reqid, name, 0, direction, &tv, sp);
+}
diff --git a/smtpd/lka_session.c b/usr.sbin/smtpd/lka_session.c
index 999e01d6..999e01d6 100644
--- a/smtpd/lka_session.c
+++ b/usr.sbin/smtpd/lka_session.c
diff --git a/smtpd/log.c b/usr.sbin/smtpd/log.c
index 14f681e3..14f681e3 100644
--- a/smtpd/log.c
+++ b/usr.sbin/smtpd/log.c
diff --git a/smtpd/log.h b/usr.sbin/smtpd/log.h
index 81d0973c..81d0973c 100644
--- a/smtpd/log.h
+++ b/usr.sbin/smtpd/log.h
diff --git a/smtpd/mail.lmtp.8 b/usr.sbin/smtpd/mail.lmtp.8
index 98dee00d..98dee00d 100644
--- a/smtpd/mail.lmtp.8
+++ b/usr.sbin/smtpd/mail.lmtp.8
diff --git a/smtpd/mail.lmtp.c b/usr.sbin/smtpd/mail.lmtp.c
index f24549ad..90b89990 100644
--- a/smtpd/mail.lmtp.c
+++ b/usr.sbin/smtpd/mail.lmtp.c
@@ -43,20 +43,21 @@ enum phase {
struct session {
const char *lhlo;
const char *mailfrom;
+ char *rcptto;
char **rcpts;
int n_rcpts;
};
-static FILE *lmtp_connect(const char *);
-static void lmtp_engine(FILE *, struct session *);
+static int lmtp_connect(const char *);
+static void lmtp_engine(int, struct session *);
static void stream_file(FILE *);
int
main(int argc, char *argv[])
{
int ch;
- FILE *conn;
+ int conn;
const char *destination = "localhost";
struct session session;
@@ -64,9 +65,10 @@ main(int argc, char *argv[])
errx(EX_TEMPFAIL, "mail.lmtp: may not be executed as root");
session.lhlo = "localhost";
- session.mailfrom = NULL;
+ session.mailfrom = getenv("SENDER");
+ session.rcptto = NULL;
- while ((ch = getopt(argc, argv, "d:l:f:")) != -1) {
+ while ((ch = getopt(argc, argv, "d:l:f:ru")) != -1) {
switch (ch) {
case 'd':
destination = optarg;
@@ -77,6 +79,15 @@ main(int argc, char *argv[])
case 'f':
session.mailfrom = optarg;
break;
+
+ case 'r':
+ session.rcptto = getenv("RECIPIENT");
+ break;
+
+ case 'u':
+ session.rcptto = getenv("USER");
+ break;
+
default:
break;
}
@@ -87,11 +98,17 @@ main(int argc, char *argv[])
if (session.mailfrom == NULL)
errx(EX_TEMPFAIL, "sender must be specified with -f");
- if (argc == 0)
+ if (argc == 0 && session.rcptto == NULL)
errx(EX_TEMPFAIL, "no recipient was specified");
- session.rcpts = argv;
- session.n_rcpts = argc;
+ if (session.rcptto) {
+ session.rcpts = &session.rcptto;
+ session.n_rcpts = 1;
+ }
+ else {
+ session.rcpts = argv;
+ session.n_rcpts = argc;
+ }
conn = lmtp_connect(destination);
lmtp_engine(conn, &session);
@@ -99,7 +116,7 @@ main(int argc, char *argv[])
return (0);
}
-static FILE *
+static int
lmtp_connect_inet(const char *destination)
{
struct addrinfo hints, *res, *res0;
@@ -173,10 +190,10 @@ lmtp_connect_inet(const char *destination)
errx(EX_TEMPFAIL, "%s", cause);
free(destcopy);
- return fdopen(s, "r+");
+ return s;
}
-static FILE *
+static int
lmtp_connect_unix(const char *destination)
{
struct sockaddr_un addr;
@@ -197,10 +214,10 @@ lmtp_connect_unix(const char *destination)
if (connect(s, (struct sockaddr *)&addr, sizeof addr) == -1)
err(EX_TEMPFAIL, "connect");
- return fdopen(s, "r+");
+ return s;
}
-static FILE *
+static int
lmtp_connect(const char *destination)
{
if (destination[0] == '/')
@@ -209,17 +226,30 @@ lmtp_connect(const char *destination)
}
static void
-lmtp_engine(FILE *conn, struct session *session)
+lmtp_engine(int fd_read, struct session *session)
{
+ int fd_write = 0;
+ FILE *file_read = 0;
+ FILE *file_write = 0;
char *line = NULL;
size_t linesize = 0;
ssize_t linelen;
enum phase phase = PHASE_BANNER;
+ if ((fd_write = dup(fd_read)) == -1)
+ err(EX_TEMPFAIL, "dup");
+
+ if ((file_read = fdopen(fd_read, "r")) == NULL)
+ err(EX_TEMPFAIL, "fdopen");
+
+ if ((file_write = fdopen(fd_write, "w")) == NULL)
+ err(EX_TEMPFAIL, "fdopen");
+
do {
- fflush(conn);
- if ((linelen = getline(&line, &linesize, conn)) == -1) {
- if (ferror(conn))
+ fflush(file_write);
+
+ if ((linelen = getline(&line, &linesize, file_read)) == -1) {
+ if (ferror(file_read))
err(EX_TEMPFAIL, "getline");
else
errx(EX_TEMPFAIL, "unexpected EOF from LMTP server");
@@ -228,9 +258,9 @@ lmtp_engine(FILE *conn, struct session *session)
line[strcspn(line, "\r")] = '\0';
if (linelen < 4 ||
- !isdigit(line[0]) ||
- !isdigit(line[1]) ||
- !isdigit(line[2]) ||
+ !isdigit((unsigned char)line[0]) ||
+ !isdigit((unsigned char)line[1]) ||
+ !isdigit((unsigned char)line[2]) ||
(line[3] != ' ' && line[3] != '-'))
errx(EX_TEMPFAIL, "LMTP server sent an invalid line");
@@ -243,17 +273,17 @@ lmtp_engine(FILE *conn, struct session *session)
switch (phase) {
case PHASE_BANNER:
- fprintf(conn, "LHLO %s\r\n", session->lhlo);
+ fprintf(file_write, "LHLO %s\r\n", session->lhlo);
phase++;
break;
case PHASE_HELO:
- fprintf(conn, "MAIL FROM:<%s>\r\n", session->mailfrom);
+ fprintf(file_write, "MAIL FROM:<%s>\r\n", session->mailfrom);
phase++;
break;
case PHASE_MAILFROM:
- fprintf(conn, "RCPT TO:<%s>\r\n", session->rcpts[session->n_rcpts - 1]);
+ fprintf(file_write, "RCPT TO:<%s>\r\n", session->rcpts[session->n_rcpts - 1]);
if (session->n_rcpts - 1 == 0) {
phase++;
break;
@@ -262,18 +292,18 @@ lmtp_engine(FILE *conn, struct session *session)
break;
case PHASE_RCPTTO:
- fprintf(conn, "DATA\r\n");
+ fprintf(file_write, "DATA\r\n");
phase++;
break;
case PHASE_DATA:
- stream_file(conn);
- fprintf(conn, ".\r\n");
+ stream_file(file_write);
+ fprintf(file_write, ".\r\n");
phase++;
break;
case PHASE_EOM:
- fprintf(conn, "QUIT\r\n");
+ fprintf(file_write, "QUIT\r\n");
phase++;
break;
diff --git a/smtpd/mail.maildir.8 b/usr.sbin/smtpd/mail.maildir.8
index ce822698..ce822698 100644
--- a/smtpd/mail.maildir.8
+++ b/usr.sbin/smtpd/mail.maildir.8
diff --git a/smtpd/mail.maildir.c b/usr.sbin/smtpd/mail.maildir.c
index fe6adba6..fe6adba6 100644
--- a/smtpd/mail.maildir.c
+++ b/usr.sbin/smtpd/mail.maildir.c
diff --git a/smtpd/mail.mboxfile.8 b/usr.sbin/smtpd/mail.mboxfile.8
index 015adcb5..015adcb5 100644
--- a/smtpd/mail.mboxfile.8
+++ b/usr.sbin/smtpd/mail.mboxfile.8
diff --git a/smtpd/mail.mboxfile.c b/usr.sbin/smtpd/mail.mboxfile.c
index c45d337c..097a8d96 100644
--- a/smtpd/mail.mboxfile.c
+++ b/usr.sbin/smtpd/mail.mboxfile.c
@@ -103,7 +103,7 @@ mboxfile_engine(const char *sender, const char *filename)
if (fflush(fp) == EOF ||
ferror(fp) ||
- fsync(fd) == -1 ||
+ (fsync(fd) == -1 && errno != EINVAL) ||
fclose(fp) == EOF)
err(EX_TEMPFAIL, NULL);
}
diff --git a/smtpd/mail.mda.8 b/usr.sbin/smtpd/mail.mda.8
index 61fed733..61fed733 100644
--- a/smtpd/mail.mda.8
+++ b/usr.sbin/smtpd/mail.mda.8
diff --git a/smtpd/mail.mda.c b/usr.sbin/smtpd/mail.mda.c
index 23958071..23958071 100644
--- a/smtpd/mail.mda.c
+++ b/usr.sbin/smtpd/mail.mda.c
diff --git a/smtpd/mail/Makefile b/usr.sbin/smtpd/mail/Makefile
index b2bc2a26..b2bc2a26 100644
--- a/smtpd/mail/Makefile
+++ b/usr.sbin/smtpd/mail/Makefile
diff --git a/smtpd/mailaddr.c b/usr.sbin/smtpd/mailaddr.c
index 4346e3dc..0965aea8 100644
--- a/smtpd/mailaddr.c
+++ b/usr.sbin/smtpd/mailaddr.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: mailaddr.c,v 1.3 2018/05/31 21:06:12 gilles Exp $ */
+/* $OpenBSD: mailaddr.c,v 1.4 2020/09/22 18:04:27 martijn Exp $ */
/*
* Copyright (c) 2015 Gilles Chehade <gilles@poolp.org>
@@ -82,12 +82,10 @@ int
mailaddr_line(struct maddrmap *maddrmap, const char *s)
{
struct maddrnode mn;
- char buffer[LINE_MAX];
- char *p, *subrcpt;
+ char *p, *subrcpt, *buffer;
int ret;
- memset(buffer, 0, sizeof buffer);
- if (strlcpy(buffer, s, sizeof buffer) >= sizeof buffer)
+ if ((buffer = strdup(s)) == NULL)
return 0;
p = buffer;
@@ -95,12 +93,16 @@ mailaddr_line(struct maddrmap *maddrmap, const char *s)
subrcpt = strip(subrcpt);
if (subrcpt[0] == '\0')
continue;
- if (!text_to_mailaddr(&mn.mailaddr, subrcpt))
+ if (!text_to_mailaddr(&mn.mailaddr, subrcpt)) {
+ free(buffer);
return 0;
+ }
log_debug("subrcpt: [%s]", subrcpt);
maddrmap_insert(maddrmap, &mn);
}
+ free(buffer);
+
if (ret >= 0)
return 1;
/* expand_line_split() returned < 0 */
diff --git a/smtpd/makemap.8 b/usr.sbin/smtpd/makemap.8
index 674bef6f..674bef6f 100644
--- a/smtpd/makemap.8
+++ b/usr.sbin/smtpd/makemap.8
diff --git a/smtpd/makemap.c b/usr.sbin/smtpd/makemap.c
index 274833e9..10e3f555 100644
--- a/smtpd/makemap.c
+++ b/usr.sbin/smtpd/makemap.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: makemap.c,v 1.72 2018/12/28 11:40:29 eric Exp $ */
+/* $OpenBSD: makemap.c,v 1.73 2020/02/24 16:16:07 millert Exp $ */
/*
* Copyright (c) 2008 Gilles Chehade <gilles@poolp.org>
@@ -69,7 +69,6 @@ static int make_aliases(DBT *, char *);
static char *conf_aliases(char *);
static int dump_db(const char *, DBTYPE);
-struct smtpd *env;
char *source;
static int mode;
@@ -99,8 +98,13 @@ makemap(int prog_mode, int argc, char *argv[])
int ch, dbputs = 0, Uflag = 0;
DBTYPE dbtype = DB_HASH;
char *p;
+ gid_t gid;
int fd = -1;
+ gid = getgid();
+ if (setresgid(gid, gid, gid) == -1)
+ err(1, "setresgid");
+
if ((env = config_default()) == NULL)
err(1, NULL);
@@ -177,9 +181,9 @@ makemap(int prog_mode, int argc, char *argv[])
errx(1, "database name too long");
}
- execlp("makemap", "makemap", "-d", argv[0], "-o", dbname, "-",
- (char *)NULL);
- err(1, "execlp");
+ execl(PATH_MAKEMAP, "makemap", "-d", argv[0], "-o", dbname,
+ "-", (char *)NULL);
+ err(1, "execl");
}
if (mode == P_NEWALIASES) {
diff --git a/smtpd/mda.c b/usr.sbin/smtpd/mda.c
index 5e8fec19..5e8fec19 100644
--- a/smtpd/mda.c
+++ b/usr.sbin/smtpd/mda.c
diff --git a/usr.sbin/smtpd/mda_mbox.c b/usr.sbin/smtpd/mda_mbox.c
new file mode 100644
index 00000000..8918e3ee
--- /dev/null
+++ b/usr.sbin/smtpd/mda_mbox.c
@@ -0,0 +1,94 @@
+/* $OpenBSD: mda_mbox.c,v 1.2 2020/02/03 15:41:22 gilles Exp $ */
+
+/*
+ * Copyright (c) 2018 Gilles Chehade <gilles@poolp.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "includes.h"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/queue.h>
+#include <sys/tree.h>
+#include <sys/socket.h>
+
+#include <err.h>
+#include <errno.h>
+#include <event.h>
+#include <fcntl.h>
+#include <imsg.h>
+#include <paths.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sysexits.h>
+#include <unistd.h>
+#include <limits.h>
+
+#include "smtpd.h"
+
+
+void
+mda_mbox(struct deliver *deliver)
+{
+ int ret;
+ char sender[LINE_MAX];
+ char *envp[] = {
+ "HOME=/",
+ "PATH=" _PATH_DEFPATH,
+ "LOGNAME=root",
+ "USER=root",
+ NULL,
+ };
+
+ if (deliver->sender.user[0] == '\0' &&
+ deliver->sender.domain[0] == '\0')
+ ret = snprintf(sender, sizeof sender, "MAILER-DAEMON");
+ else
+ ret = snprintf(sender, sizeof sender, "%s@%s",
+ deliver->sender.user, deliver->sender.domain);
+ if (ret < 0 || (size_t)ret >= sizeof sender)
+ errx(EX_TEMPFAIL, "sender address too long");
+
+ execle(PATH_MAILLOCAL, PATH_MAILLOCAL, "-f",
+ sender, deliver->userinfo.username, (char *)NULL, envp);
+ perror("execl");
+ _exit(EX_TEMPFAIL);
+}
+
+void
+mda_mbox_init(struct deliver *deliver)
+{
+ int fd;
+ int ret;
+ char buffer[LINE_MAX];
+
+ ret = snprintf(buffer, sizeof buffer, "%s/%s",
+ _PATH_MAILDIR, deliver->userinfo.username);
+ if (ret < 0 || (size_t)ret >= sizeof buffer)
+ errx(EX_TEMPFAIL, "mailbox pathname too long");
+
+ if ((fd = open(buffer, O_CREAT|O_EXCL, 0)) == -1) {
+ if (errno == EEXIST)
+ return;
+ err(EX_TEMPFAIL, "open");
+ }
+
+ if (fchown(fd, deliver->userinfo.uid, deliver->userinfo.gid) == -1)
+ err(EX_TEMPFAIL, "fchown");
+
+ if (fchmod(fd, S_IRUSR|S_IWUSR) == -1)
+ err(EX_TEMPFAIL, "fchown");
+}
diff --git a/smtpd/mda_unpriv.c b/usr.sbin/smtpd/mda_unpriv.c
index ee54e101..2143b9a0 100644
--- a/smtpd/mda_unpriv.c
+++ b/usr.sbin/smtpd/mda_unpriv.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: mda_unpriv.c,v 1.5 2018/12/27 15:41:50 gilles Exp $ */
+/* $OpenBSD: mda_unpriv.c,v 1.6 2020/02/02 22:13:48 gilles Exp $ */
/*
* Copyright (c) 2018 Gilles Chehade <gilles@poolp.org>
@@ -42,7 +42,7 @@ mda_unpriv(struct dispatcher *dsp, struct deliver *deliver,
const char *pw_name, const char *pw_dir)
{
int idx;
- char *mda_environ[10];
+ char *mda_environ[11];
char mda_exec[LINE_MAX];
char mda_wrapper[LINE_MAX];
const char *mda_command;
@@ -74,6 +74,12 @@ mda_unpriv(struct dispatcher *dsp, struct deliver *deliver,
xasprintf(&mda_environ[idx++], "LOGNAME=%s", pw_name);
xasprintf(&mda_environ[idx++], "USER=%s", pw_name);
+ if (deliver->sender.user[0])
+ xasprintf(&mda_environ[idx++], "SENDER=%s@%s",
+ deliver->sender.user, deliver->sender.domain);
+ else
+ xasprintf(&mda_environ[idx++], "SENDER=");
+
if (deliver->mda_subaddress[0])
xasprintf(&mda_environ[idx++], "EXTENSION=%s", deliver->mda_subaddress);
diff --git a/smtpd/mda_variables.c b/usr.sbin/smtpd/mda_variables.c
index b672e492..b672e492 100644
--- a/smtpd/mda_variables.c
+++ b/usr.sbin/smtpd/mda_variables.c
diff --git a/smtpd/mproc.c b/usr.sbin/smtpd/mproc.c
index 213996df..dac38af2 100644
--- a/smtpd/mproc.c
+++ b/usr.sbin/smtpd/mproc.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: mproc.c,v 1.35 2019/10/03 05:50:28 gilles Exp $ */
+/* $OpenBSD: mproc.c,v 1.36 2020/03/17 09:01:53 tobhe Exp $ */
/*
* Copyright (c) 2012 Eric Faurot <eric@faurot.net>
@@ -90,7 +90,8 @@ mproc_clear(struct mproc *p)
{
log_debug("debug: clearing p=%s, fd=%d, pid=%d", p->name, p->imsgbuf.fd, p->pid);
- event_del(&p->ev);
+ if (p->events)
+ event_del(&p->ev);
close(p->imsgbuf.fd);
imsg_clear(&p->imsgbuf);
}
@@ -637,6 +638,8 @@ m_get_envelope(struct msg *m, struct envelope *evp)
m_get_evpid(m, &evpid);
m_get_string(m, &buf);
+ if (buf == NULL)
+ fatalx("empty envelope buffer");
if (!envelope_load_buffer(evp, buf, strlen(buf)))
fatalx("failed to retrieve envelope");
diff --git a/smtpd/mta.c b/usr.sbin/smtpd/mta.c
index 0908db25..922170ae 100644
--- a/smtpd/mta.c
+++ b/usr.sbin/smtpd/mta.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: mta.c,v 1.232 2019/09/20 17:46:05 gilles Exp $ */
+/* $OpenBSD: mta.c,v 1.234 2019/12/21 10:34:07 gilles Exp $ */
/*
* Copyright (c) 2008 Pierre-Yves Ritschard <pyr@openbsd.org>
@@ -929,6 +929,10 @@ mta_query_smarthost(struct envelope *evp0)
m_create(p_lka, IMSG_MTA_LOOKUP_SMARTHOST, 0, 0, -1);
m_add_id(p_lka, evp->id);
+ if (dispatcher->u.remote.smarthost_domain)
+ m_add_string(p_lka, evp->dest.domain);
+ else
+ m_add_string(p_lka, NULL);
m_add_string(p_lka, dispatcher->u.remote.smarthost);
m_close(p_lka);
@@ -1767,6 +1771,7 @@ mta_relay(struct envelope *e, struct relayhost *relayh)
r = xcalloc(1, sizeof *r);
TAILQ_INIT(&r->tasks);
r->id = generate_uid();
+ r->dispatcher = dispatcher;
r->tls = key.tls;
r->flags = key.flags;
r->domain = key.domain;
diff --git a/smtpd/mta_session.c b/usr.sbin/smtpd/mta_session.c
index fa5be18f..ad1c3c84 100644
--- a/smtpd/mta_session.c
+++ b/usr.sbin/smtpd/mta_session.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: mta_session.c,v 1.122 2019/09/20 17:46:05 gilles Exp $ */
+/* $OpenBSD: mta_session.c,v 1.136 2020/05/21 15:38:05 millert Exp $ */
/*
* Copyright (c) 2008 Pierre-Yves Ritschard <pyr@openbsd.org>
@@ -107,6 +107,8 @@ struct mta_session {
char *helo;
char *mxname;
+ char *username;
+
int flags;
int attempt;
@@ -130,6 +132,7 @@ struct mta_session {
struct mta_task *task;
struct mta_envelope *currevp;
FILE *datafp;
+ size_t datalen;
size_t failures;
@@ -165,6 +168,36 @@ void mta_hoststat_reschedule(const char *);
void mta_hoststat_cache(const char *, uint64_t);
void mta_hoststat_uncache(const char *, uint64_t);
+
+static void mta_filter_begin(struct mta_session *);
+static void mta_filter_end(struct mta_session *);
+static void mta_connected(struct mta_session *);
+static void mta_disconnected(struct mta_session *);
+
+static void mta_report_link_connect(struct mta_session *, const char *, int,
+ const struct sockaddr_storage *,
+ const struct sockaddr_storage *);
+static void mta_report_link_greeting(struct mta_session *, const char *);
+static void mta_report_link_identify(struct mta_session *, const char *, const char *);
+static void mta_report_link_tls(struct mta_session *, const char *);
+static void mta_report_link_disconnect(struct mta_session *);
+static void mta_report_link_auth(struct mta_session *, const char *, const char *);
+static void mta_report_tx_reset(struct mta_session *, uint32_t);
+static void mta_report_tx_begin(struct mta_session *, uint32_t);
+static void mta_report_tx_mail(struct mta_session *, uint32_t, const char *, int);
+static void mta_report_tx_rcpt(struct mta_session *, uint32_t, const char *, int);
+static void mta_report_tx_envelope(struct mta_session *, uint32_t, uint64_t);
+static void mta_report_tx_data(struct mta_session *, uint32_t, int);
+static void mta_report_tx_commit(struct mta_session *, uint32_t, size_t);
+static void mta_report_tx_rollback(struct mta_session *, uint32_t);
+static void mta_report_protocol_client(struct mta_session *, const char *);
+static void mta_report_protocol_server(struct mta_session *, const char *);
+#if 0
+static void mta_report_filter_response(struct mta_session *, int, int, const char *);
+#endif
+static void mta_report_timeout(struct mta_session *);
+
+
static struct tree wait_helo;
static struct tree wait_ptr;
static struct tree wait_fd;
@@ -173,6 +206,9 @@ static struct tree wait_tls_verify;
static struct runq *hangon;
+#define SESSION_FILTERED(s) \
+ ((s)->relay->dispatcher->u.remote.filtername)
+
static void
mta_session_init(void)
{
@@ -203,6 +239,8 @@ mta_session(struct mta_relay *relay, struct mta_route *route, const char *mxname
s->route = route;
s->mxname = xstrdup(mxname);
+ mta_filter_begin(s);
+
if (relay->flags & RELAY_LMTP)
s->flags |= MTA_LMTP;
switch (relay->tls) {
@@ -351,6 +389,8 @@ mta_free(struct mta_session *s)
log_debug("debug: mta: %p: session done", s);
+ mta_disconnected(s);
+
if (s->ready)
s->relay->nconn_ready -= 1;
@@ -364,12 +404,15 @@ mta_free(struct mta_session *s)
if (s->task)
fatalx("current task should have been deleted already");
- if (s->datafp)
+ if (s->datafp) {
fclose(s->datafp);
+ s->datalen = 0;
+ }
free(s->helo);
relay = s->relay;
route = s->route;
+ free(s->username);
free(s->mxname);
free(s);
stat_decrement("mta.session", 1);
@@ -555,16 +598,19 @@ again:
case MTA_EHLO:
s->ext = 0;
mta_send(s, "EHLO %s", s->helo);
+ mta_report_link_identify(s, "EHLO", s->helo);
break;
case MTA_HELO:
s->ext = 0;
mta_send(s, "HELO %s", s->helo);
+ mta_report_link_identify(s, "HELO", s->helo);
break;
case MTA_LHLO:
s->ext = 0;
mta_send(s, "LHLO %s", s->helo);
+ mta_report_link_identify(s, "LHLO", s->helo);
break;
case MTA_STARTTLS:
@@ -615,6 +661,14 @@ again:
break;
case MTA_AUTH_PLAIN:
+ memset(ibuf, 0, sizeof ibuf);
+ if (base64_decode(s->relay->secret, (unsigned char *)ibuf,
+ sizeof(ibuf)-1) == -1) {
+ log_debug("debug: mta: %p: credentials too large on session", s);
+ mta_error(s, "Credentials too large");
+ break;
+ }
+ s->username = xstrdup(ibuf+1);
mta_send(s, "AUTH PLAIN %s", s->relay->secret);
break;
@@ -630,6 +684,7 @@ again:
mta_error(s, "Credentials too large");
break;
}
+ s->username = xstrdup(ibuf+1);
memset(obuf, 0, sizeof obuf);
base64_encode((unsigned char *)ibuf + 1, strlen(ibuf + 1), obuf, sizeof obuf);
@@ -771,11 +826,12 @@ again:
e->dest,
e->dsn_notify ? " NOTIFY=" : "",
e->dsn_notify ? dsn_strnotify(e->dsn_notify) : "",
- e->dsn_orcpt ? " ORCPT=" : "",
+ e->dsn_orcpt ? " ORCPT=rfc822;" : "",
e->dsn_orcpt ? e->dsn_orcpt : "");
} else
mta_send(s, "RCPT TO:<%s>", e->dest);
+ mta_report_tx_envelope(s, s->task->msgid, e->id);
s->rcptcount++;
break;
@@ -816,6 +872,7 @@ again:
if (s->datafp) {
fclose(s->datafp);
s->datafp = NULL;
+ s->datalen = 0;
}
mta_send(s, "RSET");
break;
@@ -840,6 +897,7 @@ mta_response(struct mta_session *s, char *line)
struct sockaddr_storage ss;
struct sockaddr *sa;
const char *domain;
+ char *pbuf;
socklen_t sa_len;
char buf[LINE_MAX];
int delivery;
@@ -852,6 +910,16 @@ mta_response(struct mta_session *s, char *line)
s->flags |= MTA_FREE;
return;
}
+
+ pbuf = "";
+ if (strlen(line) > 4) {
+ (void)strlcpy(buf, line + 4, sizeof buf);
+ if ((pbuf = strchr(buf, ' ')))
+ *pbuf = '\0';
+ pbuf = valid_domainpart(buf) ? buf : "";
+ }
+ mta_report_link_greeting(s, pbuf);
+
if (s->flags & MTA_LMTP)
mta_enter_state(s, MTA_LHLO);
else
@@ -912,15 +980,18 @@ mta_response(struct mta_session *s, char *line)
case MTA_AUTH_PLAIN:
if (line[0] != '2') {
mta_error(s, "AUTH rejected: %s", line);
+ mta_report_link_auth(s, s->username, "fail");
s->flags |= MTA_FREE;
return;
}
+ mta_report_link_auth(s, s->username, "pass");
mta_enter_state(s, MTA_READY);
break;
case MTA_AUTH_LOGIN:
if (strncmp(line, "334 ", 4) != 0) {
mta_error(s, "AUTH rejected: %s", line);
+ mta_report_link_auth(s, s->username, "fail");
s->flags |= MTA_FREE;
return;
}
@@ -930,6 +1001,7 @@ mta_response(struct mta_session *s, char *line)
case MTA_AUTH_LOGIN_USER:
if (strncmp(line, "334 ", 4) != 0) {
mta_error(s, "AUTH rejected: %s", line);
+ mta_report_link_auth(s, s->username, "fail");
s->flags |= MTA_FREE;
return;
}
@@ -939,9 +1011,11 @@ mta_response(struct mta_session *s, char *line)
case MTA_AUTH_LOGIN_PASS:
if (line[0] != '2') {
mta_error(s, "AUTH rejected: %s", line);
+ mta_report_link_auth(s, s->username, "fail");
s->flags |= MTA_FREE;
return;
}
+ mta_report_link_auth(s, s->username, "pass");
mta_enter_state(s, MTA_READY);
break;
@@ -951,10 +1025,13 @@ mta_response(struct mta_session *s, char *line)
delivery = IMSG_MTA_DELIVERY_PERMFAIL;
else
delivery = IMSG_MTA_DELIVERY_TEMPFAIL;
+
mta_flush_task(s, delivery, line, 0, 0);
mta_enter_state(s, MTA_RSET);
return;
}
+ mta_report_tx_begin(s, s->task->msgid);
+ mta_report_tx_mail(s, s->task->msgid, s->task->sender, 1);
mta_enter_state(s, MTA_RCPT);
break;
@@ -978,6 +1055,8 @@ mta_response(struct mta_session *s, char *line)
mta_hoststat_reschedule(domain);
}
else {
+ mta_report_tx_rollback(s, s->task->msgid);
+ mta_report_tx_reset(s, s->task->msgid);
if (line[0] == '5')
delivery = IMSG_MTA_DELIVERY_PERMFAIL;
else
@@ -1029,6 +1108,21 @@ mta_response(struct mta_session *s, char *line)
}
}
+ switch (line[0]) {
+ case '2':
+ mta_report_tx_rcpt(s,
+ s->task->msgid, e->dest, 1);
+ break;
+ case '4':
+ mta_report_tx_rcpt(s,
+ s->task->msgid, e->dest, -1);
+ break;
+ case '5':
+ mta_report_tx_rcpt(s,
+ s->task->msgid, e->dest, 0);
+ break;
+ }
+
if (s->currevp == NULL)
mta_enter_state(s, MTA_DATA);
else
@@ -1037,13 +1131,19 @@ mta_response(struct mta_session *s, char *line)
case MTA_DATA:
if (line[0] == '2' || line[0] == '3') {
+ mta_report_tx_data(s, s->task->msgid, 1);
mta_enter_state(s, MTA_BODY);
break;
}
+
if (line[0] == '5')
delivery = IMSG_MTA_DELIVERY_PERMFAIL;
else
delivery = IMSG_MTA_DELIVERY_TEMPFAIL;
+ mta_report_tx_data(s, s->task->msgid,
+ delivery == IMSG_MTA_DELIVERY_TEMPFAIL ? -1 : 0);
+ mta_report_tx_rollback(s, s->task->msgid);
+ mta_report_tx_reset(s, s->task->msgid);
mta_flush_task(s, delivery, line, 0, 0);
mta_enter_state(s, MTA_RSET);
break;
@@ -1059,6 +1159,14 @@ mta_response(struct mta_session *s, char *line)
delivery = IMSG_MTA_DELIVERY_PERMFAIL;
else
delivery = IMSG_MTA_DELIVERY_TEMPFAIL;
+ if (delivery != IMSG_MTA_DELIVERY_OK) {
+ mta_report_tx_rollback(s, s->task->msgid);
+ mta_report_tx_reset(s, s->task->msgid);
+ }
+ else {
+ mta_report_tx_commit(s, s->task->msgid, s->datalen);
+ mta_report_tx_reset(s, s->task->msgid);
+ }
mta_flush_task(s, delivery, line, (s->flags & MTA_LMTP) ? 1 : 0, 0);
if (s->task) {
s->rcptcount--;
@@ -1080,6 +1188,11 @@ mta_response(struct mta_session *s, char *line)
case MTA_RSET:
s->rcptcount = 0;
+
+ if (s->task) {
+ mta_report_tx_rollback(s, s->task->msgid);
+ mta_report_tx_reset(s, s->task->msgid);
+ }
if (s->relay->limits->sessdelay_transaction) {
log_debug("debug: mta: waiting for %llds after reset",
(long long int)s->relay->limits->sessdelay_transaction);
@@ -1112,7 +1225,7 @@ mta_io(struct io *io, int evt, void *arg)
switch (evt) {
case IO_CONNECTED:
- log_info("%016"PRIx64" mta connected", s->id);
+ mta_connected(s);
if (s->use_smtps) {
io_set_write(io);
@@ -1129,6 +1242,9 @@ mta_io(struct io *io, int evt, void *arg)
s->id, ssl_to_text(io_tls(s->io)));
s->flags |= MTA_TLS;
+ mta_report_link_tls(s,
+ ssl_to_text(io_tls(s->io)));
+
mta_cert_verify(s);
break;
@@ -1143,7 +1259,12 @@ mta_io(struct io *io, int evt, void *arg)
return;
}
+ /* Strip trailing '\r' */
+ if (len && line[len - 1] == '\r')
+ line[--len] = '\0';
+
log_trace(TRACE_MTA, "mta: %p: <<< %s", s, line);
+ mta_report_protocol_server(s, line);
if ((error = parse_smtp_response(line, len, &msg, &cont))) {
mta_error(s, "Bad response: %s", error);
@@ -1179,14 +1300,13 @@ mta_io(struct io *io, int evt, void *arg)
if (cont) {
if (s->replybuf[0] == '\0')
(void)strlcat(s->replybuf, line, sizeof s->replybuf);
- else {
- line = line + 4;
- if (isdigit((int)*line) && *(line + 1) == '.' &&
- isdigit((int)*line+2) && *(line + 3) == '.' &&
- isdigit((int)*line+4) && isspace((int)*(line + 5)))
- (void)strlcat(s->replybuf, line+5, sizeof s->replybuf);
- else
- (void)strlcat(s->replybuf, line, sizeof s->replybuf);
+ else if (len > 4) {
+ p = line + 4;
+ if (isdigit((unsigned char)p[0]) && p[1] == '.' &&
+ isdigit((unsigned char)p[2]) && p[3] == '.' &&
+ isdigit((unsigned char)p[4]) && isspace((unsigned char)p[5]))
+ p += 5;
+ (void)strlcat(s->replybuf, p, sizeof s->replybuf);
}
goto nextline;
}
@@ -1194,17 +1314,17 @@ mta_io(struct io *io, int evt, void *arg)
/* last line of a reply, check if we're on a continuation to parse out status and ESC.
* if we overflow reply buffer or are not on continuation, log entire last line.
*/
- if (s->replybuf[0] != '\0') {
+ if (s->replybuf[0] == '\0')
+ (void)strlcat(s->replybuf, line, sizeof s->replybuf);
+ else if (len > 4) {
p = line + 4;
- if (isdigit((int)*p) && *(p + 1) == '.' &&
- isdigit((int)*p+2) && *(p + 3) == '.' &&
- isdigit((int)*p+4) && isspace((int)*(p + 5)))
+ if (isdigit((unsigned char)p[0]) && p[1] == '.' &&
+ isdigit((unsigned char)p[2]) && p[3] == '.' &&
+ isdigit((unsigned char)p[4]) && isspace((unsigned char)p[5]))
p += 5;
if (strlcat(s->replybuf, p, sizeof s->replybuf) >= sizeof s->replybuf)
(void)strlcpy(s->replybuf, line, sizeof s->replybuf);
}
- else
- (void)strlcpy(s->replybuf, line, sizeof s->replybuf);
if (s->state == MTA_QUIT) {
log_info("%016"PRIx64" mta disconnected reason=quit messages=%zu",
@@ -1250,6 +1370,7 @@ mta_io(struct io *io, int evt, void *arg)
case IO_TIMEOUT:
log_debug("debug: mta: %p: connection timeout", s);
mta_error(s, "Connection timeout");
+ mta_report_timeout(s);
if (!s->ready)
mta_connect(s);
else
@@ -1257,40 +1378,20 @@ mta_io(struct io *io, int evt, void *arg)
break;
case IO_ERROR:
+ case IO_TLSERROR:
log_debug("debug: mta: %p: IO error: %s", s, io_error(io));
- if (!s->ready) {
- mta_error(s, "IO Error: %s", io_error(io));
- mta_connect(s);
- break;
- }
- else if (!(s->flags & (MTA_FORCE_TLS|MTA_FORCE_SMTPS|MTA_FORCE_ANYSSL))) {
- /* error in non-strict SSL negotiation, downgrade to plain */
- if (s->flags & MTA_TLS) {
- log_info("smtp-out: Error on session %016"PRIx64
- ": opportunistic TLS failed, "
- "downgrading to plain", s->id);
- s->flags &= ~MTA_TLS;
- s->flags |= MTA_DOWNGRADE_PLAIN;
- mta_connect(s);
- break;
- }
- }
- mta_error(s, "IO Error: %s", io_error(io));
- mta_free(s);
- break;
- case IO_TLSERROR:
- log_debug("debug: mta: %p: TLS IO error: %s", s, io_error(io));
- if (!(s->flags & (MTA_FORCE_TLS|MTA_FORCE_SMTPS|MTA_FORCE_ANYSSL))) {
+ if (s->state == MTA_STARTTLS && s->use_smtp_tls) {
/* error in non-strict SSL negotiation, downgrade to plain */
- log_info("smtp-out: TLS Error on session %016"PRIx64
- ": TLS failed, "
+ log_info("smtp-out: Error on session %016"PRIx64
+ ": opportunistic TLS failed, "
"downgrading to plain", s->id);
s->flags &= ~MTA_TLS;
s->flags |= MTA_DOWNGRADE_PLAIN;
mta_connect(s);
break;
}
+
mta_error(s, "IO Error: %s", io_error(io));
mta_free(s);
break;
@@ -1324,6 +1425,13 @@ mta_send(struct mta_session *s, char *fmt, ...)
log_trace(TRACE_MTA, "mta: %p: >>> %s", s, p);
+ if (strncasecmp(p, "AUTH PLAIN ", 11) == 0)
+ mta_report_protocol_client(s, "AUTH PLAIN ********");
+ else if (s->state == MTA_AUTH_LOGIN_USER || s->state == MTA_AUTH_LOGIN_PASS)
+ mta_report_protocol_client(s, "********");
+ else
+ mta_report_protocol_client(s, p);
+
io_xprintf(s->io, "%s\r\n", p);
free(p);
@@ -1346,7 +1454,7 @@ mta_queue_data(struct mta_session *s)
break;
if (ln[len - 1] == '\n')
ln[len - 1] = '\0';
- io_xprintf(s->io, "%s%s\r\n", *ln == '.' ? "." : "", ln);
+ s->datalen += io_xprintf(s->io, "%s%s\r\n", *ln == '.' ? "." : "", ln);
}
free(ln);
@@ -1562,8 +1670,12 @@ mta_cert_verify_cb(void *arg, int status)
match = 0;
(void)ssl_check_name(cert, s->mxname, &match);
X509_free(cert);
- if (!match)
+ if (!match) {
+ log_info("%016"PRIx64" mta "
+ "ssl_check_name: no match for '%s' in cert",
+ s->id, s->mxname);
status = CERT_INVALID;
+ }
}
}
@@ -1672,3 +1784,225 @@ mta_strstate(int state)
return "MTA_???";
}
}
+
+static void
+mta_filter_begin(struct mta_session *s)
+{
+ if (!SESSION_FILTERED(s))
+ return;
+
+ m_create(p_lka, IMSG_FILTER_SMTP_BEGIN, 0, 0, -1);
+ m_add_id(p_lka, s->id);
+ m_add_string(p_lka, s->relay->dispatcher->u.remote.filtername);
+ m_close(p_lka);
+}
+
+static void
+mta_filter_end(struct mta_session *s)
+{
+ if (!SESSION_FILTERED(s))
+ return;
+
+ m_create(p_lka, IMSG_FILTER_SMTP_END, 0, 0, -1);
+ m_add_id(p_lka, s->id);
+ m_close(p_lka);
+}
+
+static void
+mta_connected(struct mta_session *s)
+{
+ struct sockaddr_storage sa_src;
+ struct sockaddr_storage sa_dest;
+ int sa_len;
+
+ log_info("%016"PRIx64" mta connected", s->id);
+
+ sa_len = sizeof sa_src;
+ if (getsockname(io_fileno(s->io),
+ (struct sockaddr *)&sa_src, &sa_len) == -1)
+ bzero(&sa_src, sizeof sa_src);
+ sa_len = sizeof sa_dest;
+ if (getpeername(io_fileno(s->io),
+ (struct sockaddr *)&sa_dest, &sa_len) == -1)
+ bzero(&sa_dest, sizeof sa_dest);
+
+ mta_report_link_connect(s,
+ s->route->dst->ptrname, 1,
+ &sa_src,
+ &sa_dest);
+}
+
+static void
+mta_disconnected(struct mta_session *s)
+{
+ mta_report_link_disconnect(s);
+ mta_filter_end(s);
+}
+
+
+static void
+mta_report_link_connect(struct mta_session *s, const char *rdns, int fcrdns,
+ const struct sockaddr_storage *ss_src,
+ const struct sockaddr_storage *ss_dest)
+{
+ if (! SESSION_FILTERED(s))
+ return;
+
+ report_smtp_link_connect("smtp-out", s->id, rdns, fcrdns, ss_src, ss_dest);
+}
+
+static void
+mta_report_link_greeting(struct mta_session *s,
+ const char *domain)
+{
+ if (! SESSION_FILTERED(s))
+ return;
+
+ report_smtp_link_greeting("smtp-out", s->id, domain);
+}
+
+static void
+mta_report_link_identify(struct mta_session *s, const char *method, const char *identity)
+{
+ if (! SESSION_FILTERED(s))
+ return;
+
+ report_smtp_link_identify("smtp-out", s->id, method, identity);
+}
+
+static void
+mta_report_link_tls(struct mta_session *s, const char *ssl)
+{
+ if (! SESSION_FILTERED(s))
+ return;
+
+ report_smtp_link_tls("smtp-out", s->id, ssl);
+}
+
+static void
+mta_report_link_disconnect(struct mta_session *s)
+{
+ if (! SESSION_FILTERED(s))
+ return;
+
+ report_smtp_link_disconnect("smtp-out", s->id);
+}
+
+static void
+mta_report_link_auth(struct mta_session *s, const char *user, const char *result)
+{
+ if (! SESSION_FILTERED(s))
+ return;
+
+ report_smtp_link_auth("smtp-out", s->id, user, result);
+}
+
+static void
+mta_report_tx_reset(struct mta_session *s, uint32_t msgid)
+{
+ if (! SESSION_FILTERED(s))
+ return;
+
+ report_smtp_tx_reset("smtp-out", s->id, msgid);
+}
+
+static void
+mta_report_tx_begin(struct mta_session *s, uint32_t msgid)
+{
+ if (! SESSION_FILTERED(s))
+ return;
+
+ report_smtp_tx_begin("smtp-out", s->id, msgid);
+}
+
+static void
+mta_report_tx_mail(struct mta_session *s, uint32_t msgid, const char *address, int ok)
+{
+ if (! SESSION_FILTERED(s))
+ return;
+
+ report_smtp_tx_mail("smtp-out", s->id, msgid, address, ok);
+}
+
+static void
+mta_report_tx_rcpt(struct mta_session *s, uint32_t msgid, const char *address, int ok)
+{
+ if (! SESSION_FILTERED(s))
+ return;
+
+ report_smtp_tx_rcpt("smtp-out", s->id, msgid, address, ok);
+}
+
+static void
+mta_report_tx_envelope(struct mta_session *s, uint32_t msgid, uint64_t evpid)
+{
+ if (! SESSION_FILTERED(s))
+ return;
+
+ report_smtp_tx_envelope("smtp-out", s->id, msgid, evpid);
+}
+
+static void
+mta_report_tx_data(struct mta_session *s, uint32_t msgid, int ok)
+{
+ if (! SESSION_FILTERED(s))
+ return;
+
+ report_smtp_tx_data("smtp-out", s->id, msgid, ok);
+}
+
+static void
+mta_report_tx_commit(struct mta_session *s, uint32_t msgid, size_t msgsz)
+{
+ if (! SESSION_FILTERED(s))
+ return;
+
+ report_smtp_tx_commit("smtp-out", s->id, msgid, msgsz);
+}
+
+static void
+mta_report_tx_rollback(struct mta_session *s, uint32_t msgid)
+{
+ if (! SESSION_FILTERED(s))
+ return;
+
+ report_smtp_tx_rollback("smtp-out", s->id, msgid);
+}
+
+static void
+mta_report_protocol_client(struct mta_session *s, const char *command)
+{
+ if (! SESSION_FILTERED(s))
+ return;
+
+ report_smtp_protocol_client("smtp-out", s->id, command);
+}
+
+static void
+mta_report_protocol_server(struct mta_session *s, const char *response)
+{
+ if (! SESSION_FILTERED(s))
+ return;
+
+ report_smtp_protocol_server("smtp-out", s->id, response);
+}
+
+#if 0
+static void
+mta_report_filter_response(struct mta_session *s, int phase, int response, const char *param)
+{
+ if (! SESSION_FILTERED(s))
+ return;
+
+ report_smtp_filter_response("smtp-out", s->id, phase, response, param);
+}
+#endif
+
+static void
+mta_report_timeout(struct mta_session *s)
+{
+ if (! SESSION_FILTERED(s))
+ return;
+
+ report_smtp_timeout("smtp-out", s->id);
+}
diff --git a/smtpd/newaliases.8 b/usr.sbin/smtpd/newaliases.8
index b82e4515..b82e4515 100644
--- a/smtpd/newaliases.8
+++ b/usr.sbin/smtpd/newaliases.8
diff --git a/smtpd/parse.y b/usr.sbin/smtpd/parse.y
index e7ee2c8c..a82f8206 100644
--- a/smtpd/parse.y
+++ b/usr.sbin/smtpd/parse.y
@@ -1,4 +1,4 @@
-/* $OpenBSD: parse.y,v 1.263 2019/09/22 11:49:53 semarie Exp $ */
+/* $OpenBSD: parse.y,v 1.281 2020/09/23 19:11:50 martijn Exp $ */
/*
* Copyright (c) 2008 Gilles Chehade <gilles@poolp.org>
@@ -111,7 +111,7 @@ static struct ca *sca;
struct dispatcher *dispatcher;
struct rule *rule;
-struct processor *processor;
+struct filter_proc *processor;
struct filter_config *filter_config;
static uint32_t last_dynchain_id = 1;
@@ -179,8 +179,8 @@ typedef struct {
%}
-%token ACTION ALIAS ANY ARROW AUTH AUTH_OPTIONAL
-%token BACKUP BOUNCE
+%token ACTION ADMD ALIAS ANY ARROW AUTH AUTH_OPTIONAL
+%token BACKUP BOUNCE BYPASS
%token CA CERT CHAIN CHROOT CIPHERS COMMIT COMPRESSION CONNECT
%token DATA DATA_LINE DHE DISCONNECT DOMAIN
%token EHLO ENABLE ENCRYPTION ERROR EXPAND_ONLY
@@ -215,6 +215,7 @@ grammar : /* empty */
| grammar include '\n'
| grammar varset '\n'
| grammar bounce '\n'
+ | grammar admd '\n'
| grammar ca '\n'
| grammar mda '\n'
| grammar mta '\n'
@@ -316,6 +317,21 @@ BOUNCE WARN_INTERVAL {
;
+admd:
+ADMD STRING {
+ size_t i;
+
+ for (i = 0; $2[i] != '\0'; i++) {
+ if (!isprint($2[i])) {
+ yyerror("not a valid admd");
+ free($2);
+ YYERROR;
+ }
+ }
+ conf->sc_admd = $2;
+};
+
+
ca:
CA STRING {
char buf[HOST_NAME_MAX+1];
@@ -440,7 +456,7 @@ pki_params_opt pki_params
proc:
PROC STRING STRING {
- if (dict_get(conf->sc_processors_dict, $2)) {
+ if (dict_get(conf->sc_filter_processes_dict, $2)) {
yyerror("processor already exists with that name: %s", $2);
free($2);
free($3);
@@ -449,7 +465,7 @@ PROC STRING STRING {
processor = xcalloc(1, sizeof *processor);
processor->command = $3;
} proc_params {
- dict_set(conf->sc_processors_dict, $2, processor);
+ dict_set(conf->sc_filter_processes_dict, $2, processor);
processor = NULL;
}
;
@@ -535,7 +551,7 @@ SMTP LIMIT limits_smtp
free($3);
YYERROR;
}
- if (isspace((int)*$3) || !isprint((int)*$3) || *$3== '@') {
+ if (isspace((unsigned char)*$3) || !isprint((unsigned char)*$3) || *$3 == '@') {
yyerror("sub-addr-delim uses invalid character");
free($3);
YYERROR;
@@ -572,7 +588,7 @@ SRS KEY STRING {
dispatcher_local_option:
USER STRING {
- if (dispatcher->u.local.requires_root) {
+ if (dispatcher->u.local.is_mbox) {
yyerror("user may not be specified for this dispatcher");
YYERROR;
}
@@ -668,9 +684,8 @@ dispatcher_local_option dispatcher_local_options
dispatcher_local:
MBOX {
- dispatcher->u.local.requires_root = 1;
- dispatcher->u.local.user = xstrdup("root");
- asprintf(&dispatcher->u.local.command, PATH_LIBEXEC"/mail.local -f %%{mbox.from} %%{user.username}");
+ dispatcher->u.local.is_mbox = 1;
+ asprintf(&dispatcher->u.local.command, PATH_LIBEXEC"/mail.local -f %%{mbox.from} -- %%{user.username}");
} dispatcher_local_options
| MAILDIR {
asprintf(&dispatcher->u.local.command, PATH_LIBEXEC"/mail.maildir");
@@ -696,11 +711,11 @@ MBOX {
} dispatcher_local_options
| LMTP STRING {
asprintf(&dispatcher->u.local.command,
- PATH_LIBEXEC"/mail.lmtp -f \"%%{sender}\" -d %s %%{user.username}", $2);
+ PATH_LIBEXEC"/mail.lmtp -d \"%s\" -u", $2);
} dispatcher_local_options
| LMTP STRING RCPT_TO {
asprintf(&dispatcher->u.local.command,
- PATH_LIBEXEC"/mail.lmtp -f \"%%{sender}\" -d %s %%{dest}", $2);
+ PATH_LIBEXEC"/mail.lmtp -d \"%s\" -r", $2);
} dispatcher_local_options
| MDA STRING {
asprintf(&dispatcher->u.local.command,
@@ -824,6 +839,27 @@ HELO STRING {
dispatcher->u.remote.smarthost = strdup(t->t_name);
}
+| DOMAIN tables {
+ struct table *t = $2;
+
+ if (dispatcher->u.remote.smarthost) {
+ yyerror("host mapping already specified for this dispatcher");
+ YYERROR;
+ }
+ if (dispatcher->u.remote.backup) {
+ yyerror("backup and domain are mutually exclusive");
+ YYERROR;
+ }
+
+ if (!table_check_use(t, T_DYNAMIC|T_HASH, K_RELAYHOST)) {
+ yyerror("table \"%s\" may not be used for host lookups",
+ t->t_name);
+ YYERROR;
+ }
+
+ dispatcher->u.remote.smarthost = strdup(t->t_name);
+ dispatcher->u.remote.smarthost_domain = 1;
+}
| TLS {
if (dispatcher->u.remote.tls_required == 1) {
yyerror("tls already specified for this dispatcher");
@@ -862,6 +898,45 @@ HELO STRING {
dispatcher->u.remote.auth = strdup(t->t_name);
}
+| FILTER STRING {
+ struct filter_config *fc;
+
+ if (dispatcher->u.remote.filtername) {
+ yyerror("filter already specified for this dispatcher");
+ YYERROR;
+ }
+
+ if ((fc = dict_get(conf->sc_filters_dict, $2)) == NULL) {
+ yyerror("no filter exist with that name: %s", $2);
+ free($2);
+ YYERROR;
+ }
+ fc->filter_subsystem |= FILTER_SUBSYSTEM_SMTP_OUT;
+ dispatcher->u.remote.filtername = $2;
+}
+| FILTER {
+ char buffer[128];
+ char *filtername;
+
+ if (dispatcher->u.remote.filtername) {
+ yyerror("filter already specified for this dispatcher");
+ YYERROR;
+ }
+
+ do {
+ (void)snprintf(buffer, sizeof buffer, "<dynchain:%08x>", last_dynchain_id++);
+ } while (dict_check(conf->sc_filters_dict, buffer));
+
+ filtername = xstrdup(buffer);
+ filter_config = xcalloc(1, sizeof *filter_config);
+ filter_config->filter_type = FILTER_TYPE_CHAIN;
+ filter_config->filter_subsystem |= FILTER_SUBSYSTEM_SMTP_OUT;
+ dict_init(&filter_config->chain_procs);
+ dispatcher->u.remote.filtername = filtername;
+} '{' filter_list '}' {
+ dict_set(conf->sc_filters_dict, dispatcher->u.remote.filtername, filter_config);
+ filter_config = NULL;
+}
| SRS {
if (conf->sc_srs_key == NULL) {
yyerror("an srs key is required for srs to be specified in an action");
@@ -1027,7 +1102,7 @@ negation TAG REGEX tables {
YYERROR;
}
- if (!table_check_use(t, T_DYNAMIC|T_LIST, K_CREDENTIALS)) {
+ if (!table_check_use(t, T_DYNAMIC|T_LIST, K_STRING|K_CREDENTIALS)) {
yyerror("table \"%s\" may not be used for auth lookups",
t->t_name);
YYERROR;
@@ -1234,6 +1309,101 @@ negation TAG REGEX tables {
rule->table_from = strdup(t->t_name);
}
+| negation FROM AUTH {
+ struct table *anyhost = table_find(conf, "<anyhost>");
+
+ if (rule->flag_from) {
+ yyerror("from already specified for this rule");
+ YYERROR;
+ }
+
+ rule->flag_from = 1;
+ rule->table_from = strdup(anyhost->t_name);
+ rule->flag_smtp_auth = $1 ? -1 : 1;
+}
+| negation FROM AUTH tables {
+ struct table *anyhost = table_find(conf, "<anyhost>");
+ struct table *t = $4;
+
+ if (rule->flag_from) {
+ yyerror("from already specified for this rule");
+ YYERROR;
+ }
+
+ if (!table_check_use(t, T_DYNAMIC|T_LIST, K_STRING|K_CREDENTIALS)) {
+ yyerror("table \"%s\" may not be used for from lookups",
+ t->t_name);
+ YYERROR;
+ }
+
+ rule->flag_from = 1;
+ rule->table_from = strdup(anyhost->t_name);
+ rule->flag_smtp_auth = $1 ? -1 : 1;
+ rule->table_smtp_auth = strdup(t->t_name);
+}
+| negation FROM AUTH REGEX tables {
+ struct table *anyhost = table_find(conf, "<anyhost>");
+ struct table *t = $5;
+
+ if (rule->flag_from) {
+ yyerror("from already specified for this rule");
+ YYERROR;
+ }
+
+ if (!table_check_use(t, T_DYNAMIC|T_LIST, K_REGEX)) {
+ yyerror("table \"%s\" may not be used for from lookups",
+ t->t_name);
+ YYERROR;
+ }
+
+ rule->flag_from = 1;
+ rule->table_from = strdup(anyhost->t_name);
+ rule->flag_smtp_auth = $1 ? -1 : 1;
+ rule->flag_smtp_auth_regex = 1;
+ rule->table_smtp_auth = strdup(t->t_name);
+}
+
+| negation FROM MAIL_FROM tables {
+ struct table *anyhost = table_find(conf, "<anyhost>");
+ struct table *t = $4;
+
+ if (rule->flag_from) {
+ yyerror("from already specified for this rule");
+ YYERROR;
+ }
+
+ if (!table_check_use(t, T_DYNAMIC|T_LIST, K_MAILADDR)) {
+ yyerror("table \"%s\" may not be used for from lookups",
+ t->t_name);
+ YYERROR;
+ }
+
+ rule->flag_from = 1;
+ rule->table_from = strdup(anyhost->t_name);
+ rule->flag_smtp_mail_from = $1 ? -1 : 1;
+ rule->table_smtp_mail_from = strdup(t->t_name);
+}
+| negation FROM MAIL_FROM REGEX tables {
+ struct table *anyhost = table_find(conf, "<anyhost>");
+ struct table *t = $5;
+
+ if (rule->flag_from) {
+ yyerror("from already specified for this rule");
+ YYERROR;
+ }
+
+ if (!table_check_use(t, T_DYNAMIC|T_LIST, K_REGEX)) {
+ yyerror("table \"%s\" may not be used for from lookups",
+ t->t_name);
+ YYERROR;
+ }
+
+ rule->flag_from = 1;
+ rule->table_from = strdup(anyhost->t_name);
+ rule->flag_smtp_mail_from = $1 ? -1 : 1;
+ rule->flag_smtp_mail_from_regex = 1;
+ rule->table_smtp_mail_from = strdup(t->t_name);
+}
| negation FOR LOCAL {
struct table *t = table_find(conf, "<localnames>");
@@ -1290,6 +1460,47 @@ negation TAG REGEX tables {
rule->flag_for_regex = 1;
rule->table_for = strdup(t->t_name);
}
+| negation FOR RCPT_TO tables {
+ struct table *anyhost = table_find(conf, "<anydestination>");
+ struct table *t = $4;
+
+ if (rule->flag_for) {
+ yyerror("for already specified for this rule");
+ YYERROR;
+ }
+
+ if (!table_check_use(t, T_DYNAMIC|T_LIST, K_MAILADDR)) {
+ yyerror("table \"%s\" may not be used for for lookups",
+ t->t_name);
+ YYERROR;
+ }
+
+ rule->flag_for = 1;
+ rule->table_for = strdup(anyhost->t_name);
+ rule->flag_smtp_rcpt_to = $1 ? -1 : 1;
+ rule->table_smtp_rcpt_to = strdup(t->t_name);
+}
+| negation FOR RCPT_TO REGEX tables {
+ struct table *anyhost = table_find(conf, "<anydestination>");
+ struct table *t = $5;
+
+ if (rule->flag_for) {
+ yyerror("for already specified for this rule");
+ YYERROR;
+ }
+
+ if (!table_check_use(t, T_DYNAMIC|T_LIST, K_REGEX)) {
+ yyerror("table \"%s\" may not be used for for lookups",
+ t->t_name);
+ YYERROR;
+ }
+
+ rule->flag_for = 1;
+ rule->table_for = strdup(anyhost->t_name);
+ rule->flag_smtp_rcpt_to = $1 ? -1 : 1;
+ rule->flag_smtp_rcpt_to_regex = 1;
+ rule->table_smtp_rcpt_to = strdup(t->t_name);
+}
;
match_options:
@@ -1336,6 +1547,9 @@ filter_action_builtin_nojunk
| JUNK {
filter_config->junk = 1;
}
+| BYPASS {
+ filter_config->bypass = 1;
+}
;
filter_action_builtin_nojunk:
@@ -1406,6 +1620,25 @@ negation HELO REGEX tables {
}
;
+filter_phase_check_auth:
+negation AUTH {
+ filter_config->not_auth = $1 ? -1 : 1;
+ filter_config->auth = 1;
+}
+;
+filter_phase_check_auth_table:
+negation AUTH tables {
+ filter_config->not_auth_table = $1 ? -1 : 1;
+ filter_config->auth_table = $3;
+}
+;
+filter_phase_check_auth_regex:
+negation AUTH REGEX tables {
+ filter_config->not_auth_regex = $1 ? -1 : 1;
+ filter_config->auth_regex = $4;
+}
+;
+
filter_phase_check_mail_from_table:
negation MAIL_FROM tables {
filter_config->not_mail_from_table = $1 ? -1 : 1;
@@ -1448,9 +1681,20 @@ filter_phase_check_helo_table |
filter_phase_check_helo_regex |
filter_phase_global_options;
+filter_phase_auth_options:
+filter_phase_check_helo_table |
+filter_phase_check_helo_regex |
+filter_phase_check_auth |
+filter_phase_check_auth_table |
+filter_phase_check_auth_regex |
+filter_phase_global_options;
+
filter_phase_mail_from_options:
filter_phase_check_helo_table |
filter_phase_check_helo_regex |
+filter_phase_check_auth |
+filter_phase_check_auth_table |
+filter_phase_check_auth_regex |
filter_phase_check_mail_from_table |
filter_phase_check_mail_from_regex |
filter_phase_global_options;
@@ -1458,6 +1702,9 @@ filter_phase_global_options;
filter_phase_rcpt_to_options:
filter_phase_check_helo_table |
filter_phase_check_helo_regex |
+filter_phase_check_auth |
+filter_phase_check_auth_table |
+filter_phase_check_auth_regex |
filter_phase_check_mail_from_table |
filter_phase_check_mail_from_regex |
filter_phase_check_rcpt_to_table |
@@ -1467,6 +1714,9 @@ filter_phase_global_options;
filter_phase_data_options:
filter_phase_check_helo_table |
filter_phase_check_helo_regex |
+filter_phase_check_auth |
+filter_phase_check_auth_table |
+filter_phase_check_auth_regex |
filter_phase_check_mail_from_table |
filter_phase_check_mail_from_regex |
filter_phase_global_options;
@@ -1491,6 +1741,9 @@ filter_phase_global_options;
filter_phase_commit_options:
filter_phase_check_helo_table |
filter_phase_check_helo_regex |
+filter_phase_check_auth |
+filter_phase_check_auth_table |
+filter_phase_check_auth_regex |
filter_phase_check_mail_from_table |
filter_phase_check_mail_from_regex |
filter_phase_global_options;
@@ -1515,6 +1768,11 @@ EHLO {
} MATCH filter_phase_helo_options filter_action_builtin
;
+filter_phase_auth:
+AUTH {
+} MATCH filter_phase_auth_options filter_action_builtin
+;
+
filter_phase_mail_from:
MAIL_FROM {
filter_config->phase = FILTER_MAIL_FROM;
@@ -1571,6 +1829,7 @@ filter_phase:
filter_phase_connect
| filter_phase_helo
| filter_phase_ehlo
+| filter_phase_auth
| filter_phase_mail_from
| filter_phase_rcpt_to
| filter_phase_data
@@ -1585,6 +1844,7 @@ filter_phase_connect
filterel:
STRING {
struct filter_config *fr;
+ struct filter_proc *fp;
size_t i;
if ((fr = dict_get(conf->sc_filters_dict, $1)) == NULL) {
@@ -1607,7 +1867,7 @@ STRING {
}
if (fr->proc) {
- if (dict_check(&filter_config->chain_procs, fr->proc)) {
+ if ((fp = dict_get(&filter_config->chain_procs, fr->proc))) {
yyerror("no proc allowed twice within a filter chain: %s", fr->proc);
free($1);
YYERROR;
@@ -1615,6 +1875,7 @@ STRING {
dict_set(&filter_config->chain_procs, fr->proc, NULL);
}
+ fr->filter_subsystem |= filter_config->filter_subsystem;
filter_config->chain_size += 1;
filter_config->chain = reallocarray(filter_config->chain, filter_config->chain_size, sizeof(char *));
if (filter_config->chain == NULL)
@@ -1630,13 +1891,15 @@ filterel
filter:
FILTER STRING PROC STRING {
+ struct filter_proc *fp;
+
if (dict_get(conf->sc_filters_dict, $2)) {
yyerror("filter already exists with that name: %s", $2);
free($2);
free($4);
YYERROR;
}
- if (! dict_get(conf->sc_processors_dict, $4)) {
+ if ((fp = dict_get(conf->sc_filter_processes_dict, $4)) == NULL) {
yyerror("no processor exist with that name: %s", $4);
free($4);
YYERROR;
@@ -1667,7 +1930,7 @@ FILTER STRING PROC_EXEC STRING {
filter_config->proc = xstrdup($2);
dict_set(conf->sc_filters_dict, $2, filter_config);
} proc_params {
- dict_set(conf->sc_processors_dict, filter_config->proc, processor);
+ dict_set(conf->sc_filter_processes_dict, filter_config->proc, processor);
processor = NULL;
filter_config = NULL;
}
@@ -1842,16 +2105,19 @@ limits_scheduler: opt_limit_scheduler limits_scheduler
opt_sock_listen : FILTER STRING {
+ struct filter_config *fc;
+
if (listen_opts.options & LO_FILTER) {
yyerror("filter already specified");
free($2);
YYERROR;
}
- if (dict_get(conf->sc_filters_dict, $2) == NULL) {
+ if ((fc = dict_get(conf->sc_filters_dict, $2)) == NULL) {
yyerror("no filter exist with that name: %s", $2);
free($2);
YYERROR;
}
+ fc->filter_subsystem |= FILTER_SUBSYSTEM_SMTP_IN;
listen_opts.options |= LO_FILTER;
listen_opts.filtername = $2;
}
@@ -1871,6 +2137,7 @@ opt_sock_listen : FILTER STRING {
listen_opts.filtername = xstrdup(buffer);
filter_config = xcalloc(1, sizeof *filter_config);
filter_config->filter_type = FILTER_TYPE_CHAIN;
+ filter_config->filter_subsystem |= FILTER_SUBSYSTEM_SMTP_IN;
dict_init(&filter_config->chain_procs);
} '{' filter_list '}' {
dict_set(conf->sc_filters_dict, listen_opts.filtername, filter_config);
@@ -1881,6 +2148,20 @@ opt_sock_listen : FILTER STRING {
YYERROR;
}
}
+ | TAG STRING {
+ if (listen_opts.options & LO_TAG) {
+ yyerror("tag already specified");
+ YYERROR;
+ }
+ listen_opts.options |= LO_TAG;
+
+ if (strlen($2) >= SMTPD_TAG_SIZE) {
+ yyerror("tag name too long");
+ free($2);
+ YYERROR;
+ }
+ listen_opts.tag = $2;
+ }
;
opt_if_listen : INET4 {
@@ -1963,15 +2244,18 @@ opt_if_listen : INET4 {
listen_opts.port = $2;
}
| FILTER STRING {
+ struct filter_config *fc;
+
if (listen_opts.options & LO_FILTER) {
yyerror("filter already specified");
YYERROR;
}
- if (dict_get(conf->sc_filters_dict, $2) == NULL) {
+ if ((fc = dict_get(conf->sc_filters_dict, $2)) == NULL) {
yyerror("no filter exist with that name: %s", $2);
free($2);
YYERROR;
}
+ fc->filter_subsystem |= FILTER_SUBSYSTEM_SMTP_IN;
listen_opts.options |= LO_FILTER;
listen_opts.filtername = $2;
}
@@ -1991,6 +2275,7 @@ opt_if_listen : INET4 {
listen_opts.filtername = xstrdup(buffer);
filter_config = xcalloc(1, sizeof *filter_config);
filter_config->filter_type = FILTER_TYPE_CHAIN;
+ filter_config->filter_subsystem |= FILTER_SUBSYSTEM_SMTP_IN;
dict_init(&filter_config->chain_procs);
} '{' filter_list '}' {
dict_set(conf->sc_filters_dict, listen_opts.filtername, filter_config);
@@ -2338,12 +2623,14 @@ lookup(char *s)
/* this has to be sorted always */
static const struct keywords keywords[] = {
{ "action", ACTION },
+ { "admd", ADMD },
{ "alias", ALIAS },
{ "any", ANY },
{ "auth", AUTH },
{ "auth-optional", AUTH_OPTIONAL },
{ "backup", BACKUP },
{ "bounce", BOUNCE },
+ { "bypass", BYPASS },
{ "ca", CA },
{ "cert", CERT },
{ "chain", CHAIN },
@@ -2921,7 +3208,6 @@ static void
create_sock_listener(struct listen_opts *lo)
{
struct listener *l = xcalloc(1, sizeof(*l));
- lo->tag = "local";
lo->hostname = conf->sc_hostname;
l->ss.ss_family = AF_LOCAL;
#ifdef HAVE_STRUCT_SOCKADDR_STORAGE_SS_LEN
diff --git a/smtpd/parser.c b/usr.sbin/smtpd/parser.c
index 997e3405..4c2321ec 100644
--- a/smtpd/parser.c
+++ b/usr.sbin/smtpd/parser.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: parser.c,v 1.41 2017/07/31 16:38:33 gilles Exp $ */
+/* $OpenBSD: parser.c,v 1.42 2020/01/06 11:02:38 gilles Exp $ */
/*
* Copyright (c) 2013 Eric Faurot <eric@openbsd.org>
@@ -221,6 +221,11 @@ cmd_run(int argc, char **argv)
return (node->cmd(np, np ? param : NULL));
fail:
+ if (TAILQ_FIRST(&node->children) == NULL) {
+ fprintf(stderr, "invalid command\n");
+ return (-1);
+ }
+
fprintf(stderr, "possibilities are:\n");
TAILQ_FOREACH(tmp, &node->children, entry) {
for (j = 0; j < i; j++)
diff --git a/smtpd/parser.h b/usr.sbin/smtpd/parser.h
index f0114e9e..f0114e9e 100644
--- a/smtpd/parser.h
+++ b/usr.sbin/smtpd/parser.h
diff --git a/smtpd/pony.c b/usr.sbin/smtpd/pony.c
index 1865b339..1865b339 100644
--- a/smtpd/pony.c
+++ b/usr.sbin/smtpd/pony.c
diff --git a/smtpd/proxy.c b/usr.sbin/smtpd/proxy.c
index fdaf4f27..fdaf4f27 100644
--- a/smtpd/proxy.c
+++ b/usr.sbin/smtpd/proxy.c
diff --git a/smtpd/queue.c b/usr.sbin/smtpd/queue.c
index 5b256958..434e3647 100644
--- a/smtpd/queue.c
+++ b/usr.sbin/smtpd/queue.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: queue.c,v 1.189 2018/12/30 23:09:58 guenther Exp $ */
+/* $OpenBSD: queue.c,v 1.190 2020/04/22 11:35:34 eric Exp $ */
/*
* Copyright (c) 2008 Gilles Chehade <gilles@poolp.org>
@@ -692,7 +692,6 @@ static void
queue_timeout(int fd, short event, void *p)
{
static uint32_t msgid = 0;
- struct dispatcher *dsp;
struct envelope evp;
struct event *ev = p;
struct timeval tv;
@@ -711,13 +710,6 @@ queue_timeout(int fd, short event, void *p)
}
if (r) {
- dsp = dict_get(env->sc_dispatchers, evp.dispatcher);
- if (dsp == NULL) {
- log_warnx("warn: queue: missing dispatcher \"%s\""
- " for envelope %016"PRIx64", ignoring",
- evp.dispatcher, evp.id);
- goto reset;
- }
if (msgid && evpid_to_msgid(evp.id) != msgid) {
m_create(p_scheduler, IMSG_QUEUE_MESSAGE_COMMIT,
0, 0, -1);
@@ -730,7 +722,6 @@ queue_timeout(int fd, short event, void *p)
m_close(p_scheduler);
}
-reset:
tv.tv_sec = 0;
tv.tv_usec = 10;
evtimer_add(ev, &tv);
diff --git a/smtpd/queue_backend.c b/usr.sbin/smtpd/queue_backend.c
index 6fc720da..fa945f47 100644
--- a/smtpd/queue_backend.c
+++ b/usr.sbin/smtpd/queue_backend.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: queue_backend.c,v 1.65 2018/12/30 23:09:58 guenther Exp $ */
+/* $OpenBSD: queue_backend.c,v 1.66 2020/04/22 11:35:34 eric Exp $ */
/*
* Copyright (c) 2011 Gilles Chehade <gilles@poolp.org>
@@ -732,6 +732,9 @@ envelope_validate(struct envelope *ep)
if (memchr(ep->errorline, '\0', sizeof(ep->errorline)) == NULL)
return "invalid error line";
+ if (dict_get(env->sc_dispatchers, ep->dispatcher) == NULL)
+ return "unknown dispatcher";
+
return NULL;
}
diff --git a/smtpd/queue_fs.c b/usr.sbin/smtpd/queue_fs.c
index b3ac4f95..097ba1e2 100644
--- a/smtpd/queue_fs.c
+++ b/usr.sbin/smtpd/queue_fs.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: queue_fs.c,v 1.19 2019/06/28 13:32:51 deraadt Exp $ */
+/* $OpenBSD: queue_fs.c,v 1.20 2020/02/25 17:03:13 millert Exp $ */
/*
* Copyright (c) 2011 Gilles Chehade <gilles@poolp.org>
@@ -235,8 +235,7 @@ queue_fs_envelope_create(uint32_t msgid, const char *buf, size_t len,
fsqueue_envelope_incoming_path(*evpid, path,
sizeof(path));
- r = fsqueue_envelope_dump(path, buf, len, 0, 0);
- if (r >= 0)
+ if ((r = fsqueue_envelope_dump(path, buf, len, 0, 0)) != 0)
goto done;
}
r = 0;
diff --git a/smtpd/queue_null.c b/usr.sbin/smtpd/queue_null.c
index 1e608be8..1e608be8 100644
--- a/smtpd/queue_null.c
+++ b/usr.sbin/smtpd/queue_null.c
diff --git a/smtpd/queue_proc.c b/usr.sbin/smtpd/queue_proc.c
index d6e0f409..d6e0f409 100644
--- a/smtpd/queue_proc.c
+++ b/usr.sbin/smtpd/queue_proc.c
diff --git a/smtpd/queue_ram.c b/usr.sbin/smtpd/queue_ram.c
index 50ce17e1..50ce17e1 100644
--- a/smtpd/queue_ram.c
+++ b/usr.sbin/smtpd/queue_ram.c
diff --git a/smtpd/report_smtp.c b/usr.sbin/smtpd/report_smtp.c
index cb5ef24a..7802eaae 100644
--- a/smtpd/report_smtp.c
+++ b/usr.sbin/smtpd/report_smtp.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: report_smtp.c,v 1.10 2019/09/19 14:40:53 gilles Exp $ */
+/* $OpenBSD: report_smtp.c,v 1.11 2020/01/07 23:03:37 gilles Exp $ */
/*
* Copyright (c) 2018 Gilles Chehade <gilles@poolp.org>
@@ -180,15 +180,6 @@ void
report_smtp_tx_mail(const char *direction, uint64_t qid, uint32_t msgid, const char *address, int ok)
{
struct timeval tv;
- char buffer[SMTPD_MAXMAILADDRSIZE];
- char *p;
-
- if ((p = strchr(address, '<')) == NULL)
- return;
- (void)strlcpy(buffer, p + 1, sizeof buffer);
- if ((p = strchr(buffer, '>')) == NULL)
- return;
- *p = '\0';
gettimeofday(&tv, NULL);
@@ -197,7 +188,7 @@ report_smtp_tx_mail(const char *direction, uint64_t qid, uint32_t msgid, const c
m_add_timeval(p_lka, &tv);
m_add_id(p_lka, qid);
m_add_u32(p_lka, msgid);
- m_add_string(p_lka, buffer);
+ m_add_string(p_lka, address);
m_add_int(p_lka, ok);
m_close(p_lka);
}
@@ -206,15 +197,6 @@ void
report_smtp_tx_rcpt(const char *direction, uint64_t qid, uint32_t msgid, const char *address, int ok)
{
struct timeval tv;
- char buffer[SMTPD_MAXMAILADDRSIZE];
- char *p;
-
- if ((p = strchr(address, '<')) == NULL)
- return;
- (void)strlcpy(buffer, p + 1, sizeof buffer);
- if ((p = strchr(buffer, '>')) == NULL)
- return;
- *p = '\0';
gettimeofday(&tv, NULL);
@@ -223,7 +205,7 @@ report_smtp_tx_rcpt(const char *direction, uint64_t qid, uint32_t msgid, const c
m_add_timeval(p_lka, &tv);
m_add_id(p_lka, qid);
m_add_u32(p_lka, msgid);
- m_add_string(p_lka, buffer);
+ m_add_string(p_lka, address);
m_add_int(p_lka, ok);
m_close(p_lka);
}
diff --git a/smtpd/resolver.c b/usr.sbin/smtpd/resolver.c
index f0f0f8ea..f0f0f8ea 100644
--- a/smtpd/resolver.c
+++ b/usr.sbin/smtpd/resolver.c
diff --git a/smtpd/rfc5322.c b/usr.sbin/smtpd/rfc5322.c
index 0af66772..0af66772 100644
--- a/smtpd/rfc5322.c
+++ b/usr.sbin/smtpd/rfc5322.c
diff --git a/smtpd/rfc5322.h b/usr.sbin/smtpd/rfc5322.h
index 0979bd4c..0979bd4c 100644
--- a/smtpd/rfc5322.h
+++ b/usr.sbin/smtpd/rfc5322.h
diff --git a/smtpd/ruleset.c b/usr.sbin/smtpd/ruleset.c
index 2eae58c0..719a2913 100644
--- a/smtpd/ruleset.c
+++ b/usr.sbin/smtpd/ruleset.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: ruleset.c,v 1.46 2019/11/12 20:21:46 gilles Exp $ */
+/* $OpenBSD: ruleset.c,v 1.47 2019/11/25 14:18:33 gilles Exp $ */
/*
* Copyright (c) 2009 Gilles Chehade <gilles@poolp.org>
@@ -154,6 +154,8 @@ static int
ruleset_match_smtp_auth(struct rule *r, const struct envelope *evp)
{
int ret;
+ struct table *table;
+ enum table_service service;
if (!r->flag_smtp_auth)
return 1;
@@ -161,14 +163,14 @@ ruleset_match_smtp_auth(struct rule *r, const struct envelope *evp)
if (!(evp->flags & EF_AUTHENTICATED))
ret = 0;
else if (r->table_smtp_auth) {
- /* XXX - not until smtp_session->username is added to envelope */
- /*
- * table = table_find(m->from_table);
- * key = evp->username;
- * return table_match(table, K_CREDENTIALS, key);
- */
- return -1;
+ if (r->flag_smtp_auth_regex)
+ service = K_REGEX;
+ else
+ service = strchr(evp->username, '@') ?
+ K_MAILADDR : K_STRING;
+ table = table_find(env, r->table_smtp_auth);
+ ret = table_match(table, service, evp->username);
}
else
ret = 1;
diff --git a/smtpd/runq.c b/usr.sbin/smtpd/runq.c
index 786d36fb..786d36fb 100644
--- a/smtpd/runq.c
+++ b/usr.sbin/smtpd/runq.c
diff --git a/smtpd/scheduler.c b/usr.sbin/smtpd/scheduler.c
index ea70a83d..ea70a83d 100644
--- a/smtpd/scheduler.c
+++ b/usr.sbin/smtpd/scheduler.c
diff --git a/smtpd/scheduler_backend.c b/usr.sbin/smtpd/scheduler_backend.c
index ad2b4cab..ad2b4cab 100644
--- a/smtpd/scheduler_backend.c
+++ b/usr.sbin/smtpd/scheduler_backend.c
diff --git a/smtpd/scheduler_null.c b/usr.sbin/smtpd/scheduler_null.c
index 40db6205..40db6205 100644
--- a/smtpd/scheduler_null.c
+++ b/usr.sbin/smtpd/scheduler_null.c
diff --git a/smtpd/scheduler_proc.c b/usr.sbin/smtpd/scheduler_proc.c
index 5f4e8b70..5f4e8b70 100644
--- a/smtpd/scheduler_proc.c
+++ b/usr.sbin/smtpd/scheduler_proc.c
diff --git a/smtpd/scheduler_ramqueue.c b/usr.sbin/smtpd/scheduler_ramqueue.c
index 0c04fc0b..0c04fc0b 100644
--- a/smtpd/scheduler_ramqueue.c
+++ b/usr.sbin/smtpd/scheduler_ramqueue.c
diff --git a/smtpd/sendmail.8 b/usr.sbin/smtpd/sendmail.8
index 1696a861..1696a861 100644
--- a/smtpd/sendmail.8
+++ b/usr.sbin/smtpd/sendmail.8
diff --git a/smtpd/smtp.1 b/usr.sbin/smtpd/smtp.1
index 3cc03844..3cc03844 100644
--- a/smtpd/smtp.1
+++ b/usr.sbin/smtpd/smtp.1
diff --git a/smtpd/smtp.c b/usr.sbin/smtpd/smtp.c
index 602fd0d6..602fd0d6 100644
--- a/smtpd/smtp.c
+++ b/usr.sbin/smtpd/smtp.c
diff --git a/smtpd/smtp.h b/usr.sbin/smtpd/smtp.h
index dc91d878..dc91d878 100644
--- a/smtpd/smtp.h
+++ b/usr.sbin/smtpd/smtp.h
diff --git a/smtpd/smtp/Makefile b/usr.sbin/smtpd/smtp/Makefile
index 380e3ad6..380e3ad6 100644
--- a/smtpd/smtp/Makefile
+++ b/usr.sbin/smtpd/smtp/Makefile
diff --git a/smtpd/smtp_client.c b/usr.sbin/smtpd/smtp_client.c
index 22e79890..8e146e1b 100644
--- a/smtpd/smtp_client.c
+++ b/usr.sbin/smtpd/smtp_client.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: smtp_client.c,v 1.12 2019/09/10 12:08:26 eric Exp $ */
+/* $OpenBSD: smtp_client.c,v 1.14 2020/04/24 11:34:07 eric Exp $ */
/*
* Copyright (c) 2018 Eric Faurot <eric@openbsd.org>
@@ -680,6 +680,10 @@ smtp_client_readline(struct smtp_client *proto)
return 0;
}
+ /* Strip trailing '\r' */
+ if (len && line[len - 1] == '\r')
+ line[--len] = '\0';
+
log_trace(TRACE_SMTPCLT, "%p: <<< %s", proto, line);
/* Validate SMTP */
@@ -779,9 +783,10 @@ smtp_client_replycat(struct smtp_client *proto, const char *line)
line += 3;
if (line[0]) {
line += 1;
- if (isdigit((int)line[0]) && line[1] == '.' &&
- isdigit((int)line[2]) && line[3] == '.' &&
- isdigit((int)line[4]) && isspace((int)line[5]))
+ if (isdigit((unsigned char)line[0]) && line[1] == '.' &&
+ isdigit((unsigned char)line[2]) && line[3] == '.' &&
+ isdigit((unsigned char)line[4]) &&
+ isspace((unsigned char)line[5]))
line += 5;
}
} else
diff --git a/smtpd/smtp_session.c b/usr.sbin/smtpd/smtp_session.c
index d2487807..c103fa19 100644
--- a/smtpd/smtp_session.c
+++ b/usr.sbin/smtpd/smtp_session.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: smtp_session.c,v 1.415 2019/10/04 08:34:29 gilles Exp $ */
+/* $OpenBSD: smtp_session.c,v 1.427 2020/11/20 20:37:56 jung Exp $ */
/*
* Copyright (c) 2008 Gilles Chehade <gilles@poolp.org>
@@ -259,9 +259,32 @@ static void smtp_filter_end(struct smtp_session *);
static void smtp_filter_data_begin(struct smtp_session *);
static void smtp_filter_data_end(struct smtp_session *);
+static void smtp_report_link_connect(struct smtp_session *, const char *, int,
+ const struct sockaddr_storage *,
+ const struct sockaddr_storage *);
+static void smtp_report_link_greeting(struct smtp_session *, const char *);
+static void smtp_report_link_identify(struct smtp_session *, const char *, const char *);
+static void smtp_report_link_tls(struct smtp_session *, const char *);
+static void smtp_report_link_disconnect(struct smtp_session *);
+static void smtp_report_link_auth(struct smtp_session *, const char *, const char *);
+static void smtp_report_tx_reset(struct smtp_session *, uint32_t);
+static void smtp_report_tx_begin(struct smtp_session *, uint32_t);
+static void smtp_report_tx_mail(struct smtp_session *, uint32_t, const char *, int);
+static void smtp_report_tx_rcpt(struct smtp_session *, uint32_t, const char *, int);
+static void smtp_report_tx_envelope(struct smtp_session *, uint32_t, uint64_t);
+static void smtp_report_tx_data(struct smtp_session *, uint32_t, int);
+static void smtp_report_tx_commit(struct smtp_session *, uint32_t, size_t);
+static void smtp_report_tx_rollback(struct smtp_session *, uint32_t);
+static void smtp_report_protocol_client(struct smtp_session *, const char *);
+static void smtp_report_protocol_server(struct smtp_session *, const char *);
+static void smtp_report_filter_response(struct smtp_session *, int, int, const char *);
+static void smtp_report_timeout(struct smtp_session *);
+
+
/* musl work-around */
void portable_freeaddrinfo(struct addrinfo *);
+
static struct {
int code;
enum filter_phase filter_phase;
@@ -439,7 +462,7 @@ header_address_rewrite_buffer(char *buffer, const char *address, size_t len)
pos_component_beg = 0;
else {
for (pos_component_beg = pos_component_end; pos_component_beg >= 0; --pos_component_beg)
- if (buffer[pos_component_beg] == ')' || isspace(buffer[pos_component_beg]))
+ if (buffer[pos_component_beg] == ')' || isspace((unsigned char)buffer[pos_component_beg]))
break;
pos_component_beg += 1;
pos_component_end += 1;
@@ -698,7 +721,7 @@ smtp_session_imsg(struct mproc *p, struct imsg *imsg)
{
struct smtp_session *s;
struct smtp_rcpt *rcpt;
- char user[LOGIN_NAME_MAX];
+ char user[SMTPD_MAXMAILADDRSIZE];
char tmp[SMTP_LINE_MAX];
struct msg m;
const char *line, *helo;
@@ -787,7 +810,6 @@ smtp_session_imsg(struct mproc *p, struct imsg *imsg)
s->tx->msgid = msgid;
s->tx->evp.id = msgid_to_evpid(msgid);
s->tx->rcptcount = 0;
- report_smtp_tx_begin("smtp-in", s->id, s->tx->msgid);
smtp_reply(s, "250 %s Ok",
esc_code(ESC_STATUS_OK, ESC_OTHER_STATUS));
} else {
@@ -856,7 +878,7 @@ smtp_session_imsg(struct mproc *p, struct imsg *imsg)
m_get_evpid(&m, &evpid);
s->tx->evp.id = evpid;
s->tx->destcount++;
- report_smtp_tx_envelope("smtp-in", s->id, s->tx->msgid, evpid);
+ smtp_report_tx_envelope(s, s->tx->msgid, evpid);
}
else
s->tx->error = TX_ERROR_ENVELOPE;
@@ -903,9 +925,9 @@ smtp_session_imsg(struct mproc *p, struct imsg *imsg)
m_end(&m);
s = tree_xpop(&wait_queue_commit, reqid);
if (!success) {
- smtp_tx_free(s->tx);
smtp_reply(s, "421 %s Temporary failure",
esc_code(ESC_STATUS_TEMPFAIL, ESC_OTHER_MAIL_SYSTEM_STATUS));
+ smtp_tx_free(s->tx);
smtp_enter_state(s, STATE_QUIT);
return;
}
@@ -913,6 +935,8 @@ smtp_session_imsg(struct mproc *p, struct imsg *imsg)
smtp_reply(s, "250 %s %08x Message accepted for delivery",
esc_code(ESC_STATUS_OK, ESC_OTHER_STATUS),
s->tx->msgid);
+ smtp_report_tx_commit(s, s->tx->msgid, s->tx->odatalen);
+ smtp_report_tx_reset(s, s->tx->msgid);
log_info("%016"PRIx64" smtp message "
"msgid=%08x size=%zu nrcpt=%zu proto=%s",
@@ -952,7 +976,7 @@ smtp_session_imsg(struct mproc *p, struct imsg *imsg)
"result=ok",
s->id, user);
s->flags |= SF_AUTHENTICATED;
- report_smtp_link_auth("smtp-in", s->id, user, "pass");
+ smtp_report_link_auth(s, user, "pass");
smtp_reply(s, "235 %s Authentication succeeded",
esc_code(ESC_STATUS_OK, ESC_OTHER_STATUS));
}
@@ -961,7 +985,7 @@ smtp_session_imsg(struct mproc *p, struct imsg *imsg)
"authentication user=%s "
"result=permfail",
s->id, user);
- report_smtp_link_auth("smtp-in", s->id, user, "fail");
+ smtp_report_link_auth(s, user, "fail");
smtp_auth_failure_pause(s);
return;
}
@@ -970,7 +994,7 @@ smtp_session_imsg(struct mproc *p, struct imsg *imsg)
"authentication user=%s "
"result=tempfail",
s->id, user);
- report_smtp_link_auth("smtp-in", s->id, user, "error");
+ smtp_report_link_auth(s, user, "error");
smtp_reply(s, "421 %s Temporary failure",
esc_code(ESC_STATUS_TEMPFAIL, ESC_OTHER_MAIL_SYSTEM_STATUS));
}
@@ -1002,7 +1026,7 @@ smtp_session_imsg(struct mproc *p, struct imsg *imsg)
if (!strncmp(filter_param, "421", 3))
filter_response = FILTER_DISCONNECT;
- report_smtp_filter_response("smtp-in", s->id, s->filter_phase,
+ smtp_report_filter_response(s, s->filter_phase,
filter_response, filter_param);
smtp_reply(s, "%s", filter_param);
@@ -1026,7 +1050,7 @@ smtp_session_imsg(struct mproc *p, struct imsg *imsg)
/* fallthrough */
case FILTER_REWRITE:
- report_smtp_filter_response("smtp-in", s->id, s->filter_phase,
+ smtp_report_filter_response(s, s->filter_phase,
filter_response,
filter_param == s->filter_param ? NULL : filter_param);
if (s->filter_phase == FILTER_CONNECT) {
@@ -1093,7 +1117,7 @@ smtp_io(struct io *io, int evt, void *arg)
log_info("%016"PRIx64" smtp tls ciphers=%s",
s->id, ssl_to_text(io_tls(s->io)));
- report_smtp_link_tls("smtp-in", s->id, ssl_to_text(io_tls(s->io)));
+ smtp_report_link_tls(s, ssl_to_text(io_tls(s->io)));
s->flags |= SF_SECURE;
s->helo[0] = '\0';
@@ -1118,6 +1142,10 @@ smtp_io(struct io *io, int evt, void *arg)
if (line == NULL)
return;
+ /* Strip trailing '\r' */
+ if (len && line[len - 1] == '\r')
+ line[--len] = '\0';
+
/* Message body */
eom = 0;
if (s->state == STATE_BODY) {
@@ -1186,7 +1214,7 @@ smtp_io(struct io *io, int evt, void *arg)
log_info("%016"PRIx64" smtp disconnected "
"reason=timeout",
s->id);
- report_smtp_timeout("smtp-in", s->id);
+ smtp_report_timeout(s);
smtp_free(s, "timeout");
break;
@@ -1221,20 +1249,20 @@ smtp_command(struct smtp_session *s, char *line)
* These states are special.
*/
if (s->state == STATE_AUTH_INIT) {
- report_smtp_protocol_client("smtp-in", s->id, "********");
+ smtp_report_protocol_client(s, "********");
smtp_rfc4954_auth_plain(s, line);
return;
}
if (s->state == STATE_AUTH_USERNAME || s->state == STATE_AUTH_PASSWORD) {
- report_smtp_protocol_client("smtp-in", s->id, "********");
+ smtp_report_protocol_client(s, "********");
smtp_rfc4954_auth_login(s, line);
return;
}
if (s->state == STATE_HELO && strncasecmp(line, "AUTH PLAIN ", 11) == 0)
- report_smtp_protocol_client("smtp-in", s->id, "AUTH PLAIN ********");
+ smtp_report_protocol_client(s, "AUTH PLAIN ********");
else
- report_smtp_protocol_client("smtp-in", s->id, line);
+ smtp_report_protocol_client(s, line);
/*
@@ -1649,10 +1677,6 @@ smtp_filter_begin(struct smtp_session *s)
m_create(p_lka, IMSG_FILTER_SMTP_BEGIN, 0, 0, -1);
m_add_id(p_lka, s->id);
m_add_string(p_lka, s->listener->filter_name);
- m_add_sockaddr(p_lka, (struct sockaddr *)&s->ss);
- m_add_sockaddr(p_lka, (struct sockaddr *)&s->listener->ss);
- m_add_string(p_lka, s->rdns);
- m_add_int(p_lka, s->fcrdns);
m_close(p_lka);
}
@@ -1724,14 +1748,14 @@ smtp_filter_phase(enum filter_phase phase, struct smtp_session *s, const char *p
static void
smtp_proceed_rset(struct smtp_session *s, const char *args)
{
+ smtp_reply(s, "250 %s Reset state",
+ esc_code(ESC_STATUS_OK, ESC_OTHER_STATUS));
+
if (s->tx) {
if (s->tx->msgid)
smtp_tx_rollback(s->tx);
smtp_tx_free(s->tx);
}
-
- smtp_reply(s, "250 %s Reset state",
- esc_code(ESC_STATUS_OK, ESC_OTHER_STATUS));
}
static void
@@ -1740,7 +1764,7 @@ smtp_proceed_helo(struct smtp_session *s, const char *args)
(void)strlcpy(s->helo, args, sizeof(s->helo));
s->flags &= SF_SECURE | SF_AUTHENTICATED | SF_VERIFIED;
- report_smtp_link_identify("smtp-in", s->id, "HELO", s->helo);
+ smtp_report_link_identify(s, "HELO", s->helo);
smtp_enter_state(s, STATE_HELO);
@@ -1760,7 +1784,7 @@ smtp_proceed_ehlo(struct smtp_session *s, const char *args)
s->flags |= SF_EHLO;
s->flags |= SF_8BITMIME;
- report_smtp_link_identify("smtp-in", s->id, "EHLO", s->helo);
+ smtp_report_link_identify(s, "EHLO", s->helo);
smtp_enter_state(s, STATE_HELO);
smtp_reply(s, "250-%s Hello %s %s%s%s, pleased to meet you",
@@ -2047,7 +2071,7 @@ smtp_connected(struct smtp_session *s)
smtp_filter_begin(s);
- report_smtp_link_connect("smtp-in", s->id, s->rdns, s->fcrdns, &s->ss,
+ smtp_report_link_connect(s, s->rdns, s->fcrdns, &s->ss,
&s->listener->ss);
smtp_filter_phase(FILTER_CONNECT, s, ss_to_text(&s->ss));
@@ -2067,7 +2091,7 @@ smtp_send_banner(struct smtp_session *s)
{
smtp_reply(s, "220 %s ESMTP %s", s->smtpname, SMTPD_NAME);
s->banner_sent = 1;
- report_smtp_link_greeting("smtp-in", s->id, s->smtpname);
+ smtp_report_link_greeting(s, s->smtpname);
}
void
@@ -2102,20 +2126,23 @@ smtp_reply(struct smtp_session *s, char *fmt, ...)
}
log_trace(TRACE_SMTP, "smtp: %p: >>> %s", s, buf);
+ smtp_report_protocol_server(s, buf);
switch (buf[0]) {
case '2':
if (s->tx) {
- if (s->last_cmd == CMD_MAIL_FROM)
- report_smtp_tx_mail("smtp-in", s->id, s->tx->msgid, s->cmd + 10, 1);
+ if (s->last_cmd == CMD_MAIL_FROM) {
+ smtp_report_tx_begin(s, s->tx->msgid);
+ smtp_report_tx_mail(s, s->tx->msgid, s->cmd + 10, 1);
+ }
else if (s->last_cmd == CMD_RCPT_TO)
- report_smtp_tx_rcpt("smtp-in", s->id, s->tx->msgid, s->cmd + 8, 1);
+ smtp_report_tx_rcpt(s, s->tx->msgid, s->cmd + 8, 1);
}
break;
case '3':
if (s->tx) {
if (s->last_cmd == CMD_DATA)
- report_smtp_tx_data("smtp-in", s->id, s->tx->msgid, 1);
+ smtp_report_tx_data(s, s->tx->msgid, 1);
}
break;
case '5':
@@ -2125,13 +2152,13 @@ smtp_reply(struct smtp_session *s, char *fmt, ...)
*/
if (s->tx) {
if (s->last_cmd == CMD_MAIL_FROM)
- report_smtp_tx_mail("smtp-in", s->id, s->tx->msgid,
+ smtp_report_tx_mail(s, s->tx->msgid,
s->cmd + 10, buf[0] == '4' ? -1 : 0);
else if (s->last_cmd == CMD_RCPT_TO)
- report_smtp_tx_rcpt("smtp-in", s->id,
+ smtp_report_tx_rcpt(s,
s->tx->msgid, s->cmd + 8, buf[0] == '4' ? -1 : 0);
else if (s->last_cmd == CMD_DATA && s->tx->rcptcount)
- report_smtp_tx_data("smtp-in", s->id, s->tx->msgid,
+ smtp_report_tx_data(s, s->tx->msgid,
buf[0] == '4' ? -1 : 0);
}
@@ -2169,7 +2196,6 @@ smtp_reply(struct smtp_session *s, char *fmt, ...)
}
io_xprintf(s->io, "%s\r\n", buf);
- report_smtp_protocol_server("smtp-in", s->id, buf);
}
static void
@@ -2181,7 +2207,7 @@ smtp_free(struct smtp_session *s, const char * reason)
smtp_tx_free(s->tx);
}
- report_smtp_link_disconnect("smtp-in", s->id);
+ smtp_report_link_disconnect(s);
smtp_filter_end(s);
if (s->flags & SF_SECURE && s->listener->flags & F_SMTPS)
@@ -2224,25 +2250,23 @@ smtp_mailaddr(struct mailaddr *maddr, char *line, int mailfrom, char **args,
memmove(maddr->user, p, strlen(p) + 1);
}
- if (!valid_localpart(maddr->user) ||
- !valid_domainpart(maddr->domain)) {
- /* accept empty return-path in MAIL FROM, required for bounces */
- if (mailfrom && maddr->user[0] == '\0' && maddr->domain[0] == '\0')
- return (1);
-
- /* no user-part, reject */
- if (maddr->user[0] == '\0')
- return (0);
+ /* accept empty return-path in MAIL FROM, required for bounces */
+ if (mailfrom && maddr->user[0] == '\0' && maddr->domain[0] == '\0')
+ return (1);
- /* no domain, local user */
- if (maddr->domain[0] == '\0') {
- (void)strlcpy(maddr->domain, domain,
- sizeof(maddr->domain));
- return (1);
- }
+ /* no or invalid user-part, reject */
+ if (maddr->user[0] == '\0' || !valid_localpart(maddr->user))
return (0);
+
+ /* no domain part, local user */
+ if (maddr->domain[0] == '\0') {
+ (void)strlcpy(maddr->domain, domain,
+ sizeof(maddr->domain));
}
+ if (!valid_domainpart(maddr->domain))
+ return (0);
+
return (1);
}
@@ -2394,6 +2418,7 @@ smtp_tx(struct smtp_session *s)
(void)strlcpy(tx->evp.smtpname, s->smtpname, sizeof(tx->evp.smtpname));
(void)strlcpy(tx->evp.hostname, s->rdns, sizeof tx->evp.hostname);
(void)strlcpy(tx->evp.helo, s->helo, sizeof(tx->evp.helo));
+ (void)strlcpy(tx->evp.username, s->username, sizeof(tx->evp.username));
if (s->flags & SF_BOUNCE)
tx->evp.flags |= EF_BOUNCE;
@@ -2562,7 +2587,14 @@ smtp_tx_rcpt_to(struct smtp_tx *tx, const char *line)
}
} else if (ADVERTISE_EXT_DSN(tx->session) && strncasecmp(opt, "ORCPT=", 6) == 0) {
opt += 6;
- if (!text_to_mailaddr(&tx->evp.dsn_orcpt, opt)) {
+
+ if (strncasecmp(opt, "rfc822;", 7) == 0)
+ opt += 7;
+
+ if (!text_to_mailaddr(&tx->evp.dsn_orcpt, opt) ||
+ !valid_localpart(tx->evp.dsn_orcpt.user) ||
+ (strlen(tx->evp.dsn_orcpt.domain) != 0 &&
+ !valid_domainpart(tx->evp.dsn_orcpt.domain))) {
smtp_reply(tx->session,
"553 ORCPT address syntax error");
return;
@@ -2598,8 +2630,6 @@ smtp_tx_commit(struct smtp_tx *tx)
m_add_msgid(p_queue, tx->msgid);
m_close(p_queue);
tree_xset(&wait_queue_commit, tx->session->id, tx->session);
- report_smtp_tx_commit("smtp-in", tx->session->id, tx->msgid, tx->odatalen);
- report_smtp_tx_reset("smtp-in", tx->session->id, tx->msgid);
smtp_filter_data_end(tx->session);
}
@@ -2609,8 +2639,8 @@ smtp_tx_rollback(struct smtp_tx *tx)
m_create(p_queue, IMSG_SMTP_MESSAGE_ROLLBACK, 0, 0, -1);
m_add_msgid(p_queue, tx->msgid);
m_close(p_queue);
- report_smtp_tx_rollback("smtp-in", tx->session->id, tx->msgid);
- report_smtp_tx_reset("smtp-in", tx->session->id, tx->msgid);
+ smtp_report_tx_rollback(tx->session, tx->msgid);
+ smtp_report_tx_reset(tx->session, tx->msgid);
smtp_filter_data_end(tx->session);
}
@@ -2623,7 +2653,7 @@ smtp_tx_dataline(struct smtp_tx *tx, const char *line)
log_trace(TRACE_SMTP, "<<< [MSG] %s", line);
if (!strcmp(line, ".")) {
- report_smtp_protocol_client("smtp-in", tx->session->id, ".");
+ smtp_report_protocol_client(tx->session, ".");
log_trace(TRACE_SMTP, "<<< [EOM]");
if (tx->error)
return 1;
@@ -3006,3 +3036,189 @@ smtp_strstate(int state)
return (buf);
}
}
+
+
+static void
+smtp_report_link_connect(struct smtp_session *s, const char *rdns, int fcrdns,
+ const struct sockaddr_storage *ss_src,
+ const struct sockaddr_storage *ss_dest)
+{
+ if (! SESSION_FILTERED(s))
+ return;
+
+ report_smtp_link_connect("smtp-in", s->id, rdns, fcrdns, ss_src, ss_dest);
+}
+
+static void
+smtp_report_link_greeting(struct smtp_session *s,
+ const char *domain)
+{
+ if (! SESSION_FILTERED(s))
+ return;
+
+ report_smtp_link_greeting("smtp-in", s->id, domain);
+}
+
+static void
+smtp_report_link_identify(struct smtp_session *s, const char *method, const char *identity)
+{
+ if (! SESSION_FILTERED(s))
+ return;
+
+ report_smtp_link_identify("smtp-in", s->id, method, identity);
+}
+
+static void
+smtp_report_link_tls(struct smtp_session *s, const char *ssl)
+{
+ if (! SESSION_FILTERED(s))
+ return;
+
+ report_smtp_link_tls("smtp-in", s->id, ssl);
+}
+
+static void
+smtp_report_link_disconnect(struct smtp_session *s)
+{
+ if (! SESSION_FILTERED(s))
+ return;
+
+ report_smtp_link_disconnect("smtp-in", s->id);
+}
+
+static void
+smtp_report_link_auth(struct smtp_session *s, const char *user, const char *result)
+{
+ if (! SESSION_FILTERED(s))
+ return;
+
+ report_smtp_link_auth("smtp-in", s->id, user, result);
+}
+
+static void
+smtp_report_tx_reset(struct smtp_session *s, uint32_t msgid)
+{
+ if (! SESSION_FILTERED(s))
+ return;
+
+ report_smtp_tx_reset("smtp-in", s->id, msgid);
+}
+
+static void
+smtp_report_tx_begin(struct smtp_session *s, uint32_t msgid)
+{
+ if (! SESSION_FILTERED(s))
+ return;
+
+ report_smtp_tx_begin("smtp-in", s->id, msgid);
+}
+
+static void
+smtp_report_tx_mail(struct smtp_session *s, uint32_t msgid, const char *address, int ok)
+{
+ char mailaddr[SMTPD_MAXMAILADDRSIZE];
+ char *p;
+
+ if (! SESSION_FILTERED(s))
+ return;
+
+ if ((p = strchr(address, '<')) == NULL)
+ return;
+ (void)strlcpy(mailaddr, p + 1, sizeof mailaddr);
+ if ((p = strchr(mailaddr, '>')) == NULL)
+ return;
+ *p = '\0';
+
+ report_smtp_tx_mail("smtp-in", s->id, msgid, mailaddr, ok);
+}
+
+static void
+smtp_report_tx_rcpt(struct smtp_session *s, uint32_t msgid, const char *address, int ok)
+{
+ char mailaddr[SMTPD_MAXMAILADDRSIZE];
+ char *p;
+
+ if (! SESSION_FILTERED(s))
+ return;
+
+ if ((p = strchr(address, '<')) == NULL)
+ return;
+ (void)strlcpy(mailaddr, p + 1, sizeof mailaddr);
+ if ((p = strchr(mailaddr, '>')) == NULL)
+ return;
+ *p = '\0';
+
+ report_smtp_tx_rcpt("smtp-in", s->id, msgid, mailaddr, ok);
+}
+
+static void
+smtp_report_tx_envelope(struct smtp_session *s, uint32_t msgid, uint64_t evpid)
+{
+ if (! SESSION_FILTERED(s))
+ return;
+
+ report_smtp_tx_envelope("smtp-in", s->id, msgid, evpid);
+}
+
+static void
+smtp_report_tx_data(struct smtp_session *s, uint32_t msgid, int ok)
+{
+ if (! SESSION_FILTERED(s))
+ return;
+
+ report_smtp_tx_data("smtp-in", s->id, msgid, ok);
+}
+
+static void
+smtp_report_tx_commit(struct smtp_session *s, uint32_t msgid, size_t msgsz)
+{
+ if (! SESSION_FILTERED(s))
+ return;
+
+ report_smtp_tx_commit("smtp-in", s->id, msgid, msgsz);
+}
+
+static void
+smtp_report_tx_rollback(struct smtp_session *s, uint32_t msgid)
+{
+ if (! SESSION_FILTERED(s))
+ return;
+
+ report_smtp_tx_rollback("smtp-in", s->id, msgid);
+}
+
+static void
+smtp_report_protocol_client(struct smtp_session *s, const char *command)
+{
+ if (! SESSION_FILTERED(s))
+ return;
+
+ report_smtp_protocol_client("smtp-in", s->id, command);
+}
+
+static void
+smtp_report_protocol_server(struct smtp_session *s, const char *response)
+{
+ if (! SESSION_FILTERED(s))
+ return;
+
+ report_smtp_protocol_server("smtp-in", s->id, response);
+}
+
+static void
+smtp_report_filter_response(struct smtp_session *s, int phase, int response, const char *param)
+{
+ if (! SESSION_FILTERED(s))
+ return;
+
+ report_smtp_filter_response("smtp-in", s->id, phase, response, param);
+}
+
+static void
+smtp_report_timeout(struct smtp_session *s)
+{
+ if (! SESSION_FILTERED(s))
+ return;
+
+ report_smtp_timeout("smtp-in", s->id);
+}
diff --git a/smtpd/smtpc.c b/usr.sbin/smtpd/smtpc.c
index 59479703..c0f5d992 100644
--- a/smtpd/smtpc.c
+++ b/usr.sbin/smtpd/smtpc.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: smtpc.c,v 1.10 2019/09/21 09:04:08 semarie Exp $ */
+/* $OpenBSD: smtpc.c,v 1.11 2020/09/14 18:32:11 millert Exp $ */
/*
* Copyright (c) 2018 Eric Faurot <eric@openbsd.org>
@@ -193,7 +193,7 @@ parse_server(char *server)
*p = '\0';
p += 3;
/* check for credentials */
- c = strchr(p, '@');
+ c = strrchr(p, '@');
if (c) {
creds = p;
*c = '\0';
diff --git a/smtpd/smtpctl.8 b/usr.sbin/smtpd/smtpctl.8
index 1efcff63..b29f2063 100644
--- a/smtpd/smtpctl.8
+++ b/usr.sbin/smtpd/smtpctl.8
@@ -1,4 +1,4 @@
-.\" $OpenBSD: smtpctl.8,v 1.64 2018/09/18 06:21:45 miko Exp $
+.\" $OpenBSD: smtpctl.8,v 1.65 2020/09/14 09:48:08 martijn Exp $
.\"
.\" Copyright (c) 2006 Pierre-Yves Ritschard <pyr@openbsd.org>
.\" Copyright (c) 2012 Gilles Chehade <gilles@poolp.org>
@@ -15,7 +15,7 @@
.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
.\"
-.Dd $Mdocdate: September 18 2018 $
+.Dd $Mdocdate: September 14 2020 $
.Dt SMTPCTL 8
.Os
.Sh NAME
@@ -247,8 +247,13 @@ Shows if MTA, MDA and SMTP systems are currently running or paused.
Recursively look up SPF records for the domains read from stdin.
For example:
.Bd -literal -offset indent
-# smtpctl spf walk < domains.txt
+$ smtpctl spf walk < domains.txt
.Ed
+.Pp
+SPF records may contain macros which cannot be included in a static list and
+must be resolved dynamically at connection time.
+.Cm spf walk
+cannot provide full results in these cases.
.It Cm trace Ar subsystem
Enables real-time tracing of
.Ar subsystem .
diff --git a/smtpd/smtpctl.c b/usr.sbin/smtpd/smtpctl.c
index 81c6a17a..1cc37994 100644
--- a/smtpd/smtpctl.c
+++ b/usr.sbin/smtpd/smtpctl.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: smtpctl.c,v 1.165 2019/07/23 08:11:10 gilles Exp $ */
+/* $OpenBSD: smtpctl.c,v 1.167 2020/02/24 16:16:07 millert Exp $ */
/*
* Copyright (c) 2013 Eric Faurot <eric@openbsd.org>
@@ -1062,13 +1062,20 @@ int
main(int argc, char **argv)
{
gid_t gid;
+ struct group *gr;
int privileged;
char *argv_mailq[] = { "show", "queue", NULL };
-#ifndef HAVE___PROGNAME
- __progname = ssh_get_progname(argv[0]);
+#ifdef NEED_PROGNAME
+ __progname = get_progname(argv[0]);
#endif
+ /* check that smtpctl was installed setgid */
+ if ((gr = getgrnam(SMTPD_QUEUE_GROUP)) == NULL)
+ errx(1, "unknown group %s", SMTPD_QUEUE_GROUP);
+ else if (gr->gr_gid != getegid())
+ errx(1, "this program must be setgid %s", SMTPD_QUEUE_GROUP);
+
sendmail_compat(argc, argv);
privileged = geteuid() == 0;
@@ -1150,7 +1157,7 @@ sendmail_compat(int argc, char **argv)
*/
for (i = 1; i < argc; i++)
if (strncmp(argv[i], "-bi", 3) == 0)
- exit(makemap(P_NEWALIASES, argc, argv));
+ exit(makemap(P_SENDMAIL, argc, argv));
if (!srv_connect())
offlinefp = offline_file();
diff --git a/smtpd/smtpctl/Makefile b/usr.sbin/smtpd/smtpctl/Makefile
index ef8148be..ef8148be 100644
--- a/smtpd/smtpctl/Makefile
+++ b/usr.sbin/smtpd/smtpctl/Makefile
diff --git a/smtpd/smtpd-api.h b/usr.sbin/smtpd/smtpd-api.h
index f83edd05..f83edd05 100644
--- a/smtpd/smtpd-api.h
+++ b/usr.sbin/smtpd/smtpd-api.h
diff --git a/smtpd/smtpd-defines.h b/usr.sbin/smtpd/smtpd-defines.h
index c5202e34..f22a546f 100644
--- a/smtpd/smtpd-defines.h
+++ b/usr.sbin/smtpd/smtpd-defines.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: smtpd-defines.h,v 1.10 2018/12/27 15:41:50 gilles Exp $ */
+/* $OpenBSD: smtpd-defines.h,v 1.12 2020/02/24 16:16:08 millert Exp $ */
/*
* Copyright (c) 2013 Gilles Chehade <gilles@poolp.org>
@@ -48,6 +48,14 @@
#define PATH_SPOOL "/var/spool/smtpd"
#endif
+#ifndef PATH_MAILLOCAL
+#define PATH_MAILLOCAL PATH_LIBEXEC "/mail.local"
+#endif
+
+#ifndef PATH_MAKEMAP
+#define PATH_MAKEMAP "/usr/sbin/makemap"
+#endif
+
#define SUBADDRESSING_DELIMITER "+"
diff --git a/usr.sbin/smtpd/smtpd-filters.7 b/usr.sbin/smtpd/smtpd-filters.7
new file mode 100644
index 00000000..5af7008e
--- /dev/null
+++ b/usr.sbin/smtpd/smtpd-filters.7
@@ -0,0 +1,653 @@
+.\" $OpenBSD: smtpd-filters.7,v 1.6 2020/04/25 09:44:02 eric Exp $
+.\"
+.\" Copyright (c) 2008 Janne Johansson <jj@openbsd.org>
+.\" Copyright (c) 2009 Jacek Masiulaniec <jacekm@dobremiasto.net>
+.\" Copyright (c) 2012 Gilles Chehade <gilles@poolp.org>
+.\"
+.\" Permission to use, copy, modify, and distribute this software for any
+.\" purpose with or without fee is hereby granted, provided that the above
+.\" copyright notice and this permission notice appear in all copies.
+.\"
+.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+.\"
+.\"
+.Dd $Mdocdate: April 25 2020 $
+.Dt FILTERS 7
+.Os
+.Sh NAME
+.Nm filters
+.Nd filtering API for the
+.Xr smtpd 8
+daemon
+.Sh DESCRIPTION
+The
+.Xr smtpd 8
+daemon provides a Simple Mail Transfer Protocol (SMTP) implementation
+that allows an ordinary machine to become Mail eXchangers (MX).
+Many features that are commonly used by MX,
+such as delivery reporting or Spam filtering,
+are outside the scope of SMTP and too complex to fit in
+.Xr smtpd 8 .
+.Pp
+Because an MX needs to provide these features,
+.Xr smtpd 8
+provides an API to extend its behavior through pluggable
+.Nm .
+.Pp
+At runtime,
+.Xr smtpd 8
+can report events to
+.Nm
+and query what it should answer to these events.
+This allows the decision logic to rely on third-party programs.
+.Sh DESIGN
+The
+.Nm
+are programs that run as unique standalone processes,
+they do not share
+.Xr smtpd 8
+memory space.
+They are executed by
+.Xr smtpd 8
+at startup and expected to run in an infinite loop,
+reading events and filtering requests from
+.Xr stdin 4 ,
+writing responses to
+.Xr stdout 4
+and logging to
+.Xr stderr 4 .
+They are not allowed to terminate.
+.Pp
+Because
+.Nm
+are standalone programs that communicate with
+.Xr smtpd 8
+through
+.Xr fd 4 ,
+they may run as different users than
+.Xr smtpd 8
+and may be written in any language.
+The
+.Nm
+must not use blocking I/O,
+they must support answering asynchronously to
+.Xr smtpd 8 .
+.Sh REPORT AND FILTER
+The API relies on two streams,
+report and filter.
+.Pp
+The report stream is a one-way stream which allows
+.Xr smtpd 8
+to inform
+.Nm
+in real-time about events that are occurring in the daemon.
+The report events do not expect an answer from
+.Nm ,
+it is just meant to provide them with informations.
+A filter should be able to replicate the
+.Xr smtpd 8
+state for a session by gathering informations coming from report events.
+No decision is ever taken by the report stream.
+.Pp
+The filter stream is a two-way stream which allows
+.Xr smtpd 8
+to query
+.Nm
+about what it should do with a session at a given phase.
+The filter requests expects an answer from
+.Nm ,
+.Xr smtpd 8
+will not let the session move forward until then.
+A decision must always be taken by the filter stream.
+.Pp
+It is sometimes possible to rely on filter requests to gather information,
+but because a reponse is expected by
+.Xr smtpd 8 ,
+this is more costly than using report events.
+The correct pattern for writing filters is to use the report events to
+create a local state for a session,
+then use filter requests to take decisions based on this state.
+The only case when using filter request instead of report events is correct,
+is when a decision is required for the filter request and there is no need for
+more information than that of the event.
+.Sh PROTOCOL
+The protocol is straightforward,
+it consists of a human-readable line exchanges between
+.Nm
+and
+.Xr smtpd 8
+through
+.Xr fd 4 .
+.Pp
+The protocol begins with a handshake.
+First,
+.Xr smtpd 8
+provides
+.Nm
+with general configuration information in the form of key-value lines:
+.Bd -literal -offset indent
+config|smtpd-version|6.6.1
+config|smtp-session-timeout|300
+config|subsystem|smtp-in
+config|ready
+.Ed
+.Pp
+Then,
+.Nm
+register the stream,
+subsystem and event they want to handle:
+.Bd -literal -offset indent
+register|report|smtp-in|link-connect
+register|ready
+.Ed
+.Pp
+Finally,
+.Xr smtpd 8
+will emit report events and filter requests,
+expecting
+.Nm
+to react accordingly either by responding or not depending on the stream:
+.Bd -literal -offset indent
+report|0.5|1576146008.006099|smtp-in|link-connect|7641df9771b4ed00|mail.openbsd.org|pass|199.185.178.25:33174|45.77.67.80:25
+report|0.5|1576147242.200225|smtp-in|link-connect|7641dfb3798eb5bf|mail.openbsd.org|pass|199.185.178.25:31205|45.77.67.80:25
+report|0.5|1576148447.982572|smtp-in|link-connect|7641dfc063102cbd|mail.openbsd.org|pass|199.185.178.25:24786|45.77.67.80:25
+.Ed
+.Pp
+The char
+.Dq |
+may only appear in the last field of a payload,
+in which case it should be considered a regular char and not a separator.
+Other fields have strict formatting excluding the possibility of having a
+.Dq | .
+.Pp
+The list of subsystems and events,
+as well as the format of requests and reponses,
+will be documented in the sections below.
+.Sh CONFIGURATION
+During the initial handshake,
+.Xr smtpd 8
+will emit a serie of configuration keys and values.
+The list is meant to be ignored by
+.Nm
+that do not require it and consumed gracefully by filters that do.
+.Pp
+There are currently three keys:
+.Bd -literal -offset indent
+config|smtpd-version|6.6.1
+config|smtp-session-timeout|300
+config|subsystem|smtp-in
+.Ed
+.Pp
+When
+.Xr smtpd 8
+has sent all configuration keys it emits the following line:
+.Bd -literal -offset indent
+config|ready
+.Ed
+.Sh REPORT EVENTS
+There is currently only one subsystem supported in the API:
+smtp-in.
+.Pp
+Each report event is generated by
+.Xr smtpd 8
+as a single line similar to the one below:
+.Bd -literal -offset indent
+report|0.5|1576146008.006099|smtp-in|link-connect|7641df9771b4ed00|mail.openbsd.org|pass|199.185.178.25:33174|45.77.67.80:25
+.Ed
+.Pp
+The format consists of a protocol prefix containing the stream,
+the protocol version,
+the timestamp,
+the subsystem,
+the event and the unique session identifier separated by
+.Dq | :
+.Bd -literal -offset indent
+report|0.5|1576146008.006099|smtp-in|link-connect|7641df9771b4ed00
+.Ed
+.Pp
+It is followed by a suffix containing the event-specific parameters,
+also separated by
+.Dq | :
+.Bd -literal -offset indent
+mail.openbsd.org|pass|199.185.178.25:33174|45.77.67.80:25
+.Ed
+.Pp
+The list of events and event-specific parameters are provided here for smtp-in:
+.Bl -tag -width Ds
+.It Ic link-connect : Ar rdns fcrdns src dest
+This event is generated upon connection.
+.Pp
+.Ar rdns
+contains the reverse DNS hostname for the remote end or an empty string if none.
+.Pp
+.Ar fcrdns
+contains the string
+.Dq pass
+or
+.Dq fail
+depending on if the remote end validates FCrDNS.
+.Pp
+.Ar src
+holds either the IP address and port from source address,
+in the format
+.Dq address:port
+or the path to a UNIX socket in the format
+.Dq unix:/path .
+.Pp
+.Ar dest
+holds either the IP address and port from destination address,
+in the format
+.Dq address:port
+or the path to a UNIX socket in the format
+.Dq unix:/path .
+.It Ic link-greeting : Ar hostname
+This event is generated upon display of the server banner.
+.Pp
+.Ar hostname
+contains the hostname displayed in the banner.
+.It Ic link-identify : Ar method identity
+This event is generated upon
+.Dq HELO
+or
+.Dq EHLO
+command from the client.
+.Pp
+.Ar method
+contains the string
+.Dq HELO
+or
+.Dq EHLO
+indicating the method used by the client.
+.Pp
+.Ar identity
+contains the identity provided by the client.
+.It Ic link-tls : Ar tls-string
+This event is generated upon successful negotiation of TLS.
+.Pp
+.Ar tls-string
+contains a colon-separated list of TLS properties including the TLS version,
+the cipher suite used by the session and the cipher strenght in bits.
+.It Ic link-disconnect
+This event is generated upon disconnection of the client.
+.It Ic link-auth : Ar username result
+This event is generated upon authentication attempt of the client.
+.Pp
+.Ar username
+contains the username used for the authentication attempt.
+.Pp
+.Ar result
+contains the string
+.Dq pass ,
+.Dq fail
+or
+.Dq error
+depending on the result of the authentication attempt.
+.It Ic tx-reset : Op message-id
+This event is generated when a transaction is reset.
+.Pp
+If reset happend while in a transaction,
+.Ar message-id
+contains the identifier of the transaction being reset.
+.It Ic tx-begin : Ar message-id
+This event is generated when a transaction is initiated.
+.Pp
+.Ar message-id
+contains the identifier for the transaction.
+.It Ic tx-mail : Ar message-id Ar result address
+This event is generated when client emits
+.Dq MAIL FROM .
+.Pp
+.Ar message-id
+contains the identifier for the transaction.
+.Pp
+.Ar result
+contains
+.Dq ok
+if the sender was accepted,
+.Dq permfail
+if it was rejected
+or
+.Dq tempfail
+if it was rejected for a transient error.
+.Pp
+.Ar address
+contains the e-mail address of the sender.
+The address is normalized and sanitized,
+the protocol
+.Dq <
+and
+.Dq >
+are removed and so are parameters to
+.Dq MAIL FROM .
+.It Ic tx-rcpt : Ar message-id Ar result address
+This event is generated when client emits
+.Dq RCPT TO .
+.Pp
+.Ar message-id
+contains the identifier for the transaction.
+.Pp
+.Ar result
+contains
+.Dq ok
+if the recipient was accepted,
+.Dq permfail
+if it was rejected
+or
+.Dq tempfail
+if it was rejected for a transient error.
+.Pp
+.Ar address
+contains the e-mail address of the recipient.
+The address is normalized and sanitized,
+the protocol
+.Dq <
+and
+.Dq >
+are removed and so are parameters to
+.Dq RCPT TO .
+.It Ic tx-envelope : Ar message-id Ar envelope-id
+This event is generated when an envelope is accepted.
+.Pp
+.Ar envelope-id
+contains the unique identifier for the envelope.
+.It Ic tx-data : Ar message-id Ar result
+This event is generated when client has emitted
+.Dq DATA .
+.Pp
+.Ar message-id
+contains the unique identifier for the transaction.
+.Pp
+.Ar result
+contains
+.Dq ok
+if server accepted to process the message,
+.Dq permfail
+if it has not accepted and
+.Dq tempfail
+if a transient error is preventing the processing of message.
+.It Ic tx-commit : Ar message-id Ar message-size
+This event is generated when a transaction has been accepted by the server.
+.Pp
+.Ar message-id
+contains the unique identifier for the SMTP transaction.
+.Pp
+.Ar message-size
+contains the size of the message submitted in the
+.Dq DATA
+phase of the SMTP transaction.
+.It Ic tx-rollback : Ar message-id
+This event is generated when a transaction has been rejected by the server.
+.Pp
+.Ar message-id
+contains the unique identifier for the SMTP transaction.
+.It Ic protocol-client : Ar command
+This event is generated for every command submitted by the client.
+It contains the raw command as received by the server.
+.Pp
+.Ar command
+contains the command emitted by the client to the server.
+.It Ic protocol-server : Ar response
+This event is generated for every response emitted by the server.
+It contains the raw response as emitted by the server.
+.Pp
+.Ar response
+contains the response emitted by the server to the client.
+.It Ic filter-report : Ar filter-kind Ar name message
+This event is generated when a filter emits a report.
+.Pp
+.Ar filter-kind may be either
+.Dq builtin
+or
+.Dq proc
+depending on if the filter is an
+.Xr smtpd 8
+builtin filter or a proc filter implementing the API.
+.Pp
+.Ar name
+is the name of the filter that generated the report.
+.Pp
+.Ar message
+is a filter-specific message.
+.It Ic filter-response : Ar phase response Op param
+This event is generated when a filter responds to a filtering request.
+.Pp
+.Ar phase
+contains the phase name for the request.
+The phases are documented in the next section.
+.Pp
+.Ar response
+contains the response of the filter to the request,
+it is either one of
+.Dq proceed ,
+.Dq report ,
+.Dq reject ,
+.Dq disconnect ,
+.Dq junk or
+.Dq rewrite .
+.Pp
+If specified,
+.Ar param
+is the parameter to the response.
+.It Ic timeout
+This event is generated when a timeout happens for a session.
+.El
+.Sh FILTER REQUESTS
+There is currently only one subsystem supported in the API:
+smtp-in.
+.Pp
+The filter requests allow
+.Xr smtpd 8
+to query
+.Nm
+about what to do with a session at a particular phase.
+In addition,
+they allow
+.Nm
+to alter the content of a message by adding,
+modifying,
+or suppressing lines of input in a way that is similar to what program like
+.Xr sed 1
+or
+.Xr grep 1
+would do.
+.Pp
+Each filter request is generated by
+.Xr smtpd 8
+as a single line similar to the one below:
+.Bd -literal -offset indent
+filter|0.5|1576146008.006099|smtp-in|connect|7641df9771b4ed00|1ef1c203cc576e5d|mail.openbsd.org|pass|199.185.178.25:33174|45.77.67.80:25
+.Ed
+.Pp
+The format consists of a protocol prefix containing the stream,
+the protocol version,
+the timestamp,
+the subsystem,
+the filtering phase,
+the unique session identifier and an opaque token separated by
+.Dq |
+that the filter should provide in its response:
+.Bd -literal -offset indent
+filter|0.5|1576146008.006099|smtp-in|connect|7641df9771b4ed00|1ef1c203cc576e5d
+.Ed
+.Pp
+It is followed by a suffix containing the phase-specific parameters to the
+filter request,
+also separated by
+.Dq | :
+.Bd -literal -offset indent
+mail.openbsd.org|pass|199.185.178.25:33174|45.77.67.80:25
+.Ed
+.Pp
+Unlike with report events,
+.Xr smtpd 8
+expects answers from filter requests and will not allow a session to move
+forward before the filter has instructed
+.Xr smtpd 8
+what to do with it.
+.Pp
+For all phases,
+excepted
+.Dq data-line ,
+the responses must follow the same construct,
+a message type
+.Dq filter-result ,
+followed by the unique session id,
+the opaque token,
+a decision and optional decision-specific parameters:
+.Bd -literal -offset indent
+filter-result|7641df9771b4ed00|1ef1c203cc576e5d|proceed
+filter-result|7641df9771b4ed00|1ef1c203cc576e5d|reject|550 nope
+.Ed
+.Pp
+The possible decisions to a
+.Dq filter-result
+message will be described below.
+.Pp
+For the
+.Dq data-line
+phase,
+.Nm
+are fed with a stream of lines corresponding to the message to filter,
+and terminated by a single dot:
+.Bd -literal -offset indent
+filter|0.5|1576146008.006099|smtp-in|data-line|7641df9771b4ed00|1ef1c203cc576e5d|line 1
+filter|0.5|1576146008.006103|smtp-in|data-line|7641df9771b4ed00|1ef1c203cc576e5d|line 2
+filter|0.5|1576146008.006105|smtp-in|data-line|7641df9771b4ed00|1ef1c203cc576e5d|.
+.Ed
+.Pp
+They are expected to produce an output stream similarly terminate by a single
+dot.
+A filter may inject,
+suppress,
+modify or echo back the lines it receives.
+Ultimately,
+.Xr smtpd 8
+will assume that the message consists of the output from
+.Nm .
+.Pp
+Note that filters may be chained and the lines that are input into a filter
+are the lines that are output from previous filter.
+.Pp
+The response to
+.Dq data-line
+requests use their own construct.
+A
+.Dq filter-dataline
+prefix,
+followed by the unique session identifier,
+the opaque token and the output line as follows:
+.Bd -literal -offset indent
+filter-dataline|7641df9771b4ed00|1ef1c203cc576e5d|line 1
+filter-dataline|7641df9771b4ed00|1ef1c203cc576e5d|line 2
+filter-dataline|7641df9771b4ed00|1ef1c203cc576e5d|.
+.Ed
+.Pp
+The list of events and event-specific parameters are provided here for smtp-in:
+.Bl -tag -width Ds
+.It Ic connect : Ar rdns fcrdns src dest
+This request is emitted after connection,
+before the banner is displayed.
+.It Ic helo : Ar identity
+This request is emitted after the client has emitted
+.Dq HELO .
+.It Ic ehlo : Ar identity
+This request is emitted after the client has emitted
+.Dq EHLO .
+.It Ic starttls : Ar tls-string
+This request is emitted after the client has requested
+.Dq STARTTLS .
+.It Ic auth : Ar auth
+This request is emitted after the client has requested
+.Dq AUTH .
+.It Ic mail-from : Ar address
+This request is emitted after the client has requested
+.Dq MAIL FROM .
+.It Ic rcpt-to : Ar address
+This request is emitted after the client has requested
+.Dq RCPT TO .
+.It Ic data
+This request is emitted after the client has requested
+.Dq DATA .
+.It Ic data-line : Ar line
+This request is emitted for each line of input in the
+.Dq DATA
+phase.
+The lines are raw dot-escaped SMTP DATA input,
+terminated with a single dot.
+.It Ic commit
+This request is emitted after the final single dot is received.
+.El
+.Pp
+For every filtering phase,
+excepted
+.Dq data-line ,
+the following decisions may be taken by a filter:
+.Bl -tag -width Ds
+.It Ic proceed
+No action is taken,
+session or transaction may be passed to the next filter.
+.It Ic junk
+The session or transaction is marked as Spam.
+.Xr smtpd 8
+will prepend a
+.Dq X-Spam
+header to the message.
+.It Ic reject Ar error
+The command is rejected with the message
+.Ar error .
+The message must be a valid SMTP message including status code,
+5xx or 4xx.
+.Pp
+Messages starting with a 5xx status result in a permanent failure,
+those starting with a 4xx status result in a temporary failure.
+.Pp
+Messages starting with a 421 status will result in a client disconnect.
+.It Ic disconnect Ar error
+The client is disconnected with the message
+.Ar error .
+The message must be a valid SMTP message including status code,
+5xx or 4xx.
+.Pp
+Messages starting with a 5xx status result in a permanent failure,
+those starting with a 4xx status result in a temporary failure.
+.It Ic rewrite Ar parameter
+The command parameter is rewritten.
+.Pp
+This decision allows a filter to perform a rewrite of client-submitted
+commands before they are processed by the SMTP engine.
+.Ar parameter
+is expected to be a valid SMTP parameter for the command.
+.It Ic report Ar parameter
+Generates a report with
+.Ar parameter
+for this filter.
+.El
+.\".Sh EXAMPLES
+.\"This example filter written in
+.\".Xr sh 1
+.\"will echo back...
+.\".Bd -literal -offset indent
+.\"XXX
+.\".Ed
+.\".Pp
+.\"This example filter will filter...
+.\".Bd -literal -offset indent
+.\"XXX
+.\".Ed
+.\".Pp
+.\"Note that libraries may provide a simpler interface to
+.\".Nm
+.\"that does not require implementing the protocol itself.
+.\".Ed
+.Sh SEE ALSO
+.Xr smtpd 8
+.Sh HISTORY
+.Nm
+first appeared in
+.Ox 6.6 .
diff --git a/smtpd/smtpd.8 b/usr.sbin/smtpd/smtpd.8
index e3429f07..e3429f07 100644
--- a/smtpd/smtpd.8
+++ b/usr.sbin/smtpd/smtpd.8
diff --git a/smtpd/smtpd.c b/usr.sbin/smtpd/smtpd.c
index 662e103a..9307fc3b 100644
--- a/smtpd/smtpd.c
+++ b/usr.sbin/smtpd/smtpd.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: smtpd.c,v 1.325 2019/09/03 04:48:20 martijn Exp $ */
+/* $OpenBSD: smtpd.c,v 1.335 2020/09/23 19:11:50 martijn Exp $ */
/*
* Copyright (c) 2008 Gilles Chehade <gilles@poolp.org>
@@ -80,6 +80,7 @@
#include <util.h>
#endif
+#include <openssl/rand.h>
#include <openssl/ssl.h>
#include <openssl/evp.h>
@@ -112,17 +113,17 @@ static struct mproc *setup_peer(enum smtp_proc_type, pid_t, int);
static int imsg_wait(struct imsgbuf *, struct imsg *, int);
static void offline_scan(int, short, void *);
-static int offline_add(char *);
+static int offline_add(char *, uid_t, gid_t);
static void offline_done(void);
-static int offline_enqueue(char *);
+static int offline_enqueue(char *, uid_t, gid_t);
static void purge_task(void);
static int parent_auth_user(const char *, const char *);
static void load_pki_tree(void);
static void load_pki_keys(void);
-static void fork_processors(void);
-static void fork_processor(const char *, const char *, const char *, const char *, const char *);
+static void fork_filter_processes(void);
+static void fork_filter_process(const char *, const char *, const char *, const char *, const char *, uint32_t);
enum child_type {
CHILD_DAEMON,
@@ -143,6 +144,8 @@ struct child {
struct offline {
TAILQ_ENTRY(offline) entry;
+ uid_t uid;
+ gid_t gid;
char *path;
};
@@ -191,7 +194,7 @@ static void
parent_imsg(struct mproc *p, struct imsg *imsg)
{
struct forward_req *fwreq;
- struct processor *processor;
+ struct filter_proc *processor;
struct deliver deliver;
struct child *c;
struct msg m;
@@ -295,7 +298,7 @@ parent_imsg(struct mproc *p, struct imsg *imsg)
m_get_string(&m, &procname);
m_end(&m);
- processor = dict_xget(env->sc_processors_dict, procname);
+ processor = dict_xget(env->sc_filter_processes_dict, procname);
m_create(p_lka, IMSG_LKA_PROCESSOR_ERRFD, 0, 0, processor->errfd);
m_add_string(p_lka, procname);
m_close(p_lka);
@@ -517,11 +520,11 @@ main(int argc, char *argv[])
char *rexec = NULL;
struct smtpd *conf;
-#ifndef HAVE___PROGNAME
- __progname = ssh_get_progname(argv[0]);
+#ifdef NEED_PROGNAME
+ __progname = get_progname(argv[0]);
#endif
+ __progname = xstrdup(__progname);
-#ifndef HAVE_SETPROCTITLE
/* Save argv. Duplicate so setproctitle emulation doesn't clobber it */
saved_argc = argc;
saved_argv = xcalloc(argc + 1, sizeof(*saved_argv));
@@ -529,14 +532,15 @@ main(int argc, char *argv[])
saved_argv[i] = xstrdup(argv[i]);
saved_argv[i] = NULL;
+#ifdef NEED_SETPROCTITLE
/* Prepare for later setproctitle emulation */
compat_init_setproctitle(argc, argv);
argv = saved_argv;
+#endif
/* this is to work around GNU getopt + portable setproctitle() fuckery */
save_argc = saved_argc;
save_argv = saved_argv;
-#endif
if ((conf = config_default()) == NULL)
err(1, NULL);
@@ -1140,13 +1144,13 @@ smtpd(void) {
if (pidfile(NULL) < 0)
err(1, "pidfile");
- fork_processors();
+ fork_filter_processes();
purge_task();
#if HAVE_PLEDGE
if (pledge("stdio rpath wpath cpath fattr tmppath "
- "getpw sendfd proc exec id inet unix", NULL) == -1)
+ "getpw sendfd proc exec id inet chown unix", NULL) == -1)
err(1, "pledge");
#endif
@@ -1319,22 +1323,46 @@ purge_task(void)
}
static void
-fork_processors(void)
+fork_filter_processes(void)
{
const char *name;
- struct processor *processor;
void *iter;
+ const char *fn;
+ struct filter_config *fc;
+ struct filter_config *fcs;
+ struct filter_proc *fp;
+ size_t i;
+
+ /* For each filter chain, assign the registered subsystem to subfilters */
+ iter = NULL;
+ while (dict_iter(env->sc_filters_dict, &iter, (const char **)&fn, (void **)&fc)) {
+ if (fc->chain) {
+ for (i = 0; i < fc->chain_size; ++i) {
+ fcs = dict_xget(env->sc_filters_dict, fc->chain[i]);
+ fcs->filter_subsystem |= fc->filter_subsystem;
+ }
+ }
+ }
+
+ /* For each filter, assign the registered subsystem to underlying proc */
+ iter = NULL;
+ while (dict_iter(env->sc_filters_dict, &iter, (const char **)&fn, (void **)&fc)) {
+ if (fc->proc) {
+ fp = dict_xget(env->sc_filter_processes_dict, fc->proc);
+ fp->filter_subsystem |= fc->filter_subsystem;
+ }
+ }
iter = NULL;
- while (dict_iter(env->sc_processors_dict, &iter, &name, (void **)&processor))
- fork_processor(name, processor->command, processor->user, processor->group, processor->chroot);
+ while (dict_iter(env->sc_filter_processes_dict, &iter, &name, (void **)&fp))
+ fork_filter_process(name, fp->command, fp->user, fp->group, fp->chroot, fp->filter_subsystem);
}
static void
-fork_processor(const char *name, const char *command, const char *user, const char *group, const char *chroot_path)
+fork_filter_process(const char *name, const char *command, const char *user, const char *group, const char *chroot_path, uint32_t subsystems)
{
pid_t pid;
- struct processor *processor;
+ struct filter_proc *processor;
char buf;
int sp[2], errfd[2];
struct passwd *pw;
@@ -1366,13 +1394,14 @@ fork_processor(const char *name, const char *command, const char *user, const ch
/* parent passes the child fd over to lka */
if (pid > 0) {
- processor = dict_xget(env->sc_processors_dict, name);
+ processor = dict_xget(env->sc_filter_processes_dict, name);
processor->errfd = errfd[1];
child_add(pid, CHILD_PROCESSOR, name);
close(sp[0]);
close(errfd[0]);
m_create(p_lka, IMSG_LKA_PROCESSOR_FORK, 0, 0, sp[1]);
m_add_string(p_lka, name);
+ m_add_u32(p_lka, (uint32_t)subsystems);
m_close(p_lka);
return;
}
@@ -1391,7 +1420,7 @@ fork_processor(const char *name, const char *command, const char *user, const ch
if (setgroups(1, &gr->gr_gid) ||
setresgid(gr->gr_gid, gr->gr_gid, gr->gr_gid) ||
setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid))
- err(1, "fork_processor: cannot drop privileges");
+ err(1, "fork_filter_process: cannot drop privileges");
xclosefrom(STDERR_FILENO + 1);
@@ -1444,6 +1473,8 @@ forkmda(struct mproc *p, uint64_t id, struct deliver *deliver)
const char *pw_dir;
dsp = dict_xget(env->sc_dispatchers, deliver->dispatcher);
+ if (dsp->type != DISPATCHER_LOCAL)
+ fatalx("non-local dispatcher called from forkmda()");
log_debug("debug: smtpd: forking mda for session %016"PRIx64
": %s as %s", id, deliver->userinfo.username,
@@ -1481,7 +1512,7 @@ forkmda(struct mproc *p, uint64_t id, struct deliver *deliver)
pw_dir = deliver->userinfo.directory;
}
- if (pw_uid == 0 && !dsp->u.local.requires_root) {
+ if (pw_uid == 0 && !dsp->u.local.is_mbox) {
(void)snprintf(ebuf, sizeof ebuf, "not allowed to deliver to: %s",
deliver->userinfo.username);
m_create(p_pony, IMSG_MDA_DONE, 0, 0, -1);
@@ -1547,6 +1578,11 @@ forkmda(struct mproc *p, uint64_t id, struct deliver *deliver)
m_close(p);
return;
}
+
+ /* mbox helper, create mailbox before privdrop if it doesn't exist */
+ if (dsp->u.local.is_mbox)
+ mda_mbox_init(deliver);
+
if (chdir(pw_dir) == -1 && chdir("/") == -1)
err(1, "chdir");
if (setgroups(1, &pw_gid) ||
@@ -1570,7 +1606,12 @@ forkmda(struct mproc *p, uint64_t id, struct deliver *deliver)
/* avoid hangs by setting 5m timeout */
alarm(300);
- mda_unpriv(dsp, deliver, pw_name, pw_dir);
+ if (dsp->u.local.is_mbox &&
+ dsp->u.local.mda_wrapper == NULL &&
+ deliver->mda_exec[0] == '\0')
+ mda_mbox(deliver);
+ else
+ mda_unpriv(dsp, deliver, pw_name, pw_dir);
}
static void
@@ -1611,7 +1652,8 @@ offline_scan(int fd, short ev, void *arg)
continue;
}
- if (offline_add(e->fts_name)) {
+ if (offline_add(e->fts_name, e->fts_statp->st_uid,
+ e->fts_statp->st_gid)) {
log_warnx("warn: smtpd: "
"could not add offline message %s", e->fts_name);
continue;
@@ -1631,7 +1673,7 @@ offline_scan(int fd, short ev, void *arg)
}
static int
-offline_enqueue(char *name)
+offline_enqueue(char *name, uid_t uid, gid_t gid)
{
char *path;
struct stat sb;
@@ -1693,6 +1735,18 @@ offline_enqueue(char *name)
_exit(1);
}
+ if (sb.st_uid != uid) {
+ log_warnx("warn: smtpd: file %s has bad uid %d",
+ path, sb.st_uid);
+ _exit(1);
+ }
+
+ if (sb.st_gid != gid) {
+ log_warnx("warn: smtpd: file %s has bad gid %d",
+ path, sb.st_gid);
+ _exit(1);
+ }
+
pw = getpwuid(sb.st_uid);
if (pw == NULL) {
log_warnx("warn: smtpd: getpwuid for uid %d failed",
@@ -1749,17 +1803,19 @@ offline_enqueue(char *name)
}
static int
-offline_add(char *path)
+offline_add(char *path, uid_t uid, gid_t gid)
{
struct offline *q;
if (offline_running < OFFLINE_QUEUEMAX)
/* skip queue */
- return offline_enqueue(path);
+ return offline_enqueue(path, uid, gid);
q = malloc(sizeof(*q) + strlen(path) + 1);
if (q == NULL)
return (-1);
+ q->uid = uid;
+ q->gid = gid;
q->path = (char *)q + sizeof(*q);
memmove(q->path, path, strlen(path) + 1);
TAILQ_INSERT_TAIL(&offline_q, q, entry);
@@ -1778,7 +1834,7 @@ offline_done(void)
if ((q = TAILQ_FIRST(&offline_q)) == NULL)
break; /* all done */
TAILQ_REMOVE(&offline_q, q, entry);
- offline_enqueue(q->path);
+ offline_enqueue(q->path, q->uid, q->gid);
free(q);
}
}
@@ -2109,6 +2165,9 @@ imsg_to_str(int type)
CASE(IMSG_REPORT_SMTP_LINK_CONNECT);
CASE(IMSG_REPORT_SMTP_LINK_DISCONNECT);
CASE(IMSG_REPORT_SMTP_LINK_TLS);
+ CASE(IMSG_REPORT_SMTP_LINK_GREETING);
+ CASE(IMSG_REPORT_SMTP_LINK_IDENTIFY);
+ CASE(IMSG_REPORT_SMTP_LINK_AUTH);
CASE(IMSG_REPORT_SMTP_TX_RESET);
CASE(IMSG_REPORT_SMTP_TX_BEGIN);
diff --git a/smtpd/smtpd.conf b/usr.sbin/smtpd/smtpd.conf
index efecf2a6..a7ba6c64 100644
--- a/smtpd/smtpd.conf
+++ b/usr.sbin/smtpd/smtpd.conf
@@ -9,7 +9,7 @@ table aliases file:/etc/mail/aliases
#
listen on localhost
-action "local" mbox alias <aliases>
+action "local" maildir alias <aliases>
action "relay" relay
# Uncomment the following to accept external mail for domain "example.org"
diff --git a/smtpd/smtpd.conf.5 b/usr.sbin/smtpd/smtpd.conf.5
index 580d5838..36207c39 100644
--- a/smtpd/smtpd.conf.5
+++ b/usr.sbin/smtpd/smtpd.conf.5
@@ -1,4 +1,4 @@
-.\" $OpenBSD: smtpd.conf.5,v 1.226 2019/09/20 18:47:23 jmc Exp $
+.\" $OpenBSD: smtpd.conf.5,v 1.255 2020/09/23 19:11:50 martijn Exp $
.\"
.\" Copyright (c) 2008 Janne Johansson <jj@openbsd.org>
.\" Copyright (c) 2009 Jacek Masiulaniec <jacekm@dobremiasto.net>
@@ -17,7 +17,7 @@
.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
.\"
.\"
-.Dd $Mdocdate: September 20 2019 $
+.Dd $Mdocdate: September 23 2020 $
.Dt SMTPD.CONF 5
.Os
.Sh NAME
@@ -141,8 +141,14 @@ may contain format specifiers that are expanded before use
.Pp
If the
.Cm junk
-argument is provided, the message will be moved to the Junk
-folder if it contains a positive X-Spam header.
+argument is provided, the message will be moved to the
+.Ql Junk
+folder if it contains a positive
+.Ql X-Spam
+header.
+This folder will be created under
+.Ar pathname
+if it does not yet exist.
.It Cm mbox
Deliver the message to the user's mbox with
.Xr mail.local 8 .
@@ -228,6 +234,10 @@ Use the mapping
.Ar table
to look up a hostname matching the source address,
to advertise during the HELO phase.
+.It Cm domain Pf < Ar domains Ns >
+Do not perform MX lookups but look up destination domain in
+.Ar domains
+and use matching relay url as relay host.
.It Cm host Ar relay-url
Do not perform MX lookups but relay messages to the relay host described by
.Ar relay-url .
@@ -270,6 +280,14 @@ and
.Dq smtps
protocols for authentication.
Server certificates for those protocols are verified by default.
+.It Cm pki Ar pkiname
+For secure connections,
+use the certificate associated with
+.Ar pkiname
+(declared in a
+.Ic pki
+directive)
+to prove the client's identity to the remote mail server.
.It Cm srs
When relaying a mail resulting from a forward,
use the Sender Rewriting Scheme to rewrite sender address.
@@ -295,13 +313,19 @@ The credential table format is described in
Use
.Ar mailaddr
as the MAIL FROM address within the SMTP transaction.
-.It Cm src Ar address | Pf < Ar address Ns >
+.It Cm src Ar sourceaddr | Pf < Ar sourceaddr Ns >
Use the string or list table
-.Ar address
-for the source IP address.
+.Ar sourceaddr
+for the source IP address,
+which is useful on machines with multiple interfaces.
If the list contains more than one address, all of them are used
in such a way that traffic is routed as efficiently as possible.
.El
+.It Ic admd Ar authservid
+The Administrative Management Domain this mailserver belongs to.
+The authservid will be forwarded to filters using it to identify or mark
+authentication-results headers.
+If omitted it defaults to the server name.
.It Ic bounce Cm warn-interval Ar delay Op , Ar delay ...
Send warning messages to the envelope sender when temporary delivery
failures cause a message to remain on the queue for longer than
@@ -333,64 +357,26 @@ or using the
.Ic hostname
directive.
.It Ic filter Ar chain-name Ic chain Brq Ar filter-name Op , Ar ...
-Register a chain of filters named
-.Ar chain-name
-and consisting of the filters listed from
+Register a chain of filters
+.Ar chain-name ,
+consisting of the filters listed from
.Ar filter-name .
Filters part of a filter chain are executed in order of declaration for
each phase that they are registered for.
A filter chain may be used in place of a filter for any directive but
filter chains themselves.
-.It Ic filter Ar filter-name Ic phase Ar phase-name Ic match Ar conditions Ic disconnect Ar message
-Register builtin filter
-.Ar filter-name
-matching
-.Ar conditions
-to disconnect session with
-.Ar message .
-Phase and matching conditions are documented in a specific section,
-see
-.Sx BUILTIN FILTERING .
-.It Ic filter Ar filter-name Ic phase Ar phase-name Ic match Ar conditions Ic junk
-Register builtin filter
-.Ar filter-name
-matching
-.Ar conditions
-to mark a session or a transaction as junk.
-Phase and matching conditions are documented in a specific section,
-see
-.Sx BUILTIN FILTERING .
-.It Ic filter Ar filter-name Ic phase Ar phase-name Ic match Ar conditions Ic reject Ar message
-Register builtin filter
-.Ar filter-name
-matching
-.Ar conditions
-to reject session with
-.Ar message .
-Phase and matching conditions are documented in a specific section,
-see
-.Sx BUILTIN FILTERING .
-.It Ic filter Ar filter-name Ic phase Ar phase-name Ic match Ar conditions Ic report Ar message
-Register builtin filter
-.Ar filter-name
-matching
-.Ar conditions
-to report on session with
-.Ar message
-and proceed with the transaction.
-Phase and matching conditions are documented in a specific section,
-see
-.Sx BUILTIN FILTERING .
-.It Ic filter Ar filter-name Ic phase Ar phase-name Ic match Ar conditions Ic rewrite Ar value
-Register builtin filter
-.Ar filter-name
-matching
-.Ar conditions
-to rewrite phase parameter with new
-.Ar value .
-Phase and matching conditions are documented in a specific section,
-see
-.Sx BUILTIN FILTERING .
+.It Ic filter Ar filter-name Ic phase Ar phase-name Ic match Ar conditions decision
+Register a filter
+.Ar filter-name .
+A
+.Ar decision
+about what to do with the mail is taken at phase
+.Ar phase-name
+when matching
+.Ar conditions .
+Phases, matching conditions, and decisions are described in
+.Sx MAIL FILTERING ,
+below.
.It Ic filter Ar filter-name Ic proc Ar proc-name
Register
.Qq proc
@@ -536,17 +522,29 @@ With the
option, clients must also provide a valid certificate
to establish an SMTP session.
.El
-.It Ic listen on Cm socket Op Cm mask-src
+.It Ic listen on Cm socket Op Ar options
Listen for incoming SMTP connections on the Unix domain socket
.Pa /var/run/smtpd.sock .
This is done by default, even if the directive is absent.
-If the
-.Cm mask-src
-option is specified, printing of the HELO name, hostname, and IP
-address of the originating host is suppressed in Received: header lines.
-.\" XXX The option
-.\" Cm filter Ar string
-.\" is parsed, but not implemented, see smtpf_session.c.
+.Pp
+The
+.Ar options
+are as follows:
+.Bl -tag -width Ds
+.It Ic filter Ar name
+Apply filter
+.Ar name
+on connections handled by this listener.
+.It Cm mask-src
+Omit the
+.Sy from
+part when prepending
+.Dq Received
+headers.
+.It Cm tag Ar tag
+Clients connecting to the listener are tagged with the given
+.Ar tag .
+.El
.It Ic match Ar options Cm action Ar name
If at least one mail envelope matches the
.Ar options
@@ -586,11 +584,47 @@ Specify that session may address the regex or regex table
.Ar domain .
.It Xo
.Op Ic \&!
+.Cm for rcpt-to
+.Ar recipient | Pf < Ar recipient Ns >
+.Xc
+Specify that session may address the string or list table
+.Ar recipient .
+.It Xo
+.Op Ic \&!
+.Cm for rcpt-to regex
+.Ar recipient | Pf < Ar recipient Ns >
+.Xc
+Specify that session may address the regex or regex table
+.Ar recipient .
+.It Xo
+.Op Ic \&!
.Cm from any
.Xc
Specify that session may originate from any source.
.It Xo
.Op Ic \&!
+.Cm from auth
+.Xc
+Specify that session may originate from any authenticated user,
+no matter the source IP address.
+.It Xo
+.Op Ic \&!
+.Cm from auth
+.Ar user | Pf < Ar user Ns >
+.Xc
+Specify that session may originate from authenticated user or user list
+.Ar user ,
+no matter the source IP address.
+.It Xo
+.Op Ic \&!
+.Cm from auth regex
+.Ar user | Pf < Ar user Ns >
+.Xc
+Specify that session may originate from authenticated regex or regex list
+.Ar user ,
+no matter the source IP address.
+.It Xo
+.Op Ic \&!
.Cm from local
.Xc
Specify that session may only originate from a local IP address,
@@ -598,6 +632,22 @@ or from the local enqueuer.
This is the default, and may be omitted.
.It Xo
.Op Ic \&!
+.Cm from mail-from
+.Ar sender | Pf < Ar sender Ns >
+.Xc
+Specify that session may originate from sender or sender list
+.Ar sender ,
+no matter the source IP address.
+.It Xo
+.Op Ic \&!
+.Cm from mail-from regex
+.Ar sender | Pf < Ar sender Ns >
+.Xc
+Specify that session may originate from regex or regex list
+.Ar sender ,
+no matter the source IP address.
+.It Xo
+.Op Ic \&!
.Cm from rdns
.Xc
Specify that session may only originate from an IP address that
@@ -650,6 +700,20 @@ In addition, the following transaction options:
Matches transactions which have been authenticated.
.It Xo
.Op Ic \&!
+.Cm auth
+.Ar username | Pf < Ar username Ns >
+.Xc
+Matches transactions which have been authenticated for user or user list
+.Ar username .
+.It Xo
+.Op Ic \&!
+.Cm auth regex
+.Ar username | Pf < Ar username Ns >
+.Xc
+Matches transactions which have been authenticated for regex or regex list
+.Ar username .
+.It Xo
+.Op Ic \&!
.Cm helo
.Ar helo-name | Pf < Ar helo-name Ns >
.Xc
@@ -897,7 +961,7 @@ pairs.
The table must contain at least one key-value pair and may declare
multiple pairs as a comma-separated (whitespace optional) list.
.El
-.Ss BUILTIN FILTERING
+.Ss MAIL FILTERING
In a regular workflow,
.Xr smtpd 8
may accept or reject a message based only on the content of envelopes.
@@ -913,7 +977,7 @@ then decide if a session is allowed to move forward.
With filtering,
a session may be interrupted at any phase before an envelope is complete.
A message may also be rejected after being submitted,
-disregarding if the envelope was accepted or not.
+regardless of whether the envelope was accepted or not.
.Pp
The following phases are currently supported:
.Bl -column mail-from -offset indent
@@ -926,37 +990,39 @@ The following phases are currently supported:
.It commit Ta after message is fully is submitted
.El
.Pp
-At each phase,
-multiple criterias may be checked:
+At each phase, various conditions may be matched.
+The fcrdns, rdns, and src data are available in all phases,
+but other data must have been already submitted before they are available.
.Bl -column XXXXXXXXXXXXXXXXXXXXX -offset indent
.It fcrdns Ta forward-confirmed reverse DNS is valid
.It rdns Ta session has a reverse DNS
.It rdns Pf < Ar table Ns > Ta session has a reverse DNS in table
.It src Pf < Ar table Ns > Ta source address is in table
.It helo Pf < Ar table Ns > Ta helo name is in table
-.It mail-from Pf < Ar table Ns > Ta sender address is in table
-.It rcpt-to Pf < Ar table Ns > Ta recipient address is in table
+.It auth Ta session is authenticated
+.It auth Pf < Ar table Ns > Ta session username is in table
+.It mail-from Pf < Ar table Ns > Ta sender address is in table
+.It rcpt-to Pf < Ar table Ns > Ta recipient address is in table
.El
.Pp
-All criterias from previous phases are available to subsequent phases,
-so while the helo criteria is not available before the helo or ehlo phase,
-the fcrdns criteria is available in all phases.
-.Pp
-Criterias may all be negated by prefixing them with an exclamation mark:
+These conditions may all be negated by prefixing them with an exclamation mark:
.Bl -column XXXXXXXXXXXXXXXXXXXXX -offset indent
-.It ! fcrdns Ta forward-confirmed reverse DNS is invalid
+.It !fcrdns Ta forward-confirmed reverse DNS is invalid
.El
.Pp
-Any criteria using a table may indicate that tables hold regex by
+Any conditions using a table may indicate that tables hold regex by
prefixing the table name with the keyword regex.
.Bl -column XXXXXXXXXXXXXXXXXXXXX -offset indent
.It helo regex Pf < Ar table Ns > Ta helo name matches a regex in table
.El
-Finally,
-four decisions may be taken:
+.Pp
+Finally, a number of decisions may be taken:
.Bl -column XXXXXXXXXXXXXXXXXXXXX -offset indent
+.It bypass Ta the session or transaction bypasses filters
.It disconnect Ar message Ta the session is disconnected with message
-.It junk Ta the session or transaction is junked
+.It junk Ta the session or transaction is junked, i.e., an
+.Ql X-Spam: yes
+header is added to any messages
.It reject Ar message Ta the command is rejected with message
.It rewrite Ar value Ta the command parameter is rewritten with value
.El
@@ -964,7 +1030,7 @@ four decisions may be taken:
Decisions that involve a message require that the message be RFC valid,
meaning that they should either start with a 4xx or 5xx status code.
Descisions can be taken at any phase,
-however junking can only happen before a message is committed.
+though junking can only happen before a message is committed.
.Ss FORMAT SPECIFIERS
Some configuration directives support expansion of their parameters at runtime.
Such directives (for example
@@ -1076,8 +1142,8 @@ action "local_mail" mbox alias <aliases>
action "outbound" relay host smtp+tls://bob@smtp.example.com \e
auth <secrets>
-match for local action "local_mail"
-match for any action "outbound"
+match from local for local action "local_mail"
+match from local for any action "outbound"
.Ed
.Pp
In this second example,
@@ -1117,26 +1183,40 @@ match for any action "outbound"
match auth from any for any action "outbound"
.Ed
.Pp
-For sites that wish to sign messages using DKIM, the
-.Sy dkimproxy
-package may be used as a filter.
-The following example is the same as the default configuration,
-but all outgoing mail is passed to dkimproxy_out on port 10027
-for signing.
-The signed messages are received on port 10028 and tagged for relaying.
+For sites that wish to sign messages using DKIM,
+the following example uses
+.Sy opensmtpd-filter-dkimsign
+for DKIM signing:
.Bd -literal -offset indent
table aliases file:/etc/mail/aliases
-listen on lo0
-listen on lo0 port 10028 tag DKIM
+filter "dkimsign" proc-exec "filter-dkimsign -d <domain> -s <selector> \e
+ -k /etc/mail/dkim/private.key" user _dkimsign group _dkimsign
+
+listen on socket filter "dkimsign"
+listen on lo0 filter "dkimsign"
action "local_mail" mbox alias <aliases>
action "outbound" relay
-action "relay_dkim" relay host smtp://127.0.0.1:10027
match for local action "local_mail"
-match tag DKIM for any action "outbound"
-match for any action "relay_dkim"
+match for any action "outbound"
+.Ed
+.Pp
+Alternatively, the
+.Sy opensmtpd-filter-rspamd
+package may be used to provide integration with
+.Sy rspamd ,
+a third-party daemon which provides multiple antispam features
+as well as DKIM signing.
+As well as configuring
+.Sy rspamd
+itself,
+it requires use of the
+.Cm proc-exec
+keyword:
+.Bd -literal -offset indent
+filter "rspamd" proc-exec "filter-rspamd"
.Ed
.Pp
Sites that accept non-local messages may be able to cut down on the
diff --git a/smtpd/smtpd.h b/usr.sbin/smtpd/smtpd.h
index e81da539..beb1d7b4 100644
--- a/smtpd/smtpd.h
+++ b/usr.sbin/smtpd/smtpd.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: smtpd.h,v 1.642 2019/11/03 23:58:51 gilles Exp $ */
+/* $OpenBSD: smtpd.h,v 1.659 2020/09/23 19:11:50 martijn Exp $ */
/*
* Copyright (c) 2008 Gilles Chehade <gilles@poolp.org>
@@ -73,7 +73,7 @@
#ifndef SMTPD_NAME
#define SMTPD_NAME "OpenSMTPD"
#endif
-#define SMTPD_VERSION "6.6.1-portable"
+#define SMTPD_VERSION "6.7.0-portable"
#define SMTPD_SESSION_TIMEOUT 300
#define SMTPD_BACKLOG 5
@@ -124,8 +124,9 @@
#define MTA_EXT_DSN 0x400
-#define P_NEWALIASES 0
-#define P_MAKEMAP 1
+#define P_SENDMAIL 0
+#define P_NEWALIASES 1
+#define P_MAKEMAP 2
#define CERT_ERROR -1
#define CERT_OK 0
@@ -515,6 +516,7 @@ struct envelope {
char smtpname[HOST_NAME_MAX+1];
char helo[HOST_NAME_MAX+1];
char hostname[HOST_NAME_MAX+1];
+ char username[SMTPD_MAXMAILADDRSIZE];
char errorline[LINE_MAX];
struct sockaddr_storage ss;
@@ -610,7 +612,7 @@ struct smtpd {
size_t sc_scheduler_max_msg_batch_size;
size_t sc_scheduler_max_schedule;
- struct dict *sc_processors_dict;
+ struct dict *sc_filter_processes_dict;
int sc_ttl;
#define MAX_BOUNCE_WARN 4
@@ -648,6 +650,8 @@ struct smtpd {
char *sc_srs_key;
char *sc_srs_key_backup;
int sc_srs_ttl;
+
+ char *sc_admd;
};
#define TRACE_DEBUG 0x0001
@@ -1057,22 +1061,29 @@ enum lka_resp_status {
LKA_PERMFAIL
};
-struct processor {
+enum filter_type {
+ FILTER_TYPE_BUILTIN,
+ FILTER_TYPE_PROC,
+ FILTER_TYPE_CHAIN,
+};
+
+enum filter_subsystem {
+ FILTER_SUBSYSTEM_SMTP_IN = 1<<0,
+ FILTER_SUBSYSTEM_SMTP_OUT = 1<<1,
+};
+
+struct filter_proc {
const char *command;
const char *user;
const char *group;
const char *chroot;
int errfd;
-};
-
-enum filter_type {
- FILTER_TYPE_BUILTIN,
- FILTER_TYPE_PROC,
- FILTER_TYPE_CHAIN,
+ enum filter_subsystem filter_subsystem;
};
struct filter_config {
char *name;
+ enum filter_subsystem filter_subsystem;
enum filter_type filter_type;
enum filter_phase phase;
char *reject;
@@ -1080,6 +1091,7 @@ struct filter_config {
char *rewrite;
char *report;
uint8_t junk;
+ uint8_t bypass;
char *proc;
const char **chain;
@@ -1110,6 +1122,15 @@ struct filter_config {
int8_t not_helo_regex;
struct table *helo_regex;
+ int8_t not_auth;
+ int8_t auth;
+
+ int8_t not_auth_table;
+ struct table *auth_table;
+
+ int8_t not_auth_regex;
+ struct table *auth_regex;
+
int8_t not_mail_from_table;
struct table *mail_from_table;
@@ -1160,7 +1181,7 @@ enum dispatcher_type {
};
struct dispatcher_local {
- uint8_t requires_root; /* only for MBOX */
+ uint8_t is_mbox; /* only for MBOX */
uint8_t expand_only;
uint8_t forward_only;
@@ -1187,6 +1208,8 @@ struct dispatcher_remote {
char *mail_from;
char *smarthost;
+ int smarthost_domain;
+
char *auth;
int tls_required;
int tls_noverify;
@@ -1194,6 +1217,8 @@ struct dispatcher_remote {
int backup;
char *backupmx;
+ char *filtername;
+
int srs;
};
@@ -1359,7 +1384,7 @@ int lka(void);
/* lka_proc.c */
int lka_proc_ready(void);
-void lka_proc_forked(const char *, int);
+void lka_proc_forked(const char *, uint32_t, int);
void lka_proc_errfd(const char *, int);
struct io *lka_proc_get_io(const char *);
@@ -1398,7 +1423,7 @@ void lka_filter_init(void);
void lka_filter_register_hook(const char *, const char *);
void lka_filter_ready(void);
int lka_filter_proc_in_session(uint64_t, const char *);
-void lka_filter_begin(uint64_t, const char *, const struct sockaddr_storage *, const struct sockaddr_storage *, const char *, int);
+void lka_filter_begin(uint64_t, const char *);
void lka_filter_end(uint64_t);
void lka_filter_protocol(uint64_t, enum filter_phase, const char *);
void lka_filter_data_begin(uint64_t);
@@ -1422,6 +1447,11 @@ void mda_postprivdrop(void);
void mda_imsg(struct mproc *, struct imsg *);
+/* mda_mbox.c */
+void mda_mbox_init(struct deliver *);
+void mda_mbox(struct deliver *);
+
+
/* mda_unpriv.c */
void mda_unpriv(struct dispatcher *, struct deliver *, const char *, const char *);
diff --git a/smtpd/smtpd/Makefile b/usr.sbin/smtpd/smtpd/Makefile
index 00c7951f..858bfeda 100644
--- a/smtpd/smtpd/Makefile
+++ b/usr.sbin/smtpd/smtpd/Makefile
@@ -1,4 +1,4 @@
-# $OpenBSD: Makefile,v 1.91 2018/06/03 14:04:06 gilles Exp $
+# $OpenBSD: Makefile,v 1.109 2020/09/23 19:11:50 martijn Exp $
.PATH: ${.CURDIR}/..
@@ -24,12 +24,11 @@ SRCS+= ioev.c
SRCS+= limit.c
SRCS+= lka.c
SRCS+= lka_filter.c
-SRCS+= lka_proc.c
-SRCS+= lka_report.c
SRCS+= lka_session.c
SRCS+= log.c
SRCS+= mailaddr.c
SRCS+= mda.c
+SRCS+= mda_mbox.c
SRCS+= mda_unpriv.c
SRCS+= mda_variables.c
SRCS+= mproc.c
diff --git a/smtpd/spfwalk.c b/usr.sbin/smtpd/spfwalk.c
index ebae9320..46bf3129 100644
--- a/smtpd/spfwalk.c
+++ b/usr.sbin/smtpd/spfwalk.c
@@ -33,6 +33,8 @@
#include <err.h>
#include <errno.h>
#include <event.h>
+#include <imsg.h>
+#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -44,15 +46,23 @@
#include "unpack_dns.h"
#include "parser.h"
+struct target {
+ void (*dispatch)(struct dns_rr *, struct target *);
+ int cidr4;
+ int cidr6;
+};
+
int spfwalk(int, struct parameter *);
-static void dispatch_txt(struct dns_rr *);
-static void dispatch_mx(struct dns_rr *);
-static void dispatch_a(struct dns_rr *);
-static void dispatch_aaaa(struct dns_rr *);
-static void lookup_record(int, const char *, void (*)(struct dns_rr *));
+static void dispatch_txt(struct dns_rr *, struct target *);
+static void dispatch_mx(struct dns_rr *, struct target *);
+static void dispatch_a(struct dns_rr *, struct target *);
+static void dispatch_aaaa(struct dns_rr *, struct target *);
+static void lookup_record(int, char *, struct target *);
static void dispatch_record(struct asr_result *, void *);
static ssize_t parse_txt(const char *, size_t, char *, size_t);
+static int parse_target(char *, struct target *);
+void *xmalloc(size_t size);
int ip_v4 = 0;
int ip_v6 = 0;
@@ -63,6 +73,7 @@ struct dict seen;
int
spfwalk(int argc, struct parameter *argv)
{
+ struct target tgt;
const char *ip_family = NULL;
char *line = NULL;
size_t linesize = 0;
@@ -85,12 +96,15 @@ spfwalk(int argc, struct parameter *argv)
dict_init(&seen);
event_init();
+ tgt.cidr4 = tgt.cidr6 = -1;
+ tgt.dispatch = dispatch_txt;
+
while ((linelen = getline(&line, &linesize, stdin)) != -1) {
- while (linelen-- > 0 && isspace(line[linelen]))
+ while (linelen-- > 0 && isspace((unsigned char)line[linelen]))
line[linelen] = '\0';
if (linelen > 0)
- lookup_record(T_TXT, line, dispatch_txt);
+ lookup_record(T_TXT, line, &tgt);
}
free(line);
@@ -106,20 +120,33 @@ spfwalk(int argc, struct parameter *argv)
}
void
-lookup_record(int type, const char *record, void (*cb)(struct dns_rr *))
+lookup_record(int type, char *record, struct target *tgt)
{
struct asr_query *as;
+ struct target *ntgt;
+ size_t i;
+ if (strchr(record, '%') != NULL) {
+ for (i = 0; record[i] != '\0'; i++) {
+ if (!isprint(record[i]))
+ record[i] = '?';
+ }
+ warnx("%s: %s contains macros and can't be resolved", __func__,
+ record);
+ return;
+ }
as = res_query_async(record, C_IN, type, NULL);
if (as == NULL)
err(1, "res_query_async");
- event_asr_run(as, dispatch_record, cb);
+ ntgt = xmalloc(sizeof(*ntgt));
+ *ntgt = *tgt;
+ event_asr_run(as, dispatch_record, (void *)ntgt);
}
void
dispatch_record(struct asr_result *ar, void *arg)
{
- void (*cb)(struct dns_rr *) = arg;
+ struct target *tgt = arg;
struct unpack pack;
struct dns_header h;
struct dns_query q;
@@ -127,7 +154,7 @@ dispatch_record(struct asr_result *ar, void *arg)
/* best effort */
if (ar->ar_h_errno && ar->ar_h_errno != NO_DATA)
- return;
+ goto end;
unpack_init(&pack, ar->ar_data, ar->ar_datalen);
unpack_header(&pack, &h);
@@ -136,20 +163,23 @@ dispatch_record(struct asr_result *ar, void *arg)
for (; h.ancount; h.ancount--) {
unpack_rr(&pack, &rr);
/**/
- cb(&rr);
+ tgt->dispatch(&rr, tgt);
}
+end:
+ free(tgt);
}
void
-dispatch_txt(struct dns_rr *rr)
+dispatch_txt(struct dns_rr *rr, struct target *tgt)
{
+ char buf[4096];
+ char *argv[512];
+ char buf2[512];
+ struct target ltgt;
struct in6_addr ina;
- char buf[4096];
- char buf2[512];
- char *in = buf;
- char *argv[512];
- char **ap = argv;
- char *end;
+ char **ap = argv;
+ char *in = buf;
+ char *record, *end;
ssize_t n;
if (rr->rr_type != T_TXT)
@@ -179,6 +209,8 @@ dispatch_txt(struct dns_rr *rr)
if (**ap == '+' || **ap == '?')
(*ap)++;
+ ltgt.cidr4 = ltgt.cidr6 = -1;
+
if (strncasecmp("ip4:", *ap, 4) == 0) {
if ((ip_v4 == 1 || ip_both == 1) &&
inet_net_pton(AF_INET, *(ap) + 4,
@@ -196,35 +228,50 @@ dispatch_txt(struct dns_rr *rr)
if (strcasecmp("a", *ap) == 0) {
print_dname(rr->rr_dname, buf2, sizeof(buf2));
buf2[strlen(buf2) - 1] = '\0';
- lookup_record(T_A, buf2, dispatch_a);
- lookup_record(T_AAAA, buf2, dispatch_aaaa);
+ ltgt.dispatch = dispatch_a;
+ lookup_record(T_A, buf2, &ltgt);
+ ltgt.dispatch = dispatch_aaaa;
+ lookup_record(T_AAAA, buf2, &ltgt);
continue;
}
if (strncasecmp("a:", *ap, 2) == 0) {
- lookup_record(T_A, *(ap) + 2, dispatch_a);
- lookup_record(T_AAAA, *(ap) + 2, dispatch_aaaa);
+ record = *(ap) + 2;
+ if (parse_target(record, &ltgt) < 0)
+ continue;
+ ltgt.dispatch = dispatch_a;
+ lookup_record(T_A, record, &ltgt);
+ ltgt.dispatch = dispatch_aaaa;
+ lookup_record(T_AAAA, record, &ltgt);
continue;
}
if (strncasecmp("exists:", *ap, 7) == 0) {
- lookup_record(T_A, *(ap) + 7, dispatch_a);
+ ltgt.dispatch = dispatch_a;
+ lookup_record(T_A, *(ap) + 7, &ltgt);
continue;
}
if (strncasecmp("include:", *ap, 8) == 0) {
- lookup_record(T_TXT, *(ap) + 8, dispatch_txt);
+ ltgt.dispatch = dispatch_txt;
+ lookup_record(T_TXT, *(ap) + 8, &ltgt);
continue;
}
if (strncasecmp("redirect=", *ap, 9) == 0) {
- lookup_record(T_TXT, *(ap) + 9, dispatch_txt);
+ ltgt.dispatch = dispatch_txt;
+ lookup_record(T_TXT, *(ap) + 9, &ltgt);
continue;
}
if (strcasecmp("mx", *ap) == 0) {
print_dname(rr->rr_dname, buf2, sizeof(buf2));
buf2[strlen(buf2) - 1] = '\0';
- lookup_record(T_MX, buf2, dispatch_mx);
+ ltgt.dispatch = dispatch_mx;
+ lookup_record(T_MX, buf2, &ltgt);
continue;
}
if (strncasecmp("mx:", *ap, 3) == 0) {
- lookup_record(T_MX, *(ap) + 3, dispatch_mx);
+ record = *(ap) + 3;
+ if (parse_target(record, &ltgt) < 0)
+ continue;
+ ltgt.dispatch = dispatch_mx;
+ lookup_record(T_MX, record, &ltgt);
continue;
}
}
@@ -232,9 +279,10 @@ dispatch_txt(struct dns_rr *rr)
}
void
-dispatch_mx(struct dns_rr *rr)
+dispatch_mx(struct dns_rr *rr, struct target *tgt)
{
char buf[512];
+ struct target ltgt;
if (rr->rr_type != T_MX)
return;
@@ -243,12 +291,16 @@ dispatch_mx(struct dns_rr *rr)
buf[strlen(buf) - 1] = '\0';
if (buf[strlen(buf) - 1] == '.')
buf[strlen(buf) - 1] = '\0';
- lookup_record(T_A, buf, dispatch_a);
- lookup_record(T_AAAA, buf, dispatch_aaaa);
+
+ ltgt = *tgt;
+ ltgt.dispatch = dispatch_a;
+ lookup_record(T_A, buf, &ltgt);
+ ltgt.dispatch = dispatch_aaaa;
+ lookup_record(T_AAAA, buf, &ltgt);
}
void
-dispatch_a(struct dns_rr *rr)
+dispatch_a(struct dns_rr *rr, struct target *tgt)
{
char buffer[512];
const char *ptr;
@@ -257,12 +309,16 @@ dispatch_a(struct dns_rr *rr)
return;
if ((ptr = inet_ntop(AF_INET, &rr->rr.in_a.addr,
- buffer, sizeof buffer)))
- printf("%s\n", ptr);
+ buffer, sizeof buffer))) {
+ if (tgt->cidr4 >= 0)
+ printf("%s/%d\n", ptr, tgt->cidr4);
+ else
+ printf("%s\n", ptr);
+ }
}
void
-dispatch_aaaa(struct dns_rr *rr)
+dispatch_aaaa(struct dns_rr *rr, struct target *tgt)
{
char buffer[512];
const char *ptr;
@@ -271,11 +327,15 @@ dispatch_aaaa(struct dns_rr *rr)
return;
if ((ptr = inet_ntop(AF_INET6, &rr->rr.in_aaaa.addr6,
- buffer, sizeof buffer)))
- printf("%s\n", ptr);
+ buffer, sizeof buffer))) {
+ if (tgt->cidr6 >= 0)
+ printf("%s/%d\n", ptr, tgt->cidr6);
+ else
+ printf("%s\n", ptr);
+ }
}
-static ssize_t
+ssize_t
parse_txt(const char *rdata, size_t rdatalen, char *dst, size_t dstsz)
{
size_t len;
@@ -309,3 +369,33 @@ parse_txt(const char *rdata, size_t rdatalen, char *dst, size_t dstsz)
return r;
}
+
+int
+parse_target(char *record, struct target *tgt)
+{
+ const char *err;
+ char *m4, *m6;
+
+ m4 = record;
+ strsep(&m4, "/");
+ if (m4 == NULL)
+ return 0;
+
+ m6 = m4;
+ strsep(&m6, "/");
+
+ if (*m4) {
+ tgt->cidr4 = strtonum(m4, 0, 32, &err);
+ if (err)
+ return tgt->cidr4 = -1;
+ }
+
+ if (m6 == NULL)
+ return 0;
+
+ tgt->cidr6 = strtonum(m6, 0, 128, &err);
+ if (err)
+ return tgt->cidr6 = -1;
+
+ return 0;
+}
diff --git a/smtpd/srs.c b/usr.sbin/smtpd/srs.c
index bb4f4d9e..bb4f4d9e 100644
--- a/smtpd/srs.c
+++ b/usr.sbin/smtpd/srs.c
diff --git a/smtpd/ssl.c b/usr.sbin/smtpd/ssl.c
index a37d6fea..97f7b1df 100644
--- a/smtpd/ssl.c
+++ b/usr.sbin/smtpd/ssl.c
@@ -400,11 +400,12 @@ ssl_load_pkey(const void *data, size_t datalen, char *buf, off_t len,
#if defined(SUPPORT_ECDSA)
if (eckey)
ECDSA_set_ex_data(eckey, 0, exdata);
+#else
+ if (eckey)
+ EC_KEY_set_ex_data(eckey, 0, exdata);
#endif
RSA_free(rsa); /* dereference, will be cleaned up with pkey */
-#if defined(SUPPORT_ECDSA)
EC_KEY_free(eckey); /* dereference, will be cleaned up with pkey */
-#endif
}
*x509ptr = x509;
diff --git a/smtpd/ssl.h b/usr.sbin/smtpd/ssl.h
index 11c80c68..11c80c68 100644
--- a/smtpd/ssl.h
+++ b/usr.sbin/smtpd/ssl.h
diff --git a/smtpd/ssl_smtpd.c b/usr.sbin/smtpd/ssl_smtpd.c
index 4e5b7e75..4e5b7e75 100644
--- a/smtpd/ssl_smtpd.c
+++ b/usr.sbin/smtpd/ssl_smtpd.c
diff --git a/smtpd/ssl_verify.c b/usr.sbin/smtpd/ssl_verify.c
index 2e784b97..2e784b97 100644
--- a/smtpd/ssl_verify.c
+++ b/usr.sbin/smtpd/ssl_verify.c
diff --git a/smtpd/stat_backend.c b/usr.sbin/smtpd/stat_backend.c
index 30cb299b..30cb299b 100644
--- a/smtpd/stat_backend.c
+++ b/usr.sbin/smtpd/stat_backend.c
diff --git a/smtpd/stat_ramstat.c b/usr.sbin/smtpd/stat_ramstat.c
index bbf1541a..bbf1541a 100644
--- a/smtpd/stat_ramstat.c
+++ b/usr.sbin/smtpd/stat_ramstat.c
diff --git a/smtpd/table.5 b/usr.sbin/smtpd/table.5
index e9d4fa4b..e9d4fa4b 100644
--- a/smtpd/table.5
+++ b/usr.sbin/smtpd/table.5
diff --git a/smtpd/table.c b/usr.sbin/smtpd/table.c
index 469eeee1..469eeee1 100644
--- a/smtpd/table.c
+++ b/usr.sbin/smtpd/table.c
diff --git a/smtpd/table_db.c b/usr.sbin/smtpd/table_db.c
index f7d766dd..f7d766dd 100644
--- a/smtpd/table_db.c
+++ b/usr.sbin/smtpd/table_db.c
diff --git a/smtpd/table_getpwnam.c b/usr.sbin/smtpd/table_getpwnam.c
index ccf889be..ccf889be 100644
--- a/smtpd/table_getpwnam.c
+++ b/usr.sbin/smtpd/table_getpwnam.c
diff --git a/smtpd/table_proc.c b/usr.sbin/smtpd/table_proc.c
index 44589bd7..44589bd7 100644
--- a/smtpd/table_proc.c
+++ b/usr.sbin/smtpd/table_proc.c
diff --git a/smtpd/table_static.c b/usr.sbin/smtpd/table_static.c
index 8f78ae11..8f78ae11 100644
--- a/smtpd/table_static.c
+++ b/usr.sbin/smtpd/table_static.c
diff --git a/smtpd/to.c b/usr.sbin/smtpd/to.c
index 81a1bb54..81a1bb54 100644
--- a/smtpd/to.c
+++ b/usr.sbin/smtpd/to.c
diff --git a/smtpd/tree.c b/usr.sbin/smtpd/tree.c
index 1d720a59..1d720a59 100644
--- a/smtpd/tree.c
+++ b/usr.sbin/smtpd/tree.c
diff --git a/smtpd/tree.h b/usr.sbin/smtpd/tree.h
index 3d719f09..3d719f09 100644
--- a/smtpd/tree.h
+++ b/usr.sbin/smtpd/tree.h
diff --git a/smtpd/unpack_dns.c b/usr.sbin/smtpd/unpack_dns.c
index 974d5727..974d5727 100644
--- a/smtpd/unpack_dns.c
+++ b/usr.sbin/smtpd/unpack_dns.c
diff --git a/smtpd/unpack_dns.h b/usr.sbin/smtpd/unpack_dns.h
index 2318a0c5..2318a0c5 100644
--- a/smtpd/unpack_dns.h
+++ b/usr.sbin/smtpd/unpack_dns.h
diff --git a/smtpd/util.c b/usr.sbin/smtpd/util.c
index eec3a303..5f9d2113 100644
--- a/smtpd/util.c
+++ b/usr.sbin/smtpd/util.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: util.c,v 1.150 2019/10/03 04:49:12 gilles Exp $ */
+/* $OpenBSD: util.c,v 1.152 2020/11/29 20:07:38 tb Exp $ */
/*
* Copyright (c) 2000,2001 Markus Friedl. All rights reserved.
@@ -178,7 +178,7 @@ bsnprintf(char *str, size_t size, const char *format, ...)
va_start(ap, format);
ret = vsnprintf(str, size, format, ap);
va_end(ap);
- if (ret < 0 || ret >= (int)size)
+ if (ret < 0 || (size_t)ret >= size)
return 0;
return 1;
@@ -464,7 +464,7 @@ valid_domainpart(const char *s)
if (strlcpy(domain, p, sizeof domain) >= sizeof domain)
return 0;
- c = strchr(domain, (int)']');
+ c = strchr(domain, ']');
if (!c || c[1] != '\0')
return 0;
@@ -491,7 +491,7 @@ valid_domainpart(const char *s)
return res_hnok(s);
}
-#define LABELCHR(c) ((c) == '-' || (c) == '_' || isalpha((int)(c)) || isdigit((int)(c)))
+#define LABELCHR(c) ((c) == '-' || (c) == '_' || isalpha((unsigned char)(c)) || isdigit((unsigned char)(c)))
#define LABELMAX 63
#define DNAMEMAX 253
diff --git a/smtpd/waitq.c b/usr.sbin/smtpd/waitq.c
index 082a1e51..082a1e51 100644
--- a/smtpd/waitq.c
+++ b/usr.sbin/smtpd/waitq.c