aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.builds/freebsd.yml23
-rw-r--r--.builds/openbsd.yml16
-rw-r--r--.github/FUNDING.yml2
-rw-r--r--.github/workflows/alpine.yml15
-rw-r--r--.github/workflows/arch.yml15
-rw-r--r--.github/workflows/clang.yml35
-rw-r--r--.github/workflows/coverity.yml15
-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.yml15
-rw-r--r--.gitignore22
-rw-r--r--CHANGES.md21
-rw-r--r--INSTALL237
-rw-r--r--LICENSE342
-rw-r--r--Makefile.am3
-rw-r--r--README162
-rw-r--r--README.md202
-rw-r--r--THANKS19
-rwxr-xr-xbootstrap151
-rw-r--r--ci/COVERITY.MD5SUM1
-rw-r--r--ci/README.md83
-rw-r--r--ci/docker/Dockerfile.alpine50
-rw-r--r--ci/docker/Dockerfile.archlinux62
-rw-r--r--ci/docker/Dockerfile.fedora51
-rw-r--r--ci/docker/Dockerfile.fedora-gcc1054
-rw-r--r--ci/docker/Dockerfile.ubuntu52
-rw-r--r--ci/docker/Dockerfile.ubuntu-gcc1054
-rwxr-xr-xci/scripts/clang_scan.sh60
-rwxr-xr-xci/scripts/coverity_scan.sh77
-rwxr-xr-xci/scripts/generate_certs.sh33
-rw-r--r--ci/scripts/imports/badge.sh72
-rw-r--r--ci/scripts/ssl.conf23
-rw-r--r--configure.ac2072
-rw-r--r--contrib/Makefile.am1
-rw-r--r--contrib/libexec/Makefile.am1
-rw-r--r--contrib/libexec/encrypt/Makefile.am13
-rw-r--r--contrib/libexec/encrypt/encrypt.c102
-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/Makefile.am22
-rw-r--r--contrib/libexec/mail.local/locking.c182
-rw-r--r--contrib/libexec/mail.local/mail.local.8183
-rw-r--r--contrib/libexec/mail.local/mail.local.c392
-rw-r--r--contrib/libexec/mail.local/mail.local.h42
-rw-r--r--contrib/libexec/mail.local/pathnames.h38
-rw-r--r--etc/README.md6
-rw-r--r--etc/aliases100
-rw-r--r--m4/aclocal-openssh.m4179
-rw-r--r--mk/Makefile.am4
-rw-r--r--mk/mail/Makefile.am5
-rw-r--r--mk/mail/mail.lmtp/Makefile.am22
-rw-r--r--mk/mail/mail.maildir/Makefile.am22
-rw-r--r--mk/mail/mail.mboxfile/Makefile.am22
-rw-r--r--mk/mail/mail.mda/Makefile.am22
-rw-r--r--mk/mdoc2man.awk391
-rw-r--r--mk/pathnames10
-rw-r--r--mk/smtp/Makefile.am59
-rw-r--r--mk/smtpctl/Makefile.am99
-rw-r--r--mk/smtpd/Makefile.am196
-rw-r--r--openbsd-compat/Makefile.am235
-rw-r--r--openbsd-compat/NOTES37
-rw-r--r--openbsd-compat/SSL_CTX_use_certificate_chain_mem.c174
-rw-r--r--openbsd-compat/arc4random.c246
-rw-r--r--openbsd-compat/base64.c306
-rw-r--r--openbsd-compat/base64.h65
-rw-r--r--openbsd-compat/basename.c67
-rw-r--r--openbsd-compat/bsd-err.c94
-rw-r--r--openbsd-compat/bsd-err.h29
-rw-r--r--openbsd-compat/bsd-misc.h24
-rw-r--r--openbsd-compat/bsd-vis.h95
-rw-r--r--openbsd-compat/bsd-waitpid.c50
-rw-r--r--openbsd-compat/bsd-waitpid.h51
-rw-r--r--openbsd-compat/chacha_private.h224
-rw-r--r--openbsd-compat/clock_gettime.c59
-rw-r--r--openbsd-compat/closefrom.c107
-rw-r--r--openbsd-compat/crypt_checkpass.c33
-rw-r--r--openbsd-compat/daemon.c82
-rw-r--r--openbsd-compat/defines.h510
-rw-r--r--openbsd-compat/dirname.c72
-rw-r--r--openbsd-compat/empty.c0
-rw-r--r--openbsd-compat/entropy.c69
-rw-r--r--openbsd-compat/entropy.h37
-rw-r--r--openbsd-compat/err_h/err.h21
-rw-r--r--openbsd-compat/errc.c56
-rw-r--r--openbsd-compat/event_asr_run.c88
-rw-r--r--openbsd-compat/explicit_bzero.c15
-rw-r--r--openbsd-compat/fgetln.c61
-rw-r--r--openbsd-compat/fmt_scaled.c274
-rw-r--r--openbsd-compat/fparseln.c179
-rw-r--r--openbsd-compat/freezero.c34
-rw-r--r--openbsd-compat/getopt.c123
-rw-r--r--openbsd-compat/getpeereid.c69
-rw-r--r--openbsd-compat/imsg-buffer.c310
-rw-r--r--openbsd-compat/imsg.c330
-rw-r--r--openbsd-compat/imsg.h115
-rw-r--r--openbsd-compat/includes.h75
-rw-r--r--openbsd-compat/inet_net_pton.c236
-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/libressl.c131
-rw-r--r--openbsd-compat/nanosleep.c63
-rw-r--r--openbsd-compat/openbsd-compat.h342
-rw-r--r--openbsd-compat/paths_h/paths.h8
-rw-r--r--openbsd-compat/pidfile.c112
-rw-r--r--openbsd-compat/pipe2.c46
-rw-r--r--openbsd-compat/progname.c62
-rw-r--r--openbsd-compat/reallocarray.c42
-rw-r--r--openbsd-compat/recallocarray.c84
-rw-r--r--openbsd-compat/res_hnok.c169
-rw-r--r--openbsd-compat/res_randomid.c13
-rw-r--r--openbsd-compat/setegid.c39
-rw-r--r--openbsd-compat/seteuid.c39
-rw-r--r--openbsd-compat/setproctitle.c164
-rw-r--r--openbsd-compat/setresgid.c41
-rw-r--r--openbsd-compat/setresuid.c43
-rw-r--r--openbsd-compat/signal.c60
-rw-r--r--openbsd-compat/strerror.c44
-rw-r--r--openbsd-compat/strlcat.c59
-rw-r--r--openbsd-compat/strlcpy.c55
-rw-r--r--openbsd-compat/strmode.c146
-rw-r--r--openbsd-compat/strndup.c39
-rw-r--r--openbsd-compat/strnlen.c32
-rw-r--r--openbsd-compat/strsep.c79
-rw-r--r--openbsd-compat/strtonum.c72
-rw-r--r--openbsd-compat/sys/queue.h653
-rw-r--r--openbsd-compat/sys/tree.h755
-rw-r--r--openbsd-compat/usleep.c43
-rw-r--r--openbsd-compat/vis.c222
-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.c188
-rw-r--r--smtpd/lka_report.c509
-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--smtpscript/iobuf.c6
-rw-r--r--smtpscript/smtpscript/Makefile2
-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)13
-rw-r--r--usr.sbin/smtpd/bounce.c (renamed from smtpd/bounce.c)106
-rw-r--r--usr.sbin/smtpd/ca.c (renamed from smtpd/ca.c)186
-rw-r--r--usr.sbin/smtpd/cert.c (renamed from smtpd/cert.c)2
-rw-r--r--usr.sbin/smtpd/compress_backend.c (renamed from smtpd/compress_backend.c)2
-rw-r--r--usr.sbin/smtpd/compress_gzip.c (renamed from smtpd/compress_gzip.c)2
-rw-r--r--usr.sbin/smtpd/config.c (renamed from smtpd/config.c)19
-rw-r--r--usr.sbin/smtpd/control.c (renamed from smtpd/control.c)17
-rw-r--r--usr.sbin/smtpd/crypto.c (renamed from smtpd/crypto.c)2
-rw-r--r--usr.sbin/smtpd/dict.c (renamed from smtpd/dict.c)2
-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)14
-rw-r--r--usr.sbin/smtpd/enqueue.c (renamed from smtpd/enqueue.c)117
-rw-r--r--usr.sbin/smtpd/envelope.c (renamed from smtpd/envelope.c)20
-rw-r--r--usr.sbin/smtpd/esc.c (renamed from smtpd/esc.c)6
-rw-r--r--usr.sbin/smtpd/expand.c (renamed from smtpd/expand.c)8
-rw-r--r--usr.sbin/smtpd/filter.c868
-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)7
-rw-r--r--usr.sbin/smtpd/iobuf.c (renamed from smtpd/iobuf.c)9
-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)6
-rw-r--r--usr.sbin/smtpd/ioev.h (renamed from smtpd/ioev.h)0
-rw-r--r--usr.sbin/smtpd/libressl.c213
-rw-r--r--usr.sbin/smtpd/limit.c (renamed from smtpd/limit.c)2
-rw-r--r--usr.sbin/smtpd/lka.c (renamed from smtpd/lka.c)27
-rw-r--r--usr.sbin/smtpd/lka_filter.c (renamed from smtpd/lka_filter.c)768
-rw-r--r--usr.sbin/smtpd/lka_session.c (renamed from smtpd/lka_session.c)4
-rw-r--r--usr.sbin/smtpd/log.c (renamed from smtpd/log.c)2
-rw-r--r--usr.sbin/smtpd/log.h (renamed from smtpd/log.h)6
-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)86
-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)2
-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)11
-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)2
-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)16
-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)32
-rw-r--r--usr.sbin/smtpd/mda.c (renamed from smtpd/mda.c)11
-rw-r--r--usr.sbin/smtpd/mda_mbox.c94
-rw-r--r--usr.sbin/smtpd/mda_unpriv.c (renamed from smtpd/mda_unpriv.c)12
-rw-r--r--usr.sbin/smtpd/mda_variables.c (renamed from smtpd/mda_variables.c)2
-rw-r--r--usr.sbin/smtpd/mproc.c (renamed from smtpd/mproc.c)17
-rw-r--r--usr.sbin/smtpd/mta.c (renamed from smtpd/mta.c)30
-rw-r--r--usr.sbin/smtpd/mta_session.c (renamed from smtpd/mta_session.c)428
-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)364
-rw-r--r--usr.sbin/smtpd/parser.c (renamed from smtpd/parser.c)15
-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)4
-rw-r--r--usr.sbin/smtpd/proxy.c (renamed from smtpd/proxy.c)2
-rw-r--r--usr.sbin/smtpd/queue.c (renamed from smtpd/queue.c)17
-rw-r--r--usr.sbin/smtpd/queue_backend.c (renamed from smtpd/queue_backend.c)7
-rw-r--r--usr.sbin/smtpd/queue_fs.c (renamed from smtpd/queue_fs.c)22
-rw-r--r--usr.sbin/smtpd/queue_null.c (renamed from smtpd/queue_null.c)2
-rw-r--r--usr.sbin/smtpd/queue_proc.c (renamed from smtpd/queue_proc.c)2
-rw-r--r--usr.sbin/smtpd/queue_ram.c (renamed from smtpd/queue_ram.c)2
-rw-r--r--usr.sbin/smtpd/report_smtp.c (renamed from smtpd/report_smtp.c)30
-rw-r--r--usr.sbin/smtpd/resolver.c (renamed from smtpd/resolver.c)13
-rw-r--r--usr.sbin/smtpd/rfc5322.c (renamed from smtpd/rfc5322.c)2
-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)20
-rw-r--r--usr.sbin/smtpd/runq.c (renamed from smtpd/runq.c)2
-rw-r--r--usr.sbin/smtpd/scheduler.c (renamed from smtpd/scheduler.c)5
-rw-r--r--usr.sbin/smtpd/scheduler_backend.c (renamed from smtpd/scheduler_backend.c)2
-rw-r--r--usr.sbin/smtpd/scheduler_null.c (renamed from smtpd/scheduler_null.c)2
-rw-r--r--usr.sbin/smtpd/scheduler_proc.c (renamed from smtpd/scheduler_proc.c)2
-rw-r--r--usr.sbin/smtpd/scheduler_ramqueue.c (renamed from smtpd/scheduler_ramqueue.c)2
-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)23
-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)343
-rw-r--r--usr.sbin/smtpd/smtpc.c (renamed from smtpd/smtpc.c)16
-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)38
-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)20
-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)312
-rw-r--r--usr.sbin/smtpd/smtpd.conf19
-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)95
-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)174
-rw-r--r--usr.sbin/smtpd/srs.c (renamed from smtpd/srs.c)2
-rw-r--r--usr.sbin/smtpd/ssl.c (renamed from smtpd/ssl.c)9
-rw-r--r--usr.sbin/smtpd/ssl.h (renamed from smtpd/ssl.h)1
-rw-r--r--usr.sbin/smtpd/ssl_smtpd.c (renamed from smtpd/ssl_smtpd.c)2
-rw-r--r--usr.sbin/smtpd/ssl_verify.c (renamed from smtpd/ssl_verify.c)1
-rw-r--r--usr.sbin/smtpd/stat_backend.c (renamed from smtpd/stat_backend.c)2
-rw-r--r--usr.sbin/smtpd/stat_ramstat.c (renamed from smtpd/stat_ramstat.c)1
-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)14
-rw-r--r--usr.sbin/smtpd/table_db.c (renamed from smtpd/table_db.c)9
-rw-r--r--usr.sbin/smtpd/table_getpwnam.c (renamed from smtpd/table_getpwnam.c)2
-rw-r--r--usr.sbin/smtpd/table_proc.c (renamed from smtpd/table_proc.c)4
-rw-r--r--usr.sbin/smtpd/table_static.c (renamed from smtpd/table_static.c)2
-rw-r--r--usr.sbin/smtpd/to.c (renamed from smtpd/to.c)61
-rw-r--r--usr.sbin/smtpd/tree.c (renamed from smtpd/tree.c)2
-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)5
-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)34
-rw-r--r--usr.sbin/smtpd/waitq.c (renamed from smtpd/waitq.c)2
310 files changed, 27458 insertions, 2473 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/FUNDING.yml b/.github/FUNDING.yml
index a1f700fd..36cb5950 100644
--- a/.github/FUNDING.yml
+++ b/.github/FUNDING.yml
@@ -1,3 +1,3 @@
-#github: [poolpOrg]
+github: [poolpOrg]
patreon: gilles
diff --git a/.github/workflows/alpine.yml b/.github/workflows/alpine.yml
new file mode 100644
index 00000000..23816eab
--- /dev/null
+++ b/.github/workflows/alpine.yml
@@ -0,0 +1,15 @@
+name: Alpine Linux (amd64 musl openssl)
+on:
+ push:
+ branches:
+ - master
+ pull_request:
+ branches:
+ - master
+jobs:
+ build:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v1
+ - name: Alpine Linux (amd64 musl openssl)
+ run: docker build . --file ci/docker/Dockerfile.alpine --tag opensmtpd:alpine
diff --git a/.github/workflows/arch.yml b/.github/workflows/arch.yml
new file mode 100644
index 00000000..2ee226a2
--- /dev/null
+++ b/.github/workflows/arch.yml
@@ -0,0 +1,15 @@
+name: Archlinux (amd64 glibc libressl)
+on:
+ push:
+ branches:
+ - master
+ pull_request:
+ branches:
+ - master
+jobs:
+ build:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v1
+ - name: Archlinux (amd64 glibc libressl)
+ run: docker build . --file ci/docker/Dockerfile.archlinux --tag opensmtpd:archlinux
diff --git a/.github/workflows/clang.yml b/.github/workflows/clang.yml
new file mode 100644
index 00000000..15ccd31d
--- /dev/null
+++ b/.github/workflows/clang.yml
@@ -0,0 +1,35 @@
+name: Clang Scan
+
+on:
+ schedule:
+ - cron: '0 23 * * *' # Daily at 23:00 UTC
+jobs:
+ build:
+ runs-on: ubuntu-latest
+ steps:
+ - name: install dependencies
+ run: |
+ wget https://apt.llvm.org/llvm.sh
+ chmod +x llvm.sh
+ sudo ./llvm.sh 9
+ sudo apt install \
+ clang-tools-9 \
+ libasr-dev \
+ libevent-dev \
+ libssl-dev \
+ bison
+ - uses: actions/checkout@v1
+ - name: run scan-build
+ run: |
+ export PATH=$PATH:/usr/lib/llvm-9/bin/
+ CLANG_SCAN_BADGE_REQUIRED=1 ci/scripts/clang_scan.sh
+ - name: publish results
+ run: |
+ aws s3 rm --recursive s3://opensmtpd/reports/clang/
+ cd clang-report
+ cd "$( find . -type d | sort | tail -n1 )"
+ aws s3 sync . s3://opensmtpd/reports/clang
+ env:
+ AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
+ AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
+ AWS_DEFAULT_REGION: ${{ secrets.AWS_DEFAULT_REGION }}
diff --git a/.github/workflows/coverity.yml b/.github/workflows/coverity.yml
new file mode 100644
index 00000000..d25bb4f7
--- /dev/null
+++ b/.github/workflows/coverity.yml
@@ -0,0 +1,15 @@
+name: Coverity Scan
+on:
+ schedule:
+ - cron: '0 23 * * *' # Daily at 23:00 UTC
+jobs:
+ build:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v1
+ - name: dependencies
+ run: sudo apt install libasr-dev libevent-dev libssl-dev bison
+ - name: scan
+ run: sh ci/scripts/coverity_scan.sh
+ env:
+ token: ${{ secrets.COVERITY_SCAN_TOKEN }}
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
new file mode 100644
index 00000000..c4bfd437
--- /dev/null
+++ b/.github/workflows/ubuntu.yml
@@ -0,0 +1,15 @@
+name: Ubuntu (amd64 glibc openssl)
+on:
+ push:
+ branches:
+ - master
+ pull_request:
+ branches:
+ - master
+jobs:
+ build:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v1
+ - name: Ubuntu (amd64 glibc openssl)
+ run: docker build . --file ci/docker/Dockerfile.ubuntu --tag opensmtpd:ubuntu
diff --git a/.gitignore b/.gitignore
index 7644dffc..f9f88aa5 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,7 +4,6 @@
*.in
*.out
*.log
-*~
.#*
.deps
m4
@@ -26,6 +25,8 @@ stamp-h1
ylwrap
tags
obj
+
+#Other VCS files
CVS/
smtpd/CVS
smtpd/smtpctl/CVS
@@ -34,3 +35,22 @@ smtpd/mail/CVS
smtpd/mail/CVS
smtpd/mail/*/CVS
smtpd/smtpctl/CVS/*
+
+#Editor temporary files
+*~
+.idea
+
+# Coverity
+cov-int/
+cov-analysis-linux64/
+cov-analysis-linux64.tgz
+opensmtpd.tgz
+
+# Clang scan-build
+clang-report/
+
+
+# TLS certs
+open.smtpd.cert
+open.smtpd.csr
+open.smtpd.key
diff --git a/CHANGES.md b/CHANGES.md
index ef87ca6f..db25a4db 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -1,6 +1,20 @@
-# Release 6.6.1 (2019-11-06)
+# Release 6.6.3p1 (2020-02-10)
-## Changes in this release (since 6.6.0)
+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)
This is a bugfix release. No new features were added.
@@ -9,6 +23,7 @@ This is a bugfix release. No new features were added.
- Fixed crash in `arc4random` caused by differences in OpenSSL vs LibreSSL compatibility layer plumbing [#958](https://github.com/OpenSMTPD/OpenSMTPD/issues/958)
- Fixed issue where `from any` rules never matched by IPv6 sources [#969](https://github.com/OpenSMTPD/OpenSMTPD/issues/969)
- Fixed crash that happened during mail relay on musl distros [#929](https://github.com/OpenSMTPD/OpenSMTPD/issues/929)
+- Added reference aliases file in `etc/aliases`
- Fixed multiple compilation warnings
[#965](https://github.com/OpenSMTPD/OpenSMTPD/issues/965)
[#966](https://github.com/OpenSMTPD/OpenSMTPD/issues/966)
@@ -19,7 +34,7 @@ This is a bugfix release. No new features were added.
-# Release 6.6.0 (2019-10-26)
+# Release 6.6.0p1 (2019-10-26)
## Dependencies note:
diff --git a/INSTALL b/INSTALL
new file mode 100644
index 00000000..d3c5b40a
--- /dev/null
+++ b/INSTALL
@@ -0,0 +1,237 @@
+Installation Instructions
+*************************
+
+Copyright (C) 1994, 1995, 1996, 1999, 2000, 2001, 2002, 2004, 2005,
+2006, 2007 Free Software Foundation, Inc.
+
+This file is free documentation; the Free Software Foundation gives
+unlimited permission to copy, distribute and modify it.
+
+Basic Installation
+==================
+
+Briefly, the shell commands `./configure; make; make install' should
+configure, build, and install this package. The following
+more-detailed instructions are generic; see the `README' file for
+instructions specific to this package.
+
+ The `configure' shell script attempts to guess correct values for
+various system-dependent variables used during compilation. It uses
+those values to create a `Makefile' in each directory of the package.
+It may also create one or more `.h' files containing system-dependent
+definitions. Finally, it creates a shell script `config.status' that
+you can run in the future to recreate the current configuration, and a
+file `config.log' containing compiler output (useful mainly for
+debugging `configure').
+
+ It can also use an optional file (typically called `config.cache'
+and enabled with `--cache-file=config.cache' or simply `-C') that saves
+the results of its tests to speed up reconfiguring. Caching is
+disabled by default to prevent problems with accidental use of stale
+cache files.
+
+ If you need to do unusual things to compile the package, please try
+to figure out how `configure' could check whether to do them, and mail
+diffs or instructions to the address given in the `README' so they can
+be considered for the next release. If you are using the cache, and at
+some point `config.cache' contains results you don't want to keep, you
+may remove or edit it.
+
+ The file `configure.ac' (or `configure.in') is used to create
+`configure' by a program called `autoconf'. You need `configure.ac' if
+you want to change it or regenerate `configure' using a newer version
+of `autoconf'.
+
+The simplest way to compile this package is:
+
+ 1. `cd' to the directory containing the package's source code and type
+ `./configure' to configure the package for your system.
+
+ Running `configure' might take a while. While running, it prints
+ some messages telling which features it is checking for.
+
+ 2. Type `make' to compile the package.
+
+ 3. Optionally, type `make check' to run any self-tests that come with
+ the package.
+
+ 4. Type `make install' to install the programs and any data files and
+ documentation.
+
+ 5. You can remove the program binaries and object files from the
+ source code directory by typing `make clean'. To also remove the
+ files that `configure' created (so you can compile the package for
+ a different kind of computer), type `make distclean'. There is
+ also a `make maintainer-clean' target, but that is intended mainly
+ for the package's developers. If you use it, you may have to get
+ all sorts of other programs in order to regenerate files that came
+ with the distribution.
+
+ 6. Often, you can also type `make uninstall' to remove the installed
+ files again.
+
+Compilers and Options
+=====================
+
+Some systems require unusual options for compilation or linking that the
+`configure' script does not know about. Run `./configure --help' for
+details on some of the pertinent environment variables.
+
+ You can give `configure' initial values for configuration parameters
+by setting variables in the command line or in the environment. Here
+is an example:
+
+ ./configure CC=c99 CFLAGS=-g LIBS=-lposix
+
+ *Note Defining Variables::, for more details.
+
+Compiling For Multiple Architectures
+====================================
+
+You can compile the package for more than one kind of computer at the
+same time, by placing the object files for each architecture in their
+own directory. To do this, you can use GNU `make'. `cd' to the
+directory where you want the object files and executables to go and run
+the `configure' script. `configure' automatically checks for the
+source code in the directory that `configure' is in and in `..'.
+
+ With a non-GNU `make', it is safer to compile the package for one
+architecture at a time in the source code directory. After you have
+installed the package for one architecture, use `make distclean' before
+reconfiguring for another architecture.
+
+Installation Names
+==================
+
+By default, `make install' installs the package's commands under
+`/usr/local/bin', include files under `/usr/local/include', etc. You
+can specify an installation prefix other than `/usr/local' by giving
+`configure' the option `--prefix=PREFIX'.
+
+ You can specify separate installation prefixes for
+architecture-specific files and architecture-independent files. If you
+pass the option `--exec-prefix=PREFIX' to `configure', the package uses
+PREFIX as the prefix for installing programs and libraries.
+Documentation and other data files still use the regular prefix.
+
+ In addition, if you use an unusual directory layout you can give
+options like `--bindir=DIR' to specify different values for particular
+kinds of files. Run `configure --help' for a list of the directories
+you can set and what kinds of files go in them.
+
+ If the package supports it, you can cause programs to be installed
+with an extra prefix or suffix on their names by giving `configure' the
+option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'.
+
+Optional Features
+=================
+
+Some packages pay attention to `--enable-FEATURE' options to
+`configure', where FEATURE indicates an optional part of the package.
+They may also pay attention to `--with-PACKAGE' options, where PACKAGE
+is something like `gnu-as' or `x' (for the X Window System). The
+`README' should mention any `--enable-' and `--with-' options that the
+package recognizes.
+
+ For packages that use the X Window System, `configure' can usually
+find the X include and library files automatically, but if it doesn't,
+you can use the `configure' options `--x-includes=DIR' and
+`--x-libraries=DIR' to specify their locations.
+
+Specifying the System Type
+==========================
+
+There may be some features `configure' cannot figure out automatically,
+but needs to determine by the type of machine the package will run on.
+Usually, assuming the package is built to be run on the _same_
+architectures, `configure' can figure that out, but if it prints a
+message saying it cannot guess the machine type, give it the
+`--build=TYPE' option. TYPE can either be a short name for the system
+type, such as `sun4', or a canonical name which has the form:
+
+ CPU-COMPANY-SYSTEM
+
+where SYSTEM can have one of these forms:
+
+ OS KERNEL-OS
+
+ See the file `config.sub' for the possible values of each field. If
+`config.sub' isn't included in this package, then this package doesn't
+need to know the machine type.
+
+ If you are _building_ compiler tools for cross-compiling, you should
+use the option `--target=TYPE' to select the type of system they will
+produce code for.
+
+ If you want to _use_ a cross compiler, that generates code for a
+platform different from the build platform, you should specify the
+"host" platform (i.e., that on which the generated programs will
+eventually be run) with `--host=TYPE'.
+
+Sharing Defaults
+================
+
+If you want to set default values for `configure' scripts to share, you
+can create a site shell script called `config.site' that gives default
+values for variables like `CC', `cache_file', and `prefix'.
+`configure' looks for `PREFIX/share/config.site' if it exists, then
+`PREFIX/etc/config.site' if it exists. Or, you can set the
+`CONFIG_SITE' environment variable to the location of the site script.
+A warning: not all `configure' scripts look for a site script.
+
+Defining Variables
+==================
+
+Variables not defined in a site shell script can be set in the
+environment passed to `configure'. However, some packages may run
+configure again during the build, and the customized values of these
+variables may be lost. In order to avoid this problem, you should set
+them in the `configure' command line, using `VAR=value'. For example:
+
+ ./configure CC=/usr/local2/bin/gcc
+
+causes the specified `gcc' to be used as the C compiler (unless it is
+overridden in the site shell script).
+
+Unfortunately, this technique does not work for `CONFIG_SHELL' due to
+an Autoconf bug. Until the bug is fixed you can use this workaround:
+
+ CONFIG_SHELL=/bin/bash /bin/bash ./configure CONFIG_SHELL=/bin/bash
+
+`configure' Invocation
+======================
+
+`configure' recognizes the following options to control how it operates.
+
+`--help'
+`-h'
+ Print a summary of the options to `configure', and exit.
+
+`--version'
+`-V'
+ Print the version of Autoconf used to generate the `configure'
+ script, and exit.
+
+`--cache-file=FILE'
+ Enable the cache: use and save the results of the tests in FILE,
+ traditionally `config.cache'. FILE defaults to `/dev/null' to
+ disable caching.
+
+`--config-cache'
+`-C'
+ Alias for `--cache-file=config.cache'.
+
+`--quiet'
+`--silent'
+`-q'
+ Do not print messages saying which checks are being made. To
+ suppress all normal output, redirect it to `/dev/null' (any error
+ messages will still be shown).
+
+`--srcdir=DIR'
+ Look for the package's source code in directory DIR. Usually
+ `configure' can determine that directory automatically.
+
+`configure' also accepts some other, not widely useful, options. Run
+`configure --help' for more details.
+
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 00000000..f1cea087
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,342 @@
+This file is part of the OpenSMTPD software.
+
+The licences which components of this software fall under are as
+follows. First, we will summarize and say that all components
+are under a BSD licence, or a licence more free than that.
+
+OpenSMTPD contains no GPL code.
+
+Portable OpenSMTPD is divided in 4 parts:
+- Original OpenSMTPD
+- mail.local
+- openbsd-compat
+- smtpctl encrypt sub command
+
+
+
+OpenSMTPD
+=========
+
+
+1) Almost all code is licensed under an ISC-style license, to the following
+ copyright holders:
+
+ Gilles Chehade
+ Eric Faurot
+ Jacek Masiulaniec
+ Pierre-Yves Ritschard
+ Henning Brauer
+ Esben Norby
+ Markus Friedl
+ Daniel Hartmeier
+ Theo de Raadt
+ Claudio Jeker
+ Reyk Floeter
+ Janne Johansson
+ Alexander Schrijver
+ Marc Balmer
+ Ashish Shukla
+ Ryan Kavanagh
+ Charles Longeau
+
+
+2) ssl_privsep.c
+
+ /* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to. The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code. The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * 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 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. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * "This product includes cryptographic software written by
+ * Eric Young (eay@cryptsoft.com)"
+ * The word 'cryptographic' can be left out if the rouines from the library
+ * being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ * the apps directory (application code) you must include an acknowledgement:
+ * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``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 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.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed. i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.]
+ */
+
+ /*
+ * SSL operations needed when running in a privilege separated environment.
+ * Adapted from openssl's ssl_rsa.c by Pierre-Yves Ritschard .
+ */
+
+
+
+mail.local
+==========
+
+
+1) mail.local is covered by a 3-clause BSD license, to the following
+ copyright holders:
+
+ The Regents of the University of California.
+ David Mazieres
+ Theo de Raadt
+
+ * 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.
+
+
+
+openbsd-compat
+==============
+
+
+Most of the OpenBSD compatibility layer is based on the work by Damien Miller for
+Portable OpenSSH.
+
+1) Almost all code is licensed under an ISC-style license, to the following
+ copyright holders:
+
+ Internet Software Consortium.
+ David Mazieres
+ Damien Miller
+ Markus Friedl
+ Todd C. Miller
+ Henning Brauer
+ Pierre-Yves Ritschard
+ Reyk Floeter
+ Theo de Raadt
+ Ted Unangst
+ Charles Longeau
+
+ * 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.
+
+
+2) base64.{c,h} in addition to beeing covered by an ISC-style licence, is also
+ covered by this one:
+
+ * Portions Copyright (c) 1995 by International Business Machines, Inc.
+ *
+ * International Business Machines, Inc. (hereinafter called IBM) grants
+ * permission under its copyrights to use, copy, modify, and distribute this
+ * Software with or without fee, provided that the above copyright notice and
+ * all paragraphs of this notice appear in all copies, and that the name of IBM
+ * not be used in connection with the marketing of any product incorporating
+ * the Software or modifications thereof, without specific, written prior
+ * permission.
+ *
+ * To the extent it has a right to do so, IBM grants an immunity from suit
+ * under its patents, if any, for the use, sale or manufacture of products to
+ * the extent that such products are used for performing Domain Name System
+ * dynamic updates in TCP/IP networks by means of the Software. No immunity is
+ * granted for any product per se or for any other function of any product.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", AND IBM DISCLAIMS ALL WARRANTIES,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE. IN NO EVENT SHALL IBM BE LIABLE FOR ANY SPECIAL,
+ * DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE, EVEN
+ * IF IBM IS APPRISED OF THE POSSIBILITY OF SUCH DAMAGES.
+
+
+3) Portable OpenSMTPD includes code under the 2-clause BSD license, from the
+ following copyright holders:
+
+ Ben Lindstrom
+ Damien Miller
+ Marc Espie
+ Tim Rice
+ The NetBSD Foundation, Inc.
+ Jason R. Thorpe?
+ Niels Provos
+
+ * 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.
+
+
+4) Some code is under a 3-clause BSD license, from the
+ following copyright holders:
+
+ The Regents of the University of California.
+ Ian F. Darwin
+ Damien Miller
+ Eric P. Allman
+
+ * 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.
+
+
+5) Some code is under a 4-clause BSD license, from the
+ following copyright holder:
+
+ Christos Zoulas
+
+ * 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. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Christos Zoulas.
+ * 4. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * 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.
+
+
+6) includes.h, log.h, setresguid.c, xmalloc.c, xmalloc.c
+
+ * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
+ * All rights reserved
+ *
+ * As far as I am concerned, the code I have written for this software
+ * can be used freely for any purpose. Any derived versions of this
+ * software must be clearly marked as such, and if the derived work is
+ * incompatible with the protocol description in the RFC file, it must be
+ * called by a name other than "ssh" or "Secure Shell".
+
+
+7) chacha_private.h
+
+D. J. Bernstein
+Public domain.
+
+
+8) bootstrap (only there in the git repository)
+
+ # Copyright (c) 2002-2011 Sam Hocevar <sam@hocevar.net>
+ #
+ # This program is free software. It comes without any warranty, to
+ # the extent permitted by applicable law. You can redistribute it
+ # and/or modify it under the terms of the Do What The Fuck You Want
+ # To Public License, Version 2, as published by Sam Hocevar. See
+ # http://sam.zoy.org/wtfpl/COPYING for more details.
+
+
+
+smtpctl encrypt sub command
+===========================
+
+
+smtpctl encrypt sub command is licensed under an ISC-style license, to the
+following copyright holders:
+
+ Sunil Nimmagadda
+ Gilles Chehade
diff --git a/Makefile.am b/Makefile.am
new file mode 100644
index 00000000..7d95a5b8
--- /dev/null
+++ b/Makefile.am
@@ -0,0 +1,3 @@
+SUBDIRS = openbsd-compat mk contrib
+
+ACLOCAL_AMFLAGS = -I m4
diff --git a/README b/README
deleted file mode 100644
index 0e223143..00000000
--- a/README
+++ /dev/null
@@ -1,162 +0,0 @@
-Preliminary note
-================
-
-OpenSMTPD is a FREE implementation of the server-side SMTP protocol as
-defined by RFC 5321, with some additional standard extensions.
-
-It allows ordinary machines to exchange e-mails with other systems
-speaking the SMTP protocol.
-
-OpenSMTPD runs on top of the OpenBSD operating system but also has a
-portable version that can build and run on several systems, including:
-
-* Linux
-* FreeBSD
-* NetBSD
-* DragonFly
-
-For more information: http://www.opensmtpd.org/portable.html
-
-People interested about OpenSMTPD are encouraged to subscribe to our
-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.
-
-Cheers!
-
-
-How to build, configure and use Portable OpenSMTPD
-==================================================
-
-Dependencies
-------------
-
-Portable OpenSMTPD relies on:
- * autoconf (http://www.gnu.org/software/autoconf/)
- * automake (http://www.gnu.org/software/automake/)
- * bison (http://www.gnu.org/software/bison/)
- or byacc (http://invisible-island.net/byacc/byacc.html)
- * libevent (http://libevent.org/)
- * libtool (http://www.gnu.org/software/libtool/)
- * openssl (http://www.openssl.org/)
- * libasr (https://opensmtpd.org/archives/libasr-1.0.2.tar.gz)
-
-
-Get the source
---------------
-
- git clone -b portable git://github.com/OpenSMTPD/OpenSMTPD.git opensmtpd
-
-
-Build
------
-
- cd opensmtpd*
- ./bootstrap # Only if you build from git sources
- ./configure
- make
- sudo make install
-
-# Special notes for FreeBSD/DragonFlyBSD/Mac OS X:
-
-Please launch configure with special directive about libevent and
-libasr directory:
-
-# FreeBSD / DragonFlyBSD:
-
- ./configure --with-libasr=/usr/local
-
-# Mac OS X:
-
- ./configure --with-libevent=/opt/local --with-libasr=/opt/local
-
-
-Install
--------
-
- sudo make install
-
-
-Configure /etc/smtpd.conf
--------------------------
-
-Please have a look at the complete format description of smtpd.conf
-configuration file (http://opensmtpd.org/smtpd.conf.5.html)
-
-
-Add OpenSMTPD users
--------------------
-
-To operate, OpenSMTPD requires at least one user, by default _smtpd; and
-preferably two users, by default _smtpd and _smtpq.
-
-Using two users instead of one will increase security by a large factor
-so... if you want to voluntarily reduce security or you have absolute
-more faith in our code than we do, by all means use one.
-
-
-The instructions below assume the default users however, the configure
-script allows overriding these using the options:
---with-user-smtpd, --with-user-queue, and --with-group-queue.
-
-
-# NetBSD, Linux (Debian, Arch Linux, ...)
-
- 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
-
-# DragonFlyBSD, FreeBSD
-
- pw useradd _smtpd -c "SMTP Daemon" -d /var/empty -s /sbin/nologin
- pw useradd _smtpq -c "SMTPD Queue" -d /var/empty -s /sbin/nologin
-
-# Mac OS X
-
-First we need a group with an unused GID below 500, list the current
-ones used:
-
- /usr/bin/dscl . -list /Groups PrimaryGroupID | sort -n -k2,2
-
-Add a group - here we have picked 444:
-
- /usr/bin/sudo /usr/bin/dscl . -create /Groups/_smtpd
- PrimaryGroupID 444
-
-Then the user. Again we need an unused UID below 500, list the current
-ones used:
-
- /usr/bin/dscl . -list /Users UniqueID | sort -n -k2,2
-
-Add a user - here we have picked 444:
-
- /usr/bin/sudo /usr/bin/dscl . -create /Users/_smtpd UniqueID 444
- /usr/bin/sudo /usr/bin/dscl . -delete /Users/_smtpd AuthenticationAuthority
- /usr/bin/sudo /usr/bin/dscl . -delete /Users/_smtpd PasswordPolicyOptions
- /usr/bin/sudo /usr/bin/dscl . -delete /Users/_smtpd dsAttrTypeNative:KerberosKeys
- /usr/bin/sudo /usr/bin/dscl . -delete /Users/_smtpd dsAttrTypeNative:ShadowHashData
- /usr/bin/sudo /usr/bin/dscl . -create /Users/_smtpd RealName "SMTP Daemon"
- /usr/bin/sudo /usr/bin/dscl . -create /Users/_smtpd Password "*"
- /usr/bin/sudo /usr/bin/dscl . -create /Users/_smtpd PrimaryGroupID 444
- /usr/bin/sudo /usr/bin/dscl . -create /Users/_smtpd NFSHomeDirectory /var/empty
- /usr/bin/sudo /usr/bin/dscl . -create /Users/_smtpd UserShell /usr/bin/false
-
-repeat for the _smtpq user.
-
-
-Launch smtpd
-------------
-
-First, kill any running sendmail/exim/qmail/postfix or other.
-
-Then:
-
- smtpd
-
-or in debug and verbose mode
-
- smtpd -dv
diff --git a/README.md b/README.md
new file mode 100644
index 00000000..bc35c16b
--- /dev/null
+++ b/README.md
@@ -0,0 +1,202 @@
+# OpenSMTPD
+
+[![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/)
+
+
+
+OpenSMTPD is a FREE implementation of the server-side SMTP protocol as
+defined by [RFC 5321](https://tools.ietf.org/html/rfc5321), with some
+additional standard extensions.
+
+It allows ordinary machines to exchange e-mails with other systems
+speaking the SMTP protocol.
+
+OpenSMTPD runs on top of the OpenBSD operating system but also has a
+portable version that can build and run on several systems, including:
+
+* Linux
+* FreeBSD
+* NetBSD
+* DragonFly
+
+For more information: http://www.opensmtpd.org/portable.html
+
+People interested about OpenSMTPD are encouraged to subscribe to our
+mailing list: http://www.opensmtpd.org/list.html
+
+and to join the IRC channel: #OpenSMTPD @ irc.freenode.net
+
+The manual pages are available online at https://www.opensmtpd.org/manual.html,
+which you are encouraged to contribute to.
+
+Cheers!
+
+
+# How to build, configure and use Portable OpenSMTPD
+
+## Dependencies
+
+Portable OpenSMTPD relies on:
+ * autoconf (http://www.gnu.org/software/autoconf/)
+ * automake (http://www.gnu.org/software/automake/)
+ * bison (http://www.gnu.org/software/bison/)
+ or byacc (http://invisible-island.net/byacc/byacc.html)
+ * libevent (http://libevent.org/)
+ * libtool (http://www.gnu.org/software/libtool/)
+ * libressl (https://www.libressl.org/)
+ or OpenSSL (https://www.openssl.org/)
+
+
+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 git://github.com/OpenSMTPD/OpenSMTPD.git opensmtpd
+
+
+## Build
+
+ cd opensmtpd*
+ ./bootstrap # Only if you build from git sources
+ ./configure
+ make
+ sudo make install
+
+### Special notes for FreeBSD/DragonFlyBSD/Mac OS X:
+
+Please launch configure with special directive about libevent and
+libasr directory:
+
+### FreeBSD / DragonFlyBSD:
+
+ ./configure --with-libasr=/usr/local
+
+### MacOS:
+
+ ./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
+
+ sudo make install
+
+
+## Setup historical interface
+
+OpenSMTPD provides a single utility `smtpctl` to control the daemon and
+the local submission subsystem.
+
+To accomodate systems that require historical interfaces such as `sendmail`,
+`newaliases` or `makemap`, the `smtpctl` utility can operate in compatibility
+mode if called with the historical name.
+
+On mailwrapper-enabled systems, this is achieved by editing `/etc/mailer.conf`
+and adding the following lines:
+
+ sendmail /usr/sbin/smtpctl
+ send-mail /usr/sbin/smtpctl
+ mailq /usr/sbin/smtpctl
+ makemap /usr/sbin/smtpctl
+ newaliases /usr/sbin/smtpctl
+
+
+Whereas on systems that don't provide mailwrapper, it can be achieved by
+setting the appropriate symbolic links:
+
+ ln -s /usr/sbin/smtpctl sendmail
+ ln -s /usr/sbin/smtpctl send-mail
+ ln -s /usr/sbin/smtpctl mailq
+ ln -s /usr/sbin/smtpctl makemap
+ ln -s /usr/sbin/smtpctl newaliases
+
+
+The OpenSMTPD project leaves it up to the package maintainers to setup the
+links in their packages as it is very hard for us to accomodate all systems
+with the prefered method in a clean way.
+
+
+## Configure /etc/smtpd.conf
+
+Please have a look at the complete format description of smtpd.conf
+configuration file (https://man.openbsd.org/smtpd.conf)
+
+
+## Add OpenSMTPD users
+
+To operate, OpenSMTPD requires at least one user, by default `_smtpd`; and
+preferably two users, by default `_smtpd` and `_smtpq`.
+
+Using two users instead of one will increase security by a large factor
+so... if you want to voluntarily reduce security or you have absolute
+more faith in our code than we do, by all means use one.
+
+
+The instructions below assume the default users however, the configure
+script allows overriding these using the options:
+`--with-user-smtpd`, `--with-user-queue`, and `--with-group-queue`.
+
+
+### NetBSD, Linux (Debian, Arch Linux, ...)
+
+ 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
+
+### DragonFlyBSD, FreeBSD
+
+ pw useradd _smtpd -c "SMTP Daemon" -d /var/empty -s /sbin/nologin
+ pw useradd _smtpq -c "SMTPD Queue" -d /var/empty -s /sbin/nologin
+
+### Mac OS X
+
+First we need a group with an unused GID below `500`, list the current
+ones used:
+
+ /usr/bin/dscl . -list /Groups PrimaryGroupID | sort -n -k2,2
+
+Add a group - here we have picked `444`:
+
+ /usr/bin/sudo /usr/bin/dscl . -create /Groups/_smtpd
+ PrimaryGroupID 444
+
+Then the user. Again we need an unused UID below `500`, list the current
+ones used:
+
+ /usr/bin/dscl . -list /Users UniqueID | sort -n -k2,2
+
+Add a user - here we have picked `444`:
+
+ /usr/bin/sudo /usr/bin/dscl . -create /Users/_smtpd UniqueID 444
+ /usr/bin/sudo /usr/bin/dscl . -delete /Users/_smtpd AuthenticationAuthority
+ /usr/bin/sudo /usr/bin/dscl . -delete /Users/_smtpd PasswordPolicyOptions
+ /usr/bin/sudo /usr/bin/dscl . -delete /Users/_smtpd dsAttrTypeNative:KerberosKeys
+ /usr/bin/sudo /usr/bin/dscl . -delete /Users/_smtpd dsAttrTypeNative:ShadowHashData
+ /usr/bin/sudo /usr/bin/dscl . -create /Users/_smtpd RealName "SMTP Daemon"
+ /usr/bin/sudo /usr/bin/dscl . -create /Users/_smtpd Password "*"
+ /usr/bin/sudo /usr/bin/dscl . -create /Users/_smtpd PrimaryGroupID 444
+ /usr/bin/sudo /usr/bin/dscl . -create /Users/_smtpd NFSHomeDirectory /var/empty
+ /usr/bin/sudo /usr/bin/dscl . -create /Users/_smtpd UserShell /usr/bin/false
+
+repeat for the `_smtpq` user.
+
+
+## Launch smtpd
+
+First, kill any running sendmail/exim/qmail/postfix or other.
+
+Then:
+
+ smtpd
+
+or in debug and verbose mode
+
+ smtpd -dv
+
diff --git a/THANKS b/THANKS
deleted file mode 100644
index 20f8fe99..00000000
--- a/THANKS
+++ /dev/null
@@ -1,19 +0,0 @@
-Jason McIntyre
-Freddy Dissaux
-Alexandre Lissy
-Brad Arrington
-Todd T. Fries
-Francois Tigeot
-Ashish Shukla
-Jean-Loup Colautti
-Rune Lynge
-Jason A. Donenfeld
-Gleb Kozyrev
-Colin Didier
-Ryan Kavanagh
-Sunil Nimmagadda
-
-
-Miod Vallat
-
-And of course Gilles Chehade and Eric Faurot for their work and continuous help.
diff --git a/bootstrap b/bootstrap
new file mode 100755
index 00000000..24e29440
--- /dev/null
+++ b/bootstrap
@@ -0,0 +1,151 @@
+#! /bin/sh
+
+# bootstrap: generic bootstrap/autogen.sh script for autotools projects
+#
+# Copyright (c) 2002-2011 Sam Hocevar <sam@hocevar.net>
+#
+# This program is free software. It comes without any warranty, to
+# the extent permitted by applicable law. You can redistribute it
+# and/or modify it under the terms of the Do What The Fuck You Want
+# To Public License, Version 2, as published by Sam Hocevar. See
+# http://sam.zoy.org/wtfpl/COPYING for more details.
+#
+# The latest version of this script can be found at the following place:
+# http://caca.zoy.org/wiki/build
+
+# Die if an error occurs
+set -e
+
+# Guess whether we are using configure.ac or configure.in
+if test -f configure.ac; then
+ conffile="configure.ac"
+elif test -f configure.in; then
+ conffile="configure.in"
+else
+ echo "$0: could not find configure.ac or configure.in"
+ exit 1
+fi
+
+# Check for needed features
+auxdir="`sed -ne 's/^[ \t]*A._CONFIG_AUX_DIR *([[ ]*\([^] )]*\).*/\1/p' $conffile`"
+pkgconfig="`grep '^[ \t]*PKG_PROG_PKG_CONFIG' $conffile >/dev/null 2>&1 && echo yes || echo no`"
+libtool="`grep '^[ \t]*A._PROG_LIBTOOL' $conffile >/dev/null 2>&1 && echo yes || echo no`"
+header="`grep '^[ \t]*A._CONFIG_HEADER' $conffile >/dev/null 2>&1 && echo yes || echo no`"
+makefile="`[ -f Makefile.am ] && echo yes || echo no`"
+aclocalflags="`sed -ne 's/^[ \t]*ACLOCAL_AMFLAGS[ \t]*=//p' Makefile.am 2>/dev/null || :`"
+
+# Check for automake
+amvers="no"
+for v in 16.1 16 15 14 13; do
+ if automake-1.${v} --version >/dev/null 2>&1; then
+ amvers="-1.${v}"
+ break
+ elif automake1.${v} --version >/dev/null 2>&1; then
+ amvers="1.${v}"
+ break
+ fi
+done
+
+if test "${amvers}" = "no" && automake --version > /dev/null 2>&1; then
+ amvers="`automake --version | sed -e '1s/[^0-9]*//' -e q`"
+ if `echo "$amvers\n1.14" | sort -V | head -n 1 | grep -q "$amvers"`; then
+ amvers="no"
+ else
+ amvers=""
+ fi
+fi
+
+if test "$amvers" = "no"; then
+ echo "$0: you need automake version 1.14 or later"
+ exit 1
+fi
+
+# Check for autoconf
+acvers="no"
+for v in "" "269" "-2.69"; do
+ if autoconf${v} --version >/dev/null 2>&1; then
+ acvers="${v}"
+ break
+ fi
+done
+
+if test "$acvers" = "no"; then
+ echo "$0: you need autoconf"
+ exit 1
+fi
+
+# Check for libtool
+if test "$libtool" = "yes"; then
+ libtoolize="no"
+ if glibtoolize --version >/dev/null 2>&1; then
+ libtoolize="glibtoolize"
+ else
+ for v in "16" "15" "" "14"; do
+ if libtoolize${v} --version >/dev/null 2>&1; then
+ libtoolize="libtoolize${v}"
+ break
+ fi
+ done
+ fi
+
+ if test "$libtoolize" = "no"; then
+ echo "$0: you need libtool"
+ exit 1
+ fi
+fi
+
+# Check for pkg-config
+if test "$pkgconfig" = "yes"; then
+ if ! pkg-config --version >/dev/null 2>&1; then
+ echo "$0: you need pkg-config"
+ exit 1
+ fi
+fi
+
+# Remove old cruft
+for x in aclocal.m4 configure config.guess config.log config.sub config.cache config.h.in config.h compile libtool.m4 ltoptions.m4 ltsugar.m4 ltversion.m4 ltmain.sh libtool ltconfig missing mkinstalldirs depcomp install-sh; do rm -f $x autotools/$x; if test -n "$auxdir"; then rm -f "$auxdir/$x"; fi; done
+rm -Rf autom4te.cache
+if test -n "$auxdir"; then
+ if test ! -d "$auxdir"; then
+ mkdir "$auxdir"
+ fi
+ aclocalflags="${aclocalflags} -I $auxdir -I ."
+fi
+
+# Honour M4PATH because sometimes M4 doesn't
+save_IFS=$IFS
+IFS=:
+tmp="$M4PATH"
+for x in $tmp; do
+ if test -n "$x"; then
+ aclocalflags="${aclocalflags} -I $x"
+ fi
+done
+IFS=$save_IFS
+
+# Explain what we are doing from now
+set -x
+
+# Bootstrap package
+if test "$libtool" = "yes"; then
+ ${libtoolize} --copy --force
+ if test -n "$auxdir" -a ! "$auxdir" = "." -a -f "ltmain.sh"; then
+ echo "$0: working around a minor libtool issue"
+ mv ltmain.sh "$auxdir/"
+ fi
+fi
+
+aclocal${amvers} ${aclocalflags}
+autoconf${acvers}
+if test "$header" = "yes"; then
+ autoheader${acvers}
+fi
+if test "$makefile" = "yes"; then
+ #add --include-deps if you want to bootstrap with any other compiler than gcc
+ #automake${amvers} --add-missing --copy --include-deps
+ automake${amvers} --foreign --add-missing --copy
+fi
+
+# Remove cruft that we no longer want
+rm -Rf autom4te.cache
+
diff --git a/ci/COVERITY.MD5SUM b/ci/COVERITY.MD5SUM
new file mode 100644
index 00000000..67b8a2de
--- /dev/null
+++ b/ci/COVERITY.MD5SUM
@@ -0,0 +1 @@
+d0d7d7df9d6609e578f85096a755fb8f ./cov-analysis-linux64.tgz
diff --git a/ci/README.md b/ci/README.md
new file mode 100644
index 00000000..2eee08f1
--- /dev/null
+++ b/ci/README.md
@@ -0,0 +1,83 @@
+# Continuous Integration
+
+This directory contains CI/CD related scripts and resources
+
+CI/CD process leverages GitHub Actions as a primary automation platform since
+up to 20 parallel workflows are available for opensource projects.
+
+On high level we have multiple dimensions to test:
+
+ - OS Distribution
+ - TLS library implementation: libress, openssl
+ - libc implementations: glibc, musl
+ - compiler: gcc, clang (not yet tested)
+
+Within this matrix build tests, static code analysis, functional and
+integration tests are planned. Currently only build tests and static code
+analysis exist. Help is much needed with developing functional and integration
+tests.
+
+## Directory strucuture
+
+- [docker](#dockerfiles) dockerfiles for various distributions
+- [scripts](#scripts) useful scripts for ci/cd automation
+
+
+## Design Considerations
+
+- Keep workflow yaml files and execution logic as separate as possible.
+ Reference ci scripts from workflow files to allow running same tests
+ locally, without depending on github.
+
+
+
+# Dockerfiles
+
+Dockerfiles in [docker](docker/) directory can be used for developing and
+testing OpenSMTPD. These dockerfiles are intended to be used for dev/test
+cycle ONLY and ARE NOT intended to be a delivery mechanism for end users and
+should not be published on external resouces like DockerHub. Dockerfiles in
+this folder can be used as a reference for package maintainers of various
+distributions.
+
+
+## Usage
+
+OpenSMTPD provides a set of dockerfiles for getting started with development
+quickly locally or with GitHub's Actions.
+
+For each distribution there is a separate dockerfile with a distro name
+suffixed. E.g. `Dockerfile.alpine` is a dockerfile that builds OpenSMTPD in
+Alpine Linux environment.
+
+To build:
+
+ docker build -f docker/Dockerfile.alpine -t opensmtpd-alpine
+
+
+All configuration files that are in `/etc/mail` are taken from `etc/` directory.
+
+
+To run the container that you've just built run:
+
+ docker run --name smtpd_server -p 25:25 opensmtpd-alpine
+
+
+
+# Scripts
+
+Scripts to automate ci/cd actions:
+
+- [coverity_scan](scripts/coverity_scan.sh) - runs coverity scan and submits
+ the rusult to Coverity. See script contents for usage instructions.
+
+- [generate_certs](scripts/generate_certs.sh) - convenient script to quickly
+ generate some TLS certificates. Useful for testing.
+
+# Historical reference
+
+[Initial design discusstion](https://github.com/OpenSMTPD/OpenSMTPD/issues/947)
+
+
+
+
diff --git a/ci/docker/Dockerfile.alpine b/ci/docker/Dockerfile.alpine
new file mode 100644
index 00000000..2c7c66fc
--- /dev/null
+++ b/ci/docker/Dockerfile.alpine
@@ -0,0 +1,50 @@
+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
+WORKDIR /opensmtpd
+
+# install necessary packages
+RUN apk add --no-cache \
+ autoconf \
+ automake \
+ bison \
+ ca-certificates \
+ fts-dev \
+ gcc \
+ fts \
+ libevent-dev \
+ libtool \
+ libtool \
+ linux-pam-dev \
+ make \
+ musl-dev \
+ libressl \
+ libressl-dev \
+ zlib-dev
+
+# create users and directories
+# note: alpine uses busybox and useradd is not available there
+# also long flags are not available too, so sorry for the
+RUN mkdir -p /var/lib/opensmtpd/empty \
+ && adduser _smtpd -h /var/lib/opensmtpd/empty/ -D -H -s /bin/false \
+ && adduser _smtpq -h /var/lib/opensmtpd/empty/ -D -H -s /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
+
+# build 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.archlinux b/ci/docker/Dockerfile.archlinux
new file mode 100644
index 00000000..dcd46684
--- /dev/null
+++ b/ci/docker/Dockerfile.archlinux
@@ -0,0 +1,62 @@
+FROM archlinux
+
+# 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 pacman -Suy --noconfirm \
+ base \
+ make \
+ m4 \
+ grep \
+ gcc \
+ automake \
+ autoconf \
+ libtool \
+ bison \
+ gettext \
+ libevent \
+ libressl \
+ pam \
+ zlib
+
+
+# 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
+
+
+# build opensmtpd
+RUN ./bootstrap \
+ && ./configure --with-gnu-ld \
+ --sysconfdir=/etc/mail \
+ --with-cflags='-I/usr/include/libressl -L/usr/lib/libressl -Wl,-rpath=/usr/lib/libressl' \
+ --with-path-empty=/var/lib/opensmtpd/empty \
+ --with-auth-pam \
+ && make \
+ && make install \
+ && cp etc/aliases /etc/mail/aliases
+
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
new file mode 100644
index 00000000..6626033d
--- /dev/null
+++ b/ci/docker/Dockerfile.ubuntu
@@ -0,0 +1,52 @@
+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
+
+# 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.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/ci/scripts/clang_scan.sh b/ci/scripts/clang_scan.sh
new file mode 100755
index 00000000..714d55a7
--- /dev/null
+++ b/ci/scripts/clang_scan.sh
@@ -0,0 +1,60 @@
+#!/bin/sh
+set -eu
+
+# Unconditionally go to the root level of the git repo.
+# If you invoke it from outside of the repo go to
+# the script location first
+cd "$(dirname "$0")"
+cd "$(git rev-parse --show-toplevel)"
+
+# Clang Scan script
+#
+# USAGE:
+# - clang must be installed
+# - make sure you have clean repository,
+# e.g. git clean -ffdx
+# - if you want to download github badge set CLANG_SCAN_BADGE_REQUIRED variable
+# - Run script from anywhere inside the repository
+# ./ci/scripts/clang_scan.sh
+# or
+# CLANG_SCAN_BADGE_REQUIRED=1 ./ci/scripts/clang_scan.sh
+#
+
+if ! type scan-build > /dev/null; then
+ echo "clang scan-build is missing"
+ exit 1
+fi
+
+# Unconditionally go to the root level of the git repo.
+# If you invoke it from outside of the repo go to
+# the script location first
+cd "$(dirname "$0")"
+# This moves us to the root of the repo
+cd "$(git rev-parse --show-toplevel)"
+
+# Get short SHA of the HEAD
+sha=$(git rev-parse --short HEAD)
+
+results_dir=${CLANG_SCAN_RESULTS_DIR:-clang-report}
+mkdir -p "$results_dir"
+
+# Build with scan-build
+./bootstrap
+./configure
+scan-build -o "$results_dir" \
+ --keep-empty \
+ --html-title="OpenSMTPD $sha" make
+
+
+set -x
+# conditionally generate badge
+if [ -z "${CLANG_SCAN_BADGE_REQUIRED:-}" ]; then
+ echo "Skipping badge generation"
+else
+ echo "Generating badge"
+ . ci/scripts/imports/badge.sh
+ cd "$results_dir"
+ cd "$( find . -type d | sort | tail -n1 )"
+ issues_nr="$( find . -name "report-*" | wc -l)"
+ download_badge "$issues_nr" "clang analysis" "$(pwd)" 30
+fi
diff --git a/ci/scripts/coverity_scan.sh b/ci/scripts/coverity_scan.sh
new file mode 100755
index 00000000..ab302767
--- /dev/null
+++ b/ci/scripts/coverity_scan.sh
@@ -0,0 +1,77 @@
+#!/bin/sh
+set -eu
+
+# Coverity Scan script
+# Steps closely follow official documentation https://scan.coverity.com/download
+#
+# USAGE: provide coverity project token as 'token' environment variable and run
+# token=abcdedf ./ci/scripts/coverity_scan.sh
+#
+# Or uncomment this line and put token here. But do not commit this to git.
+# token=""
+project_name="OpenSMTPD%2FOpenSMTPD"
+cov_analysis_url="https://scan.coverity.com/download/cxx/linux64"
+maintainer="ihor@antonovs.family"
+
+# Unconditionally go to the root level of the git repo.
+# If you invoke it from outside of the repo go to
+# the script location first
+cd "$(dirname "$0")"
+# This moves us to the root of the repo
+cd "$(git rev-parse --show-toplevel)"
+
+# Get short SHA of the HEAD
+sha=$(git rev-parse --short HEAD)
+
+# Download Coverity Build Tool if absent
+set +x
+# shellcheck disable=SC2154
+md5sum -c ./ci/COVERITY.MD5SUM || wget $cov_analysis_url \
+ --post-data "token=$token&project=$project_name" \
+ -O cov-analysis-linux64.tgz
+set -x
+
+#Check MD5
+md5sum -c ./ci/COVERITY.MD5SUM
+
+# Extract Coverty Scan Tool
+rm -rf ./cov-analysis-linux64
+mkdir -p cov-analysis-linux64
+tar xzf cov-analysis-linux64.tgz --strip 1 -C cov-analysis-linux64
+
+# export PATH=$(pwd)/cov-analysis-linux64/bin:$PATH
+
+# Build with cov-build
+./bootstrap
+./configure
+cov-analysis-linux64/bin/cov-build --dir cov-int make
+
+# Compress the rusults
+tar czvf opensmtpd.tgz cov-int
+
+
+# Submit the result to Coverity Scan
+# Some parts are shamelessly taken from:
+# https://scan.coverity.com/scripts/travisci_build_coverity_scan.sh
+set +x
+response=$(curl \
+ --silent \
+ --write-out "\n%{http_code}\n" \
+ --form token="$token" \
+ --form email="$maintainer" \
+ --form file=@opensmtpd.tgz \
+ --form version="portable-$sha" \
+ --form description="daily scan" \
+ "https://scan.coverity.com/builds?project=$project_name")
+set -x
+
+status_code=$(echo "$response" | sed -n '$p')
+
+if [ "$status_code" != "200" ]; then
+ text=$(echo "$response" | sed '$d')
+ echo -e "Coverity Scan upload failed: $text"
+ exit 1
+fi
+
+
+
diff --git a/ci/scripts/generate_certs.sh b/ci/scripts/generate_certs.sh
new file mode 100755
index 00000000..a9249c1e
--- /dev/null
+++ b/ci/scripts/generate_certs.sh
@@ -0,0 +1,33 @@
+#!/bin/sh
+
+# Generate self-signed SSL certs
+# Usage: ./generate_certs.sh
+
+days=3560 # 10 years
+config="$(dirname "$0")/ssl.conf"
+cert="open.smtpd.cert"
+key="open.smtpd.key"
+csr="open.smtpd.csr"
+
+# Key + CSR generation:
+openssl req \
+ -new \
+ -x509 \
+ -newkey rsa:2048 \
+ -sha256 \
+ -nodes \
+ -keyout $key \
+ -out $csr \
+ -days $days \
+ -config "$config"
+
+# Certificate generation:
+openssl req \
+ -new \
+ -x509 \
+ -newkey rsa:2048 \
+ -days $days \
+ -nodes \
+ -config "$config" \
+ -keyout $key \
+ -out $cert
diff --git a/ci/scripts/imports/badge.sh b/ci/scripts/imports/badge.sh
new file mode 100644
index 00000000..d6cf0b3a
--- /dev/null
+++ b/ci/scripts/imports/badge.sh
@@ -0,0 +1,72 @@
+#!/bin/sh
+# Copyright 2019 Neovim Project Contributors (https://neovim.io/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Helper functions for getting badges.
+
+# Get code quality color.
+# ${1}: Amount of bugs actually found.
+# ${2}: Maximum number of bugs above which color will be red. Defaults to 20.
+# ${3}: Maximum number of bugs above which color will be yellow. Defaults to
+# $1 / 2.
+# Output: 24-bit hexadecimal representation of the color (xxxxxx).
+get_code_quality_color() {
+ bugs=$1 ; shift # shift will fail if there is no argument
+ max_bugs=${1:-20}
+ yellow_threshold=${2:-$(( max_bugs / 2 ))}
+
+ red=255
+ green=255
+ blue=0
+
+ bugs=$(( bugs < max_bugs ? bugs : max_bugs))
+ if test $bugs -ge "$yellow_threshold" ; then
+ green=$(( 255 - 255 * (bugs - yellow_threshold) / yellow_threshold ))
+ else
+ red=$(( 255 * bugs / yellow_threshold ))
+ fi
+
+ printf "%02x%02x%02x" $red $green $blue
+}
+
+# Get code quality badge.
+# ${1}: Amount of bugs actually found.
+# ${2}: Badge text.
+# ${3}: Directory where to save badge to.
+# ${3}: Maximum number of bugs above which color will be red. Defaults to 20.
+# ${4}: Maximum number of bugs above which color will be yellow. Defaults to
+# $1 / 2.
+# Output: 24-bit hexadecimal representation of the color (xxxxxx).
+download_badge() {
+ bugs=$1 ; shift
+ badge_text="$1" ; shift
+ reports_dir="$1" ; shift
+ max_bugs=${1:-20}
+ yellow_threshold=${2:-$(( max_bugs / 2 ))}
+
+ code_quality_color="$(
+ get_code_quality_color $bugs $max_bugs $yellow_threshold)"
+ badge="${badge_text}-${bugs}-${code_quality_color}"
+
+ rm -f "$reports_dir/badge.svg"
+
+ response="$(
+ curl --tlsv1 "https://img.shields.io/badge/${badge}.svg" \
+ -o"$reports_dir/badge.svg" 2>&1)"
+
+ if ! grep -F 'xmlns="http://www.w3.org/2000/svg"' "$reports_dir/badge.svg" ; then
+ echo "Failed to download badge to $reports_dir: $response"
+ rm -f "$reports_dir/badge.svg"
+ fi
+}
diff --git a/ci/scripts/ssl.conf b/ci/scripts/ssl.conf
new file mode 100644
index 00000000..eddfb7f8
--- /dev/null
+++ b/ci/scripts/ssl.conf
@@ -0,0 +1,23 @@
+[req]
+default_bits = 2048
+prompt = no
+default_md = sha256
+x509_extensions = v3_req
+distinguished_name = dn
+
+
+# Puffy the pufferfish
+# https://en.wikipedia.org/wiki/Tetraodontidae
+[dn]
+C = AZ
+ST = Chordata
+L = Actinopterygii_Tetraodontiformes
+O = Tetraodontoidei_Tetraodontidae
+CN = Puffy
+
+[v3_req]
+subjectAltName = @alt_names
+
+[alt_names]
+DNS.1 = puffy.bsd
+DNS.2 = puffy.mail
diff --git a/configure.ac b/configure.ac
new file mode 100644
index 00000000..d46aeae7
--- /dev/null
+++ b/configure.ac
@@ -0,0 +1,2072 @@
+# $Id: configure.ac,v 1.519 2013/03/22 01:49:15 dtucker Exp $
+#
+# Copyright (c) 2016 Gilles Chehade <gilles@poolp.org>
+# Copyright (c) 1999-2004 Damien Miller
+#
+# 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.
+
+
+#
+# WE NEED TO CLEANUP CONFIGURE.AC AND MAKE IT FOLLOW THE
+# STANDARD LAYOUT ...
+#
+# 3.1.3 Standard configure.ac Layout
+#
+# https://www.gnu.org/software/autoconf/manual/autoconf-2.69/html_node/Autoconf-Input-Layout.html
+#
+
+
+#
+# AUTOCONF REQUIREMENTS
+#
+AC_PREREQ(2.69)
+
+
+#
+# AC_INIT
+#
+AC_INIT([OpenSMTPD],
+ [portable],
+ [bugs@opensmtpd.org],
+ [opensmtpd],
+ [https://www.OpenSMTPD.org])
+
+AM_INIT_AUTOMAKE([subdir-objects no-dependencies])
+LT_INIT
+
+# here we should test for variables set by libtool detection
+if test "x$with_pic" != "xno"; then
+ CFLAGS="$CFLAGS ${pic_flag}"
+fi
+
+
+#
+# PACKAGE INFORMATION
+#
+AC_LANG([C])
+AC_CONFIG_MACRO_DIR([m4])
+AC_CONFIG_HEADER([config.h])
+AC_PROG_CC
+AC_CANONICAL_HOST
+AC_C_BIGENDIAN
+
+
+#
+# CHECKS FOR PROGRAMS
+#
+AC_PROG_CPP
+AC_PROG_INSTALL
+AC_PROG_LIBTOOL
+AC_PATH_PROG([AR], [ar])
+AC_PATH_PROG([CAT], [cat])
+AC_PATH_PROG([CHMOD], [chmod])
+AC_PATH_PROG([CHOWN], [chown])
+AC_PATH_PROG([ZCAT], [zcat])
+AC_PATH_PROG([SED], [sed])
+AC_PATH_PROG([TEST_MINUS_S_SH], [bash])
+AC_PATH_PROG([TEST_MINUS_S_SH], [ksh])
+AC_PATH_PROG([TEST_MINUS_S_SH], [sh])
+AC_PATH_PROG([SH], [sh])
+AC_PATH_PROG([GROFF], [groff])
+AC_PATH_PROG([NROFF], [nroff])
+AC_PATH_PROG([MANDOC], [mandoc])
+AC_PROG_YACC
+
+AC_SUBST([ZCAT])
+
+
+if test -z "$AR"; then
+ AC_MSG_ERROR([*** 'ar' missing, please install or fix your \$PATH ***])
+fi
+
+if test -z "$LD"; then
+ LD=$CC
+fi
+AC_SUBST([LD])
+
+dnl select manpage formatter
+if test -n "$MANDOC"; then
+ MANFMT="$MANDOC"
+elif test -n "$NROFF"; then
+ MANFMT="$NROFF -mandoc"
+elif test -n "$GROFF"; then
+ MANFMT="$GROFF -mandoc -Tascii"
+else
+ AC_MSG_WARN([no manpage formatted found])
+ MANFMT="false"
+fi
+AC_SUBST([MANFMT])
+
+
+#
+# CHECKS FOR LIBRARIES
+#
+
+
+
+#
+# CHECKS FOR HEADERS
+#
+AC_CHECK_HEADERS([ \
+ arpa/nameser_compat.h \
+ crypt.h \
+ dirent.h \
+ err.h \
+ fcntl.h \
+ getopt.h \
+ grp.h \
+ libgen.h \
+ limits.h \
+ maillock.h \
+ mach/mach_time.h \
+ ndir.h \
+ netdb.h \
+ pam/pam_appl.h \
+ paths.h \
+ security/pam_appl.h \
+ shadow.h \
+ sys/cdefs.h \
+ sys/dir.h \
+ sys/file.h \
+ sys/mount.h \
+ sys/ndir.h \
+ sys/pstat.h \
+ sys/statfs.h \
+ sys/time.h \
+ sys/un.h \
+ time.h \
+ ucred.h \
+ util.h \
+ vis.h
+])
+
+AM_CONDITIONAL([NEED_ERR_H], [test x$HAVE_ERR_H = x1])
+AM_CONDITIONAL([SUPPORT_PATHS_H], [test x$HAVE_PATHS_H = x1])
+
+# NetBSD requires sys/types.h before login_cap.h
+AC_CHECK_HEADERS([login_cap.h], [], [], [
+#include <sys/types.h>
+])
+
+# older BSDs need sys/param.h before sys/mount.h
+AC_CHECK_HEADERS([sys/mount.h], [], [], [
+#include <sys/param.h>
+])
+
+AC_CHECK_HEADERS([bsd/libutil.h libutil.h])
+
+AC_CHECK_HEADER([fts.h],
+ [],
+ [AC_MSG_ERROR([*** fts.h missing - please install libfts ***])],
+ [
+#include <sys/types.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
+#
+AC_CHECK_TYPES([long long, unsigned long long, long double, u_int, u_char])
+AC_CHECK_SIZEOF([short int], [2])
+AC_CHECK_SIZEOF([int], [4])
+AC_CHECK_SIZEOF([long int], [4])
+AC_CHECK_SIZEOF([long long int], [8])
+
+AC_TYPE_INT8_T
+AC_TYPE_INT16_T
+AC_TYPE_INT32_T
+AC_TYPE_INT64_T
+AC_TYPE_UINT8_T
+AC_TYPE_UINT16_T
+AC_TYPE_UINT32_T
+AC_TYPE_UINT64_T
+AC_TYPE_INTPTR_T
+AC_TYPE_INTMAX_T
+AC_TYPE_UINTPTR_T
+AC_TYPE_UINTMAX_T
+AC_TYPE_SIZE_T
+AC_TYPE_SSIZE_T
+AC_TYPE_OFF_T
+AC_TYPE_MODE_T
+AC_TYPE_PID_T
+AC_TYPE_UID_T
+
+TYPE_SOCKLEN_T
+
+AC_CHECK_TYPES([sig_atomic_t], [], [], [
+#include <signal.h>
+])
+AC_CHECK_TYPES([fsblkcnt_t, fsfilcnt_t], [], [], [
+#include <sys/types.h>
+#ifdef HAVE_SYS_BITYPES_H
+#include <sys/bitypes.h>
+#endif
+#ifdef HAVE_SYS_STATFS_H
+#include <sys/statfs.h>
+#endif
+#ifdef HAVE_SYS_STATVFS_H
+#include <sys/statvfs.h>
+#endif
+])
+
+AC_CHECK_TYPES([in_addr_t, in_port_t], [], [], [
+#include <sys/types.h>
+#include <netinet/in.h>
+])
+
+AC_CHECK_TYPES([sa_family_t], [], [], [
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+])
+
+AC_CHECK_TYPES([struct timespec])
+AC_CHECK_TYPES([struct ifgroupreq])
+AC_CHECK_TYPES([struct sockaddr_storage], [], [], [
+#include <sys/types.h>
+#include <sys/socket.h>
+])
+AC_CHECK_TYPES([struct sockaddr_in6], [], [], [
+#include <sys/types.h>
+#include <netinet/in.h>
+])
+AC_CHECK_TYPES([struct in6_addr], [], [], [
+#include <sys/types.h>
+#include <netinet/in.h>
+])
+AC_CHECK_TYPES([struct addrinfo], [], [], [
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netdb.h>
+])
+AC_CHECK_TYPES([struct timeval], [], [], [
+#include <sys/time.h>
+])
+
+AC_CHECK_DECL([LLONG_MAX], [have_llong_max=1], , [#include <limits.h>])
+
+
+#
+# CHECKS FOR STRUCTURES
+#
+AC_CHECK_MEMBERS([struct sockaddr_in6.sin6_scope_id], [], [], [
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#include <netinet/in.h>
+])
+
+AC_CHECK_MEMBERS([struct passwd.pw_gecos, struct passwd.pw_class,
+struct passwd.pw_change, struct passwd.pw_expire],
+[], [], [
+#include <sys/types.h>
+#include <pwd.h>
+])
+
+AC_CHECK_MEMBERS([struct stat.st_flags], , ,
+ [ #include <sys/types.h>
+ #include <sys/stat.h> ]
+)
+
+AC_CHECK_MEMBERS([struct stat.st_mtim], , ,
+ [ #include <sys/types.h>
+ #include <sys/stat.h> ]
+)
+
+AC_CHECK_MEMBERS([struct stat.st_mtimespec], , ,
+ [ #include <sys/types.h>
+ #include <sys/stat.h> ]
+)
+
+AC_CHECK_MEMBERS([struct sockaddr.sa_len], , ,
+ [ #include <netdb.h>
+ #include <netinet/in.h>
+ #include <sys/socket.h> ]
+)
+
+AC_CHECK_MEMBERS([struct sockaddr_storage.ss_len], , ,
+ [ #include <netdb.h>
+ #include <netinet/in.h>
+ #include <sys/socket.h> ]
+)
+
+AC_CHECK_MEMBERS([struct sockaddr_in.sin_len], , ,
+ [ #include <netdb.h>
+ #include <netinet/in.h>
+ #include <sys/socket.h> ]
+)
+
+AC_CHECK_MEMBERS([struct sockaddr_in6.sin6_len], , ,
+ [ #include <netdb.h>
+ #include <netinet/in.h>
+ #include <sys/socket.h> ]
+)
+
+AC_CHECK_MEMBERS([struct statfs.f_favail], , ,
+ [ #include <sys/vfs.h>
+ #include <sys/statfs.h> ]
+)
+
+AC_CHECK_MEMBERS([struct sockaddr_storage.ss_family], [], [], [
+#include <sys/types.h>
+#include <sys/socket.h>
+])
+
+AC_CHECK_MEMBERS([struct sockaddr_storage.__ss_family], [], [], [
+#include <sys/types.h>
+#include <sys/socket.h>
+])
+
+AC_CHECK_MEMBERS([struct tm.tm_gmtoff, struct tm.tm_zone], [],
+ [
+ AC_CHECK_DECLS([timezone, altzone, tzname],
+ [],
+ [ AC_MSG_ERROR([cannot find timezone])],
+ [ #include <time.h> ]
+ )
+ ],
+ [ #include <time.h> ]
+)
+
+AC_CHECK_MEMBERS([struct DIR.d_type], [], [], [
+#include <sys/types.h>
+#include <dirent.h>
+])
+
+#
+# CHECKS FOR DECLARATIONS
+#
+AC_CHECK_DECLS([O_NONBLOCK], [], [], [
+#include <sys/types.h>
+#ifdef HAVE_SYS_STAT_H
+# include <sys/stat.h>
+#endif
+#ifdef HAVE_FCNTL_H
+# include <fcntl.h>
+#endif
+])
+
+AC_CHECK_DECLS([AF_LOCAL, PF_LOCAL], [], [], [
+#include <sys/socket.h>
+])
+
+AC_CHECK_DECLS([IPPORT_HILASTAUTO], [], [], [
+#include <netinet/in.h>
+])
+
+AC_CHECK_DECLS([WAIT_MYPGRP], [], [], [
+#include <sys/wait.h>
+])
+
+AC_CHECK_DECLS([writev], [], [], [
+#include <sys/types.h>
+#include <sys/uio.h>
+#include <unistd.h>
+])
+
+AC_CHECK_DECLS([LOG_PERROR], [], [], [
+#include <syslog.h>
+])
+
+
+#
+# CHECKS FOR COMPILER CHARACTERISTICS
+#
+AC_C_INLINE
+
+
+AC_ARG_WITH([libs],
+ [ --with-libs Specify additional libraries to link with],
+ [
+ if test -n "$withval" -a "$withval" != "no" -a "${withval}" != "yes"; then
+ LIBS="$LIBS $withval"
+ fi
+ ]
+)
+#
+# CHECKS FOR LIBRARY FUNCTIONS
+#
+AC_SEARCH_LIBS([basename],
+ [gen],
+ [
+ AC_DEFINE([HAVE_BASENAME], [1],
+ [Define if you have the basename() function.])
+ ])
+
+AC_SEARCH_LIBS([closefrom],
+ [gen],
+ [
+ AC_DEFINE([HAVE_CLOSEFROM], [1],
+ [Define if you have the closefrom() function.])
+ AC_COMPILE_IFELSE(
+ [
+ AC_LANG_PROGRAM([AC_INCLUDES_DEFAULT], [[int res = closefrom(0);]])
+ ],
+ AC_DEFINE(HAVE_CLOSEFROM_INT, 1, [closefrom return int])
+ )
+ ])
+
+AC_SEARCH_LIBS([fmt_scaled],
+ [util bsd],
+ [
+ AC_DEFINE([HAVE_FMT_SCALED], [1],
+ [Define if you have the fmt_scaled() function.])
+ ])
+
+AC_SEARCH_LIBS([dirname],
+ [gen],
+ [
+ AC_DEFINE([HAVE_DIRNAME], [1],
+ [Define if you have the dirname() function.])
+ ])
+
+AC_SEARCH_LIBS([inet_net_pton],
+ [resolv bsd],
+ [
+ AC_DEFINE([HAVE_INET_NET_PTON], [1],
+ [Define if you have the inet_net_pton() function.])
+ ])
+
+AC_SEARCH_LIBS([clock_gettime],
+ [rt],
+ [
+ AC_DEFINE([HAVE_CLOCK_GETTIME], [1],
+ [Define if you have the clock_gettime() function.])
+ ])
+
+AC_SEARCH_LIBS([fts_open],
+ [fts],
+ [
+ AC_DEFINE([HAVE_FTS_OPEN], [1],
+ [Define if you have the fts_open() function.])
+ ])
+
+AC_SEARCH_LIBS([daemon],
+ [bsd],
+ [
+ AC_DEFINE([HAVE_DAEMON], [1],
+ [Define if you have the daemon() function.])
+ ])
+
+AC_SEARCH_LIBS([fparseln],
+ [util],
+ [
+ AC_DEFINE([HAVE_FPARSELN], [1],
+ [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],
+ [
+ AC_DEFINE([HAVE_RES_9_B64_NTOP], [1],
+ [Define if you have the res_9_b64_ntop() function.])
+ ])
+
+AC_SEARCH_LIBS([__b64_pton],
+ [resolv],
+ [
+ AC_DEFINE([HAVE___B64_PTON], [1],
+ [Define if you have the __b64_pton() function.])
+ ])
+
+AC_SEARCH_LIBS([b64_pton],
+ [resolv],
+ [
+ AC_DEFINE([HAVE_B64_PTON], [1],
+ [Define if you have the b64_pton() function.])
+ ])
+
+AC_SEARCH_LIBS([__b64_ntop],
+ [resolv],
+ [
+ AC_DEFINE([HAVE___B64_NTOP], [1],
+ [Define if you have the b64_ntop() function.])
+ ])
+
+AC_SEARCH_LIBS([b64_ntop],
+ [resolv],
+ [
+ AC_DEFINE([HAVE_B64_NTOP], [1],
+ [Define if you have the b64_ntop() function.])
+ ])
+
+AC_SEARCH_LIBS([setsockopt],
+ [socket],
+ [
+ AC_DEFINE([HAVE_SETSOCKOPT], [1],
+ [Define if you have the setsockopt() function.])
+ ])
+
+AC_SEARCH_LIBS([crypt],
+ [crypt],
+ [
+ AC_DEFINE([HAVE_CRYPT], [1],
+ [Define if you have the crypt() function.])
+ ])
+
+AC_SEARCH_LIBS([imsg_init],
+ [util],
+ [
+ AC_DEFINE([HAVE_IMSG], [1],
+ [Define if you have the imsg framework.])
+ ])
+
+AC_SEARCH_LIBS([event_asr_run],
+ [event],
+ [
+ AC_DEFINE([HAVE_EVENT_ASR_RUN], [1],
+ [Define if you have the event_asr_run() function.])
+ ])
+
+AC_CHECK_FUNCS([ \
+ asprintf \
+ arc4random \
+ bcopy \
+ calloc_conceal \
+ chflags \
+ crypt_checkpass \
+ dirfd \
+ err \
+ errc \
+ errx \
+ explicit_bzero \
+ fchflags \
+ fgetln \
+ flock \
+ freeaddrinfo \
+ freezero \
+ getaddrinfo \
+ getdtablesize \
+ getdtablecount \
+ getline \
+ getnameinfo \
+ gettimeofday \
+ getopt \
+ getpeereid \
+ getpeerucred \
+ getspnam \
+ inet_aton \
+ inet_ntoa \
+ inet_ntop \
+ malloc_conceal \
+ memmove \
+ nanosleep \
+ nsleep \
+ pipe2 \
+ pidfile \
+ pledge \
+ reallocarray \
+ recallocarray \
+ res_hnok \
+ res_randomid \
+ setenv \
+ seteuid \
+ setegid \
+ setproctitle \
+ setregid \
+ setreuid \
+ setresuid \
+ setresgid \
+ setsid \
+ signal \
+ sigaction \
+ snprintf \
+ socketpair \
+ strdup \
+ strerror \
+ strlcat \
+ strlcpy \
+ strmode \
+ strndup \
+ strnlen \
+ strnvis \
+ strtonum \
+ sysconf \
+ tcgetpgrp \
+ time \
+ usleep \
+ vasprintf \
+ vsnprintf \
+ waitpid \
+ warn \
+ warnx \
+])
+
+AC_CHECK_DECL([strsep],
+ [AC_CHECK_FUNCS([strsep])],
+ [],
+ [
+#ifdef HAVE_STRING_H
+# include <string.h>
+#endif
+ ])
+
+
+# These functions might be found in libressl
+AC_CHECK_DECLS([strlcat, strlcpy],
+ [],
+ [],
+ [])
+
+#
+# CHECKS FOR SYSTEM SERVICES
+#
+AC_MSG_CHECKING([for /proc/pid/fd directory])
+if test -d "/proc/$$/fd"; then
+ AC_DEFINE([HAVE_PROC_PID], [1], [Define if you have /proc/$pid/fd])
+ AC_MSG_RESULT([yes])
+else
+ AC_MSG_RESULT([no])
+fi
+
+
+#
+# AC_CONFIG_FILES
+#
+
+#
+# AC_OUTPUT
+#
+
+
+###
+### EVERYTHING BELOW MUST BE CLEANED AND MOVED ABOVE
+###
+
+#l150 (without _FORTIFY_SOURCE=2)
+if test "$GCC" = "yes" -o "$GCC" = "egcs"; then
+ OSSH_CHECK_CFLAG_COMPILE([-Qunused-arguments])
+ OSSH_CHECK_CFLAG_COMPILE([-Wunknown-warning-option])
+ OSSH_CHECK_CFLAG_COMPILE([-Wall])
+ OSSH_CHECK_CFLAG_COMPILE([-Wpointer-arith])
+ OSSH_CHECK_CFLAG_COMPILE([-Wuninitialized])
+ OSSH_CHECK_CFLAG_COMPILE([-Wsign-compare])
+ OSSH_CHECK_CFLAG_COMPILE([-Wformat-security])
+ OSSH_CHECK_CFLAG_COMPILE([-Wsizeof-pointer-memaccess])
+ OSSH_CHECK_CFLAG_COMPILE([-Wpointer-sign], [-Wno-pointer-sign])
+ OSSH_CHECK_CFLAG_COMPILE([-Wunused-result], [-Wno-unused-result])
+ OSSH_CHECK_CFLAG_COMPILE([-fno-strict-aliasing])
+# OSSH_CHECK_CFLAG_COMPILE([-D_FORTIFY_SOURCE=2])
+ if test "x$use_toolchain_hardening" = "x1"; then
+ OSSH_CHECK_LDFLAG_LINK([-Wl,-z,relro])
+ OSSH_CHECK_LDFLAG_LINK([-Wl,-z,now])
+ OSSH_CHECK_LDFLAG_LINK([-Wl,-z,noexecstack])
+ # NB. -ftrapv expects certain support functions to be present in
+ # the compiler library (libgcc or similar) to detect integer operations
+ # that can overflow. We must check that the result of enabling it
+ # actually links. The test program compiled/linked includes a number
+ # of integer operations that should exercise this.
+ OSSH_CHECK_CFLAG_LINK([-ftrapv])
+ fi
+ AC_MSG_CHECKING([gcc version])
+ GCC_VER=`$CC -v 2>&1 | $AWK '/gcc version /{print $3}'`
+ case $GCC_VER in
+ 1.*) no_attrib_nonnull=1 ;;
+ 2.8* | 2.9*)
+ no_attrib_nonnull=1
+ ;;
+ 2.*) no_attrib_nonnull=1 ;;
+ *) ;;
+ esac
+ AC_MSG_RESULT([$GCC_VER])
+
+ AC_MSG_CHECKING([if $CC accepts -fno-builtin-memset])
+ saved_CFLAGS="$CFLAGS"
+ CFLAGS="$CFLAGS -fno-builtin-memset"
+ AC_LINK_IFELSE([AC_LANG_PROGRAM([[ #include <string.h> ]],
+ [[ char b[10]; memset(b, 0, sizeof(b)); ]])],
+ [ AC_MSG_RESULT([yes]) ],
+ [ AC_MSG_RESULT([no])
+ CFLAGS="$saved_CFLAGS" ]
+ )
+
+ # -fstack-protector-all doesn't always work for some GCC versions
+ # and/or platforms, so we test if we can. If it's not supported
+ # on a given platform gcc will emit a warning so we use -Werror.
+ if test "x$use_stack_protector" = "x1"; then
+ for t in -fstack-protector-strong -fstack-protector-all \
+ -fstack-protector; do
+ AC_MSG_CHECKING([if $CC supports $t])
+ saved_CFLAGS="$CFLAGS"
+ saved_LDFLAGS="$LDFLAGS"
+ CFLAGS="$CFLAGS $t -Werror"
+ LDFLAGS="$LDFLAGS $t -Werror"
+ AC_LINK_IFELSE(
+ [AC_LANG_PROGRAM([[ #include <stdio.h> ]],
+ [[
+ char x[256];
+ snprintf(x, sizeof(x), "XXX");
+ ]])],
+ [ AC_MSG_RESULT([yes])
+ CFLAGS="$saved_CFLAGS $t"
+ LDFLAGS="$saved_LDFLAGS $t"
+ AC_MSG_CHECKING([if $t works])
+ AC_RUN_IFELSE(
+ [AC_LANG_PROGRAM([[ #include <stdio.h> ]],
+ [[
+ char x[256];
+ snprintf(x, sizeof(x), "XXX");
+ ]])],
+ [ AC_MSG_RESULT([yes])
+ break ],
+ [ AC_MSG_RESULT([no]) ],
+ [ AC_MSG_WARN([cross compiling: cannot test])
+ break ]
+ )
+ ],
+ [ AC_MSG_RESULT([no]) ]
+ )
+ CFLAGS="$saved_CFLAGS"
+ LDFLAGS="$saved_LDFLAGS"
+ done
+ fi
+
+ if test -z "$have_llong_max"; then
+ # retry LLONG_MAX with -std=gnu99, needed on some Linuxes
+ unset ac_cv_have_decl_LLONG_MAX
+ saved_CFLAGS="$CFLAGS"
+ CFLAGS="$CFLAGS -std=gnu99"
+ AC_CHECK_DECL([LLONG_MAX],
+ [have_llong_max=1],
+ [CFLAGS="$saved_CFLAGS"],
+ [#include <limits.h>]
+ )
+ fi
+fi
+
+AC_MSG_CHECKING([if compiler allows __attribute__ on return types])
+AC_COMPILE_IFELSE(
+ [AC_LANG_PROGRAM([[
+#include <stdlib.h>
+__attribute__((__unused__)) static void foo(void){return;}]],
+ [[ exit(0); ]])],
+ [ AC_MSG_RESULT([yes]) ],
+ [ AC_MSG_RESULT([no])
+ AC_DEFINE(NO_ATTRIBUTE_ON_RETURN_TYPE, 1,
+ [compiler does not accept __attribute__ on return types]) ]
+)
+
+if test "x$no_attrib_nonnull" != "x1"; then
+ AC_DEFINE([HAVE_ATTRIBUTE__NONNULL__], [1], [Have attribute nonnull])
+fi
+
+AC_ARG_WITH([rpath],
+ [ --without-rpath Disable auto-added -R linker paths],
+ [
+ if test "x$withval" = "xno"; then
+ need_dash_r=""
+ fi
+ if test "x$withval" = "xyes"; then
+ need_dash_r=1
+ fi
+ ]
+)
+
+
+AC_ARG_WITH([cflags],
+ [ --with-cflags Specify additional flags to pass to compiler],
+ [
+ if test -n "$withval" -a "$withval" != "no" -a "${withval}" != "yes"; then
+ CFLAGS="$CFLAGS $withval"
+ fi
+ ]
+)
+AC_ARG_WITH([cppflags],
+ [ --with-cppflags Specify additional flags to pass to preprocessor] ,
+ [
+ if test -n "$withval" -a "$withval" != "no" -a "${withval}" != "yes"; then
+ CPPFLAGS="$CPPFLAGS $withval"
+ fi
+ ]
+)
+AC_ARG_WITH([ldflags],
+ [ --with-ldflags Specify additional flags to pass to linker],
+ [
+ if test -n "$withval" -a "$withval" != "xno" -a "${withval}" != "yes"; then
+ LDFLAGS="$LDFLAGS $withval"
+ fi
+ ]
+)
+AC_ARG_WITH([Werror],
+ [ --with-Werror Build main code with -Werror],
+ [
+ if test -n "$withval" -a "$withval" != "no"; then
+ werror_flags="-Werror"
+ if test "${withval}" != "yes"; then
+ werror_flags="$withval"
+ fi
+ fi
+ ]
+)
+
+
+
+AC_ARG_WITH([pie],
+ [ --with-pie Build Position Independent Executables if possible], [
+ if test "x$withval" = "xno"; then
+ use_pie=no
+ fi
+ if test "x$withval" = "xyes"; then
+ use_pie=yes
+ fi
+ ]
+)
+if test -z "$use_pie"; then
+ use_pie=no
+fi
+if test "x$use_toolchain_hardening" != "x1" -a "x$use_pie" = "xauto"; then
+ # Turn off automatic PIE when toolchain hardening is off.
+ use_pie=no
+fi
+if test "x$use_pie" = "xauto"; then
+ # Automatic PIE requires gcc >= 4.x
+ AC_MSG_CHECKING([for gcc >= 4.x])
+ AC_COMPILE_IFELSE([AC_LANG_SOURCE([[
+#if !defined(__GNUC__) || __GNUC__ < 4
+#error gcc is too old
+#endif
+]])],
+ [ AC_MSG_RESULT([yes]) ],
+ [ AC_MSG_RESULT([no])
+ use_pie=no ]
+)
+fi
+if test "x$use_pie" != "xno"; then
+ SAVED_CFLAGS="$CFLAGS"
+ SAVED_LDFLAGS="$LDFLAGS"
+ OSSH_CHECK_CFLAG_COMPILE([-fPIE])
+ OSSH_CHECK_LDFLAG_LINK([-pie])
+ # We use both -fPIE and -pie or neither.
+ AC_MSG_CHECKING([whether both -fPIE and -pie are supported])
+ if echo "x $CFLAGS" | grep ' -fPIE' >/dev/null 2>&1 && \
+ echo "x $LDFLAGS" | grep ' -pie' >/dev/null 2>&1 ; then
+ AC_MSG_RESULT([yes])
+ else
+ AC_MSG_RESULT([no])
+ CFLAGS="$SAVED_CFLAGS"
+ LDFLAGS="$SAVED_LDFLAGS"
+ fi
+fi
+
+
+
+
+
+
+#l432 (customized)
+# Check for some target-specific stuff
+
+case "$host" in
+*-*-darwin*)
+ use_pie=auto
+ AC_MSG_CHECKING([if we have working getaddrinfo])
+ AC_RUN_IFELSE([AC_LANG_SOURCE([[
+#include <stdlib.h>
+#include <mach-o/dyld.h>
+main() { if (NSVersionOfRunTimeLibrary("System") >= (60 << 16))
+ exit(0);
+ else
+ exit(1);
+}
+ ]])],
+ [AC_MSG_RESULT([working])],
+ [AC_MSG_RESULT([buggy])
+ AC_DEFINE([BROKEN_GETADDRINFO], [1],
+ [getaddrinfo is broken (if present)])
+ ],
+ [AC_MSG_RESULT([assume it is working])])
+ AC_DEFINE([SETEUID_BREAKS_SETUID], [1], [define if seteuid breaks setuid])
+ AC_DEFINE([BROKEN_SETREUID], [1], [define if setreuid is broken])
+ AC_DEFINE([BROKEN_SETREGID], [1], [define if setregid is broken])
+ AC_DEFINE([BROKEN_GLOB], [1], [OS X glob does not do what we expect])
+ AC_DEFINE([SPT_TYPE], [SPT_REUSEARGV],
+ [Define to a Set Process Title type if your system is
+ supported by bsd-setproctitle.c])
+ AC_DEFINE([BROKEN_STRNVIS], [1],
+ [OSX strnvis argument order is swapped compared to OpenBSD])
+ BROKEN_STRNVIS=1
+ ;;
+*-*-dragonfly*)
+ ;;
+*-*-linux* | *-gnu* | *-k*bsd*-gnu* )
+ use_pie=auto
+ CFLAGS="$CFLAGS -D_BSD_SOURCE -D_DEFAULT_SOURCE"
+ AC_DEFINE([SPT_TYPE], [SPT_REUSEARGV])
+ ;;
+*-*-netbsd*)
+ if test "x$withval" != "xno"; then
+ need_dash_r=1
+ 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
+ AC_DEFINE([HAVE_ATTRIBUTE__SENTINEL__], [1], [OpenBSD's gcc has sentinel])
+ AC_DEFINE([HAVE_ATTRIBUTE__BOUNDED__], [1], [OpenBSD's gcc has bounded])
+
+ 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])
+ ;;
+*-sun-solaris*)
+ AC_DEFINE([HAVE_M_DATA], [1], [M_DATA is defined in sys/stream.h included by netinet/in.h])
+ ;;
+esac
+AC_SUBST([ASR_LIB])
+
+
+AC_MSG_CHECKING([compiler and flags for sanity])
+AC_RUN_IFELSE([AC_LANG_PROGRAM([[
+#include <stdio.h>
+#include <stdlib.h> ]], [[ exit(0); ]])],
+ [ AC_MSG_RESULT([yes]) ],
+ [
+ AC_MSG_RESULT([no])
+ AC_MSG_ERROR([*** compiler cannot create working executables, check config.log ***])
+ ],
+ [ AC_MSG_WARN([cross compiling: not checking compiler sanity]) ]
+)
+
+
+
+#l1747
+
+
+
+
+# Check for missing getpeereid (or equiv) support
+NO_PEERCHECK=""
+if test "x$ac_cv_func_getpeereid" != "xyes" -a "x$ac_cv_func_getpeerucred" != "xyes"; then
+ AC_MSG_CHECKING([whether system supports SO_PEERCRED getsockopt])
+ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
+#include <sys/types.h>
+#include <sys/socket.h>]], [[int i = SO_PEERCRED;]])],
+ [ AC_MSG_RESULT([yes])
+ AC_DEFINE([HAVE_SO_PEERCRED], [1], [Have PEERCRED socket option])
+ ], [AC_MSG_RESULT([no])
+ NO_PEERCHECK=1
+ ])
+fi
+
+#l4176 (customized s/ssh.1/smtpd/smtpd.8/)
+# Options from here on. Some of these are preset by platform above
+AC_ARG_WITH([mantype],
+ [ --with-mantype=man|cat|doc Set man page type],
+ [
+ case "$withval" in
+ man|cat|doc)
+ MANTYPE=$withval
+ ;;
+ *)
+ AC_MSG_ERROR([invalid man type: $withval])
+ ;;
+ esac
+ ]
+)
+if test -z "$MANTYPE"; then
+ TestPath="/usr/bin${PATH_SEPARATOR}/usr/ucb"
+ AC_PATH_PROGS([NROFF], [nroff awf], [/bin/false], [$TestPath])
+ if ${NROFF} -mdoc ${srcdir}/smtpd/smtpd.8 >/dev/null 2>&1; then
+ MANTYPE=doc
+ elif ${NROFF} -man ${srcdir}/smtpd/smtpd.8 >/dev/null 2>&1; then
+ MANTYPE=man
+ else
+ MANTYPE=cat
+ fi
+fi
+AC_SUBST([MANTYPE])
+if test "$MANTYPE" = "doc"; then
+ mansubdir=man;
+else
+ mansubdir=$MANTYPE;
+fi
+AC_SUBST([mansubdir])
+#l4207
+
+
+#l4432 (customized s/pid/sock/)
+# Whether to enable BSD auth support
+BSD_AUTH_MSG=no
+AC_ARG_WITH([auth-bsdauth],
+ [ --with-auth-bsdauth Enable bsd_auth(3) authentication support],
+ [
+ if test "x$withval" != "xno"; then
+ AC_DEFINE([BSD_AUTH], [1],
+ [Define if you have BSD auth support])
+ BSD_AUTH_MSG=yes
+ fi
+ ]
+)
+
+
+#l2757
+# Check for PAM libs
+PAM_MSG="no"
+USE_PAM_SERVICE=smtpd
+AC_ARG_WITH([auth-pam],
+ [ --with-auth-pam=SERVICE Enable PAM authentication support (default=smtpd)],
+ [
+ if test "x$withval" != "xno"; then
+ if test -n "$withval" -a "x${withval}" != "xyes"; then
+ USE_PAM_SERVICE=$withval
+ fi
+
+ if test "x$ac_cv_header_security_pam_appl_h" != "xyes" -a \
+ test "x$ac_cv_header_pam_pam_appl_h" != "xyes"; then
+ AC_MSG_ERROR([PAM headers not found])
+ fi
+
+ saved_LIBS="$LIBS"
+ AC_CHECK_LIB([dl], [dlopen], , )
+ AC_CHECK_LIB([pam], [pam_set_item], , [AC_MSG_ERROR([*** libpam missing])])
+ AC_CHECK_FUNCS([pam_getenvlist])
+ AC_CHECK_FUNCS([pam_putenv])
+ LIBS="$saved_LIBS"
+
+ PAM_MSG="yes"
+
+ SMTPDLIBS="$SMTPDLIBS -lpam"
+ AC_DEFINE([USE_PAM], [1],
+ [Define if you want to enable PAM support])
+
+ if test "x$ac_cv_lib_dl_dlopen" = "xyes"; then
+ case "$LIBS" in
+ *-ldl*)
+ # libdl already in LIBS
+ ;;
+ *)
+ SMTPDLIBS="$SMTPDLIBS -ldl"
+ ;;
+ esac
+ fi
+ fi
+ ]
+)
+AC_DEFINE_UNQUOTED([USE_PAM_SERVICE], ["$USE_PAM_SERVICE"], [pam service])
+AC_SUBST([USE_PAM_SERVICE])
+
+
+# Check for older PAM
+if test "x$PAM_MSG" = "xyes"; then
+ # Check PAM strerror arguments (old PAM)
+ AC_MSG_CHECKING([whether pam_strerror takes only one argument])
+ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
+#include <stdlib.h>
+#if defined(HAVE_SECURITY_PAM_APPL_H)
+#include <security/pam_appl.h>
+#elif defined (HAVE_PAM_PAM_APPL_H)
+#include <pam/pam_appl.h>
+#endif
+ ]], [[
+(void)pam_strerror((pam_handle_t *)NULL, -1);
+ ]])], [AC_MSG_RESULT([no])], [
+ AC_DEFINE([HAVE_OLD_PAM], [1],
+ [Define if you have an old version of PAM
+ which takes only one argument to pam_strerror])
+ AC_MSG_RESULT([yes])
+ PAM_MSG="yes (old library)"
+
+ ])
+fi
+#l2816
+
+
+##gilles
+
+SMTPD_USER=_smtpd
+AC_ARG_WITH([user-smtpd],
+ [ --with-user-smtpd=user Specify non-privileged user for smtpd (default=_smtpd)],
+ [
+ if test -n "$withval" -a "$withval" != "no" -a "${withval}" != "yes"; then
+ SMTPD_USER=$withval
+ fi
+ ]
+)
+AC_DEFINE_UNQUOTED([SMTPD_USER], ["$SMTPD_USER"],
+ [non-privileged user for privilege separation])
+AC_SUBST([SMTPD_USER])
+
+SMTPD_QUEUE_USER=_smtpq
+AC_ARG_WITH([user-queue],
+ [ --with-user-queue=user Specify non-privileged user for queue process (default=_smtpq)],
+ [
+ if test -n "$withval" -a "$withval" != "no" -a "${withval}" != "yes"; then
+ SMTPD_QUEUE_USER=$withval
+ fi
+ ]
+)
+AC_DEFINE_UNQUOTED([SMTPD_QUEUE_USER], ["$SMTPD_QUEUE_USER"],
+ [non-privileged user for queue process])
+AC_SUBST([SMTPD_QUEUE_USER])
+
+SMTPD_QUEUE_GROUP=_smtpq
+AC_ARG_WITH([group-queue],
+ [ --with-group-queue=group Specify non-privileged group for offline queue (default=_smtpq)],
+ [
+ if test -n "$withval" -a "$withval" != "no" -a "${withval}" != "yes"; then
+ SMTPD_QUEUE_GROUP=$withval
+ fi
+ ]
+)
+AC_DEFINE_UNQUOTED([SMTPD_QUEUE_GROUP], ["$SMTPD_QUEUE_GROUP"],
+ [non-privileged group for queue process])
+AC_SUBST([SMTPD_QUEUE_GROUP])
+
+# Where to place spooler
+spooldir=/var/spool/smtpd
+AC_ARG_WITH([path-queue],
+ [ --with-path-queue=PATH Specify path to queue directory (default=/var/spool/smtpd)],
+ [
+ if test -n "$withval" -a "$withval" != "no" -a "${withval}" != "yes"; then
+ spooldir=$withval
+ if test ! -d $spooldir; then
+ AC_MSG_WARN([** no $spooldir directory on this system **])
+ fi
+ fi
+ ]
+)
+
+AC_DEFINE_UNQUOTED([PATH_SPOOL], ["$spooldir"],
+ [Specify location of spooler])
+AC_SUBST([spooldir])
+
+
+PRIVSEP_PATH=/var/empty
+AC_ARG_WITH([path-empty],
+ [ --with-path-empty=PATH Specify path to empty directory (default=/var/empty)],
+ [
+ if test -n "$withval" -a "$withval" != "no" -a "${withval}" != "yes"; then
+ PRIVSEP_PATH=$withval
+ fi
+ ]
+)
+AC_SUBST([PRIVSEP_PATH])
+#l4022
+
+#l4066
+dnl # --with-maildir=/path/to/mail gets top priority.
+dnl # if maildir is set in the platform case statement above we use that.
+dnl # Otherwise we run a program to get the dir from system headers.
+dnl # We first look for _PATH_MAILDIR then MAILDIR then _PATH_MAIL
+dnl # If we find _PATH_MAILDIR we do nothing because that is what
+dnl # session.c expects anyway. Otherwise we set to the value found
+dnl # stripping any trailing slash. If for some strage reason our program
+dnl # does not find what it needs, we default to /var/spool/mail.
+# Check for mail directory
+AC_ARG_WITH([path-mbox],
+ [ --with-path-mbox=PATH Specify path to mbox directory (default=/var/spool/mail)],
+ [
+ if test -n "$withval" -a "$withval" != "no" -a "${withval}" != "yes"; then
+ AC_DEFINE_UNQUOTED([MAIL_DIRECTORY], ["$withval"],
+ [Set this to your mail directory if you do not have _PATH_MAILDIR])
+ fi
+ ],[
+ if test -n "$maildir"; then
+ AC_DEFINE_UNQUOTED([MAIL_DIRECTORY], ["$maildir"])
+ else
+ AC_MSG_CHECKING([system mail directory])
+ AC_RUN_IFELSE(
+ [AC_LANG_PROGRAM([[
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#ifdef HAVE_PATHS_H
+#include <paths.h>
+#endif
+#ifdef HAVE_MAILLOCK_H
+#include <maillock.h>
+#endif
+#define DATA "conftest.maildir"
+ ]], [[
+ FILE *fd;
+ int rc;
+
+ fd = fopen(DATA,"w");
+ if(fd == NULL)
+ exit(1);
+
+#if defined (_PATH_MAILDIR)
+ if ((rc = fprintf(fd ,"_PATH_MAILDIR:%s\n", _PATH_MAILDIR)) <0)
+ exit(1);
+#elif defined (MAILDIR)
+ if ((rc = fprintf(fd ,"MAILDIR:%s\n", MAILDIR)) <0)
+ exit(1);
+#elif defined (_PATH_MAIL)
+ if ((rc = fprintf(fd ,"_PATH_MAIL:%s\n", _PATH_MAIL)) <0)
+ exit(1);
+#else
+ exit (2);
+#endif
+
+ exit(0);
+ ]])],
+ [
+ maildir_what=`awk -F: '{print $1}' conftest.maildir`
+ maildir=`awk -F: '{print $2}' conftest.maildir \
+ | sed 's|/$||'`
+ AC_MSG_RESULT([$maildir from $maildir_what])
+ if test "x$maildir_what" != "x_PATH_MAILDIR"; then
+ AC_DEFINE_UNQUOTED([MAIL_DIRECTORY], ["$maildir"])
+ fi
+ ],
+ [
+ if test "X$ac_status" = "X2"; then
+# our test program didn't find it. Default to /var/spool/mail
+ AC_MSG_RESULT([/var/spool/mail])
+ AC_DEFINE_UNQUOTED([MAIL_DIRECTORY], ["/var/spool/mail"])
+ else
+ AC_MSG_RESULT([*** not found ***])
+ fi
+ ],
+ [
+ AC_MSG_WARN([cross compiling: use --with-maildir=/path/to/mail])
+ ]
+ )
+ fi
+ ]
+) # maildir
+#l4146
+
+# Where to place smtpd.sock
+sockdir=/var/run
+# make sure the directory exists
+if test ! -d $sockdir; then
+ sockdir=`eval echo ${sysconfdir}`
+ case $sockdir in
+ NONE/*) sockdir=`echo $sockdir | sed "s~NONE~$ac_default_prefix~"` ;;
+ esac
+fi
+
+AC_ARG_WITH([path-socket],
+ [ --with-path-socket=PATH Specify path to smtpd.sock directory (default=/var/run)],
+ [
+ if test -n "$withval" -a "$withval" != "no" -a "${withval}" != "yes"; then
+ sockdir=$withval
+ if test ! -d $sockdir; then
+ AC_MSG_WARN([** no $sockdir directory on this system **])
+ fi
+ fi
+ ]
+)
+
+AC_DEFINE_UNQUOTED([SMTPD_SOCKDIR], ["$sockdir"],
+ [Specify location of smtpd.sock])
+AC_SUBST([sockdir])
+#l4470
+
+# Where to place smtpd.pid
+piddir=/var/run
+AC_MSG_CHECKING([system pid directory])
+AC_RUN_IFELSE(
+ [
+ AC_LANG_PROGRAM([[
+#include <stdio.h>
+#include <stdlib.h>
+#ifdef HAVE_PATHS_H
+#include <paths.h>
+#endif
+#define DATA "conftest.piddir"
+ ]],
+ [[
+#ifdef _PATH_VARRUN
+FILE *fd;
+int rc;
+
+if ((fd = fopen(DATA,"w")) == NULL) { exit(1); }
+if ((rc = fprintf(fd ,"%s\n", _PATH_VARRUN)) < 0) { exit(2); }
+exit(0);
+#else
+exit(-1);
+#endif
+ ]])
+ ], [
+ piddir=`cat conftest.piddir`
+ AC_MSG_RESULT([$piddir from paths.h])
+ ],
+ [
+ AC_MSG_RESULT([$piddir from default value])
+ ],
+ [
+ AC_MSG_RESULT([$piddir from default value])
+ ]
+)
+
+AC_ARG_WITH([path-pidfile],
+ [ --with-path-pidfile=PATH Specify path to smtpd.pid directory (default=/var/run)],
+ [
+ if test -n "$withval" -a "$withval" != "no" -a "${withval}" != "yes"; then
+ piddir=$withval
+ fi
+ ]
+)
+
+AC_DEFINE_UNQUOTED([SMTPD_PIDDIR], ["$piddir"], [Specify location of smtpd.pid])
+AC_SUBST([piddir])
+
+CA_FILE=/etc/ssl/cert.pem
+AC_ARG_WITH([path-CAfile],
+ [ --with-path-CAfile=FILE Specify path to CA certificate (default=/etc/ssl/cert.pem)],
+ [
+ if test -n "$withval" -a "$withval" != "no" -a "${withval}" != "yes"; then
+ CA_FILE=$withval
+ fi
+ ]
+)
+AC_SUBST([CA_FILE])
+
+
+
+
+
+
+# compute LLONG_MIN and LLONG_MAX if we don't know them.
+if test -z "$have_llong_max"; then
+ AC_MSG_CHECKING([for max value of long long])
+ AC_RUN_IFELSE(
+ [AC_LANG_PROGRAM([[
+#include <stdio.h>
+#include <stdlib.h>
+/* Why is this so damn hard? */
+#ifdef __GNUC__
+# undef __GNUC__
+#endif
+#define __USE_ISOC99
+#include <limits.h>
+#define DATA "conftest.llminmax"
+#define my_abs(a) ((a) < 0 ? ((a) * -1) : (a))
+
+/*
+ * printf in libc on some platforms (eg old Tru64) does not understand %lld so
+ * we do this the hard way.
+ */
+static int
+fprint_ll(FILE *f, long long n)
+{
+ unsigned int i;
+ int l[sizeof(long long) * 8];
+
+ if (n < 0)
+ if (fprintf(f, "-") < 0)
+ return -1;
+ for (i = 0; n != 0; i++) {
+ l[i] = my_abs(n % 10);
+ n /= 10;
+ }
+ do {
+ if (fprintf(f, "%d", l[--i]) < 0)
+ return -1;
+ } while (i != 0);
+ if (fprintf(f, " ") < 0)
+ return -1;
+ return 0;
+}
+ ]], [[
+ FILE *f;
+ long long i, llmin, llmax = 0;
+
+ if((f = fopen(DATA,"w")) == NULL)
+ exit(1);
+
+#if defined(LLONG_MIN) && defined(LLONG_MAX)
+ fprintf(stderr, "Using system header for LLONG_MIN and LLONG_MAX\n");
+ llmin = LLONG_MIN;
+ llmax = LLONG_MAX;
+#else
+ fprintf(stderr, "Calculating LLONG_MIN and LLONG_MAX\n");
+ /* This will work on one's complement and two's complement */
+ for (i = 1; i > llmax; i <<= 1, i++)
+ llmax = i;
+ llmin = llmax + 1LL; /* wrap */
+#endif
+
+ /* Sanity check */
+ if (llmin + 1 < llmin || llmin - 1 < llmin || llmax + 1 > llmax
+ || llmax - 1 > llmax || llmin == llmax || llmin == 0
+ || llmax == 0 || llmax < LONG_MAX || llmin > LONG_MIN) {
+ fprintf(f, "unknown unknown\n");
+ exit(2);
+ }
+
+ if (fprint_ll(f, llmin) < 0)
+ exit(3);
+ if (fprint_ll(f, llmax) < 0)
+ exit(4);
+ if (fclose(f) < 0)
+ exit(5);
+ exit(0);
+ ]])],
+ [
+ llong_min=`$AWK '{print $1}' conftest.llminmax`
+ llong_max=`$AWK '{print $2}' conftest.llminmax`
+
+ AC_MSG_RESULT([$llong_max])
+ AC_DEFINE_UNQUOTED([LLONG_MAX], [${llong_max}LL],
+ [max value of long long calculated by configure])
+ AC_MSG_CHECKING([for min value of long long])
+ AC_MSG_RESULT([$llong_min])
+ AC_DEFINE_UNQUOTED([LLONG_MIN], [${llong_min}LL],
+ [min value of long long calculated by configure])
+ ],
+ [
+ AC_MSG_RESULT([not found])
+ ],
+ [
+ AC_MSG_WARN([cross compiling: not checking])
+ ]
+ )
+fi
+
+
+
+
+
+#l3561
+
+
+dnl make sure we're using the real structure members and not defines
+AC_CACHE_CHECK([for msg_accrights field in struct msghdr],
+ ac_cv_have_accrights_in_msghdr, [
+ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/uio.h>
+#include <stdlib.h>
+ ]], [[
+#ifdef msg_accrights
+#error "msg_accrights is a macro"
+exit(1);
+#endif
+struct msghdr m;
+m.msg_accrights = 0;
+exit(0);
+ ]])],
+ [ ac_cv_have_accrights_in_msghdr="yes" ],
+ [ ac_cv_have_accrights_in_msghdr="no" ]
+ )
+])
+if test "x$ac_cv_have_accrights_in_msghdr" = "xyes"; then
+ AC_DEFINE([HAVE_ACCRIGHTS_IN_MSGHDR], [1],
+ [Define if your system uses access rights style
+ file descriptor passing])
+fi
+
+
+AC_CACHE_CHECK([for msg_control field in struct msghdr],
+ ac_cv_have_control_in_msghdr, [
+ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/uio.h>
+#include <stdlib.h>
+ ]], [[
+#ifdef msg_control
+#error "msg_control is a macro"
+exit(1);
+#endif
+struct msghdr m;
+m.msg_control = 0;
+exit(0);
+ ]])],
+ [ ac_cv_have_control_in_msghdr="yes" ],
+ [ ac_cv_have_control_in_msghdr="no" ]
+ )
+])
+if test "x$ac_cv_have_control_in_msghdr" = "xyes"; then
+ AC_DEFINE([HAVE_CONTROL_IN_MSGHDR], [1],
+ [Define if your system uses ancillary data style
+ file descriptor passing])
+fi
+
+AC_CACHE_CHECK([if libc defines __progname], ac_cv_libc_defines___progname, [
+ AC_LINK_IFELSE([AC_LANG_PROGRAM([[ #include <stdio.h> ]],
+ [[ extern char *__progname; printf("%s", __progname); ]])],
+ [ ac_cv_libc_defines___progname="yes" ],
+ [ ac_cv_libc_defines___progname="no"
+ ])
+])
+if test "x$ac_cv_libc_defines___progname" = "xyes"; then
+ AC_DEFINE([HAVE___PROGNAME], [1], [Define if libc defines __progname])
+fi
+
+AC_CACHE_CHECK([whether $CC implements __FUNCTION__], ac_cv_cc_implements___FUNCTION__, [
+ AC_LINK_IFELSE([AC_LANG_PROGRAM([[ #include <stdio.h> ]],
+ [[ printf("%s", __FUNCTION__); ]])],
+ [ ac_cv_cc_implements___FUNCTION__="yes" ],
+ [ ac_cv_cc_implements___FUNCTION__="no"
+ ])
+])
+if test "x$ac_cv_cc_implements___FUNCTION__" = "xyes"; then
+ AC_DEFINE([HAVE___FUNCTION__], [1],
+ [Define if compiler implements __FUNCTION__])
+fi
+
+AC_CACHE_CHECK([whether $CC implements __func__], ac_cv_cc_implements___func__, [
+ AC_LINK_IFELSE([AC_LANG_PROGRAM([[ #include <stdio.h> ]],
+ [[ printf("%s", __func__); ]])],
+ [ ac_cv_cc_implements___func__="yes" ],
+ [ ac_cv_cc_implements___func__="no"
+ ])
+])
+if test "x$ac_cv_cc_implements___func__" = "xyes"; then
+ AC_DEFINE([HAVE___func__], [1], [Define if compiler implements __func__])
+fi
+
+AC_CACHE_CHECK([whether va_copy exists], ac_cv_have_va_copy, [
+ AC_LINK_IFELSE([AC_LANG_PROGRAM([[
+#include <stdarg.h>
+va_list x,y;
+ ]], [[ va_copy(x,y); ]])],
+ [ ac_cv_have_va_copy="yes" ],
+ [ ac_cv_have_va_copy="no"
+ ])
+])
+if test "x$ac_cv_have_va_copy" = "xyes"; then
+ AC_DEFINE([HAVE_VA_COPY], [1], [Define if va_copy exists])
+fi
+
+AC_CACHE_CHECK([whether __va_copy exists], ac_cv_have___va_copy, [
+ AC_LINK_IFELSE([AC_LANG_PROGRAM([[
+#include <stdarg.h>
+va_list x,y;
+ ]], [[ __va_copy(x,y); ]])],
+ [ ac_cv_have___va_copy="yes" ], [ ac_cv_have___va_copy="no"
+ ])
+])
+if test "x$ac_cv_have___va_copy" = "xyes"; then
+ AC_DEFINE([HAVE___VA_COPY], [1], [Define if __va_copy exists])
+fi
+
+AC_CACHE_CHECK([whether getopt has optreset support],
+ ac_cv_have_getopt_optreset, [
+ AC_LINK_IFELSE([AC_LANG_PROGRAM([[ #include <getopt.h> ]],
+ [[ extern int optreset; optreset = 0; ]])],
+ [ ac_cv_have_getopt_optreset="yes" ],
+ [ ac_cv_have_getopt_optreset="no"
+ ])
+])
+if test "x$ac_cv_have_getopt_optreset" = "xyes"; then
+ AC_DEFINE([HAVE_GETOPT_OPTRESET], [1],
+ [Define if your getopt(3) defines and uses optreset])
+fi
+#l3765
+
+
+
+
+#l4045
+STRIP_OPT=-s
+AC_ARG_ENABLE([strip],
+ [ --disable-strip Disable calling strip(1) on install],
+ [
+ if test "x$enableval" = "xno"; then
+ STRIP_OPT=
+ fi
+ ]
+)
+AC_SUBST([STRIP_OPT])
+#l4054
+
+
+
+case "$host" in
+*-*-openbsd*)
+ pkglibexecdir="$libexecdir/smtpd"
+ ;;
+*)
+ pkglibexecdir="$libexecdir/opensmtpd"
+ ;;
+esac
+AC_SUBST([pkglibexecdir])
+
+
+
+
+
+
+
+
+
+#l4742
+dnl Adding -Werror to CFLAGS early prevents configure tests from running.
+dnl Add now.
+CFLAGS="$CFLAGS $werror_flags"
+
+AC_SUBST([TEST_MALLOC_OPTIONS], [$TEST_MALLOC_OPTIONS])
+
+
+AC_EXEEXT
+#l4757
+
+
+# Search for fts
+AC_ARG_WITH([libfts],
+ [ --with-libfts=PATH Specify path to libfts installation (default: none, part of libc)],
+ [ if test "x$withval" = "xno"; then
+ AC_MSG_ERROR([*** fts 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
+ LIBS="-lfts $LIBS"
+ fi
+ ]
+)
+
+
+
+##chl (based on OpenSSL checks, see above)
+# Search for libevent
+saved_CPPFLAGS="$CPPFLAGS"
+saved_LDFLAGS="$LDFLAGS"
+AC_ARG_WITH([libevent],
+ [ --with-libevent=PATH Specify path to libevent installation ],
+ [
+ if test "x$withval" != "xno"; then
+ case "$withval" in
+ # Relative paths
+ ./*|../*) withval="`pwd`/$withval"
+ esac
+ 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
+ elif test -d "$withval/lib64"; then
+ if test -n "${need_dash_r}"; then
+ LDFLAGS="-L${withval}/lib64 -R${withval}/lib64 ${LDFLAGS}"
+ else
+ LDFLAGS="-L${withval}/lib64 ${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
+ need_libevent_autodetect=no
+ fi
+ ]
+)
+
+if test "x${need_libevent_autodetect}" != "xno"; then
+ for path in /usr/local /usr; do
+ if test -f "${path}/include/event.h"; then
+ CPPFLAGS="-I${path}/include ${CPPFLAGS}"
+ LDFLAGS="-L${path}/lib ${LDFLAGS}"
+ fi
+ done
+fi
+
+AC_CHECK_HEADER([event.h], ,[AC_MSG_ERROR([*** event.h missing - please install libevent ***])],
+[#include <sys/types.h>])
+LIBS="-levent $LIBS"
+AC_MSG_CHECKING([if programs using libevent functions will link])
+AC_LINK_IFELSE(
+ [AC_LANG_PROGRAM([[
+ #include <event.h>
+ ]], [[
+ event_base_new();
+ ]])],
+ [
+ AC_MSG_RESULT([yes])
+ ],
+ [
+ AC_MSG_RESULT([no])
+ ]
+)
+
+
+#l2174 (customized, bu adding -lssl to LIBS)
+# Search for OpenSSL
+saved_CPPFLAGS="$CPPFLAGS"
+saved_LDFLAGS="$LDFLAGS"
+AC_ARG_WITH([libssl],
+ [ --with-libssl=PATH Specify path to libssl installation ],
+ [
+ if test "x$withval" != "xno"; then
+ case "$withval" in
+ # Relative paths
+ ./*|../*) withval="`pwd`/$withval"
+ esac
+ 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
+ elif test -d "$withval/lib64"; then
+ if test -n "${need_dash_r}"; then
+ LDFLAGS="-L${withval}/lib64 -R${withval}/lib64 ${LDFLAGS}"
+ else
+ LDFLAGS="-L${withval}/lib64 ${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
+ ]
+)
+## XXX chl -lssl manually added
+LIBS="-lcrypto -lssl $LIBS"
+AC_TRY_LINK_FUNC([RAND_add], [AC_DEFINE([HAVE_OPENSSL], [1],
+ [Define if your ssl headers are included
+ with #include <openssl/header.h>])],
+ [
+ dnl Check default openssl install dir
+ if test -n "${need_dash_r}"; then
+ LDFLAGS="-L/usr/local/ssl/lib -R/usr/local/ssl/lib ${saved_LDFLAGS}"
+ else
+ LDFLAGS="-L/usr/local/ssl/lib ${saved_LDFLAGS}"
+ fi
+ CPPFLAGS="-I/usr/local/ssl/include ${saved_CPPFLAGS}"
+ AC_CHECK_HEADER([openssl/opensslv.h], ,
+ [AC_MSG_ERROR([*** LibreSSL headers missing - please install first or check config.log ***])])
+ AC_TRY_LINK_FUNC([RAND_add], [AC_DEFINE([HAVE_OPENSSL])],
+ [
+ AC_MSG_ERROR([*** Can't find recent LibreSSL libcrypto (see config.log for details) ***])
+ ]
+ )
+ ]
+)
+
+
+LIBS="-lcrypto -lssl $LIBS"
+AC_MSG_CHECKING([whether SSL_CTX_use_certificate_chain_mem is available])
+AC_TRY_LINK_FUNC([SSL_CTX_use_certificate_chain_mem],
+ [
+ AC_DEFINE([HAVE_SSL_CTX_USE_CERTIFICATE_CHAIN_MEM], [1],
+ [Define if SSL_CTX_use_certificate_chain_mem exists in libssl])
+ AC_MSG_RESULT([yes])
+ ],
+ [ AC_MSG_RESULT([no])]
+)
+
+LIBS="-lcrypto -lssl $LIBS"
+AC_MSG_CHECKING([whether ECDSA is available])
+AC_TRY_LINK_FUNC([ENGINE_get_ECDSA],
+ [
+ AC_DEFINE([SUPPORT_ECDSA], [1],
+ [Define if ECDSA is supported])
+ AC_MSG_RESULT([yes])
+ ],
+ [ AC_MSG_RESULT([no])]
+)
+
+# Sanity check OpenSSL headers
+AC_MSG_CHECKING([whether LibreSSL's headers match the library])
+AC_RUN_IFELSE(
+ [AC_LANG_PROGRAM([[
+#include <stdlib.h>
+#include <string.h>
+#include <openssl/opensslv.h>
+#include <openssl/crypto.h>
+ ]], [[
+ exit(SSLeay() == OPENSSL_VERSION_NUMBER ? 0 : 1);
+ ]])],
+ [
+ AC_MSG_RESULT([yes])
+ ],
+ [
+ AC_MSG_RESULT([no])
+ AC_MSG_ERROR([Your LibreSSL headers do not match your library.])
+ ],
+ [
+ AC_MSG_WARN([cross compiling: not checking])
+ ]
+)
+
+AC_MSG_CHECKING([if programs using LibreSSL functions will link])
+AC_LINK_IFELSE(
+ [AC_LANG_PROGRAM([[ #include <openssl/evp.h> ]],
+ [[ SSLeay_add_all_algorithms(); ]])],
+ [
+ AC_MSG_RESULT([yes])
+ ],
+ [
+ AC_MSG_RESULT([no])
+ saved_LIBS="$LIBS"
+ LIBS="$LIBS -ldl"
+ AC_MSG_CHECKING([if programs using LibreSSL need -ldl])
+ AC_LINK_IFELSE(
+ [AC_LANG_PROGRAM([[ #include <openssl/evp.h> ]],
+ [[ SSLeay_add_all_algorithms(); ]])],
+ [
+ AC_MSG_RESULT([yes])
+ ],
+ [
+ AC_MSG_RESULT([no])
+ LIBS="$saved_LIBS"
+ ]
+ )
+ ]
+)
+
+AC_CHECK_DECL([LIBRESSL_VERSION_NUMBER], , , [#include <openssl/ssl.h>])
+
+#l2371
+
+
+dnl zlib is required
+AC_ARG_WITH([libz],
+ [ --with-libz=PATH Specify path to libz installation],
+ [ if test "x$withval" = "xno"; then
+ AC_MSG_ERROR([*** zlib 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([zlib.h], ,[AC_MSG_ERROR([*** zlib.h missing - please install first or check config.log ***])])
+AC_CHECK_LIB([z], [deflate], ,
+ [
+ saved_CPPFLAGS="$CPPFLAGS"
+ saved_LDFLAGS="$LDFLAGS"
+ save_LIBS="$LIBS"
+ dnl Check default zlib 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}"
+ LIBS="$LIBS -lz"
+ AC_TRY_LINK_FUNC([deflate], [AC_DEFINE([HAVE_LIBZ])],
+ [
+ AC_MSG_ERROR([*** zlib missing - please install first or check config.log ***])
+ ]
+ )
+ ]
+)
+
+
+
+
+
+AC_ARG_WITH([table-db],
+ [ --with-table-db Enable building of table-db backend (default=no)],
+ [
+ if test "x$withval" = "xyes"; then
+ use_db_api=1
+ else
+ use_db_api=0
+ fi
+ ]
+)
+
+if test "x$use_db_api" = "x1"; then
+# Search for libdb
+AC_CHECK_HEADER(db_185.h, [AC_DEFINE([HAVE_DB_185_H], [], [if you have the <db_185.h> header file]) ] , [
+AC_CHECK_HEADER(db.h, [AC_DEFINE([HAVE_DB_H], [], [if you have the <db.h> header file]) ] , [
+AC_CHECK_HEADER(db1/db.h, [AC_DEFINE([HAVE_DB1_DB_H], [], [if you have the <db1/db.h> header file]) ] , [
+ AC_MSG_ERROR([*** Can't find Berkeley DB headers (see config.log for details) ***])
+])])])
+fi
+
+save_LIBS="$LIBS"
+
+if test "x$use_db_api" = "x1"; then
+DB_LIB=
+
+for libdb in db db1 c; do
+ AC_CHECK_LIB($libdb, dbopen, [ DB_LIB="$libdb"; break; ],
+ AC_CHECK_LIB($libdb, __db185_open, [ DB_LIB="$libdb"; break; ]))
+done
+
+if test -z "$DB_LIB"; then
+ AC_MSG_ERROR([Berkeley DB not found or not built with --enable-185])
+fi
+
+DB_LIB="-l$DB_LIB"
+AC_SUBST([DB_LIB])
+fi
+
+LIBS="$save_LIBS"
+
+
+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
+
+
+##
+#AM_CONDITIONAL([NEED_BASENAME], [test "x$ac_cv_have_basename" != "xyes"])
+AM_CONDITIONAL([NEED_BASE64], [test "x$ac_cv_search_b64_ntop" = "xno" -a "x$ac_cv_search___b64_ntop" = "xno" ])
+AM_CONDITIONAL([NEED_BASENAME], [test "x$ac_cv_search_basename" = "xno"])
+AM_CONDITIONAL([NEED_CLOCK_GETTIME], [test "x$ac_cv_search_clock_gettime" = "xno"])
+AM_CONDITIONAL([NEED_CLOSEFROM], [test "x$ac_cv_search_closefrom" = "xno"])
+AM_CONDITIONAL([NEED_DAEMON], [test "x$ac_cv_search_daemon" = "xno"])
+AM_CONDITIONAL([NEED_DIRNAME], [test "x$ac_cv_search_dirname" = "xno"])
+AM_CONDITIONAL([NEED_EVENT_ASR_RUN], [test "x$ac_cv_search_event_asr_run" = "xno"])
+AM_CONDITIONAL([NEED_FMT_SCALED], [test "x$ac_cv_search_fmt_scaled" = "xno"])
+AM_CONDITIONAL([NEED_FPARSELN], [test "x$ac_cv_search_fparseln" = "xno"])
+AM_CONDITIONAL([NEED_IMSG], [test "x$ac_cv_search_imsg_init" = "xno"])
+AM_CONDITIONAL([NEED_INET_NET_PTON], [test "x$ac_cv_search_inet_net_pton" = "xno"])
+
+AM_CONDITIONAL([NEED_ERR], [test "x$ac_cv_func_err" != "xyes"])
+AM_CONDITIONAL([NEED_ERRC], [test "x$ac_cv_func_errc" != "xyes"])
+AM_CONDITIONAL([NEED_CRYPT_CHECKPASS], [test "x$ac_cv_func_crypt_checkpass" != "xyes"])
+AM_CONDITIONAL([NEED_EXPLICIT_BZERO], [test "x$ac_cv_func_explicit_bzero" != "xyes"])
+AM_CONDITIONAL([NEED_FGETLN], [test "x$ac_cv_func_fgetln" != "xyes"])
+AM_CONDITIONAL([NEED_FREEZERO], [test "x$ac_cv_func_freezero" != "xyes"])
+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"])
+AM_CONDITIONAL([NEED_SETEGID], [test "x$ac_cv_func_setegid" != "xyes"])
+AM_CONDITIONAL([NEED_SETEUID], [test "x$ac_cv_func_seteuid" != "xyes"])
+AM_CONDITIONAL([NEED_SETRESGID], [test "x$ac_cv_func_setresgid" != "xyes"])
+AM_CONDITIONAL([NEED_SETRESUID], [test "x$ac_cv_func_setresuid" != "xyes"])
+AM_CONDITIONAL([NEED_SIGNAL], [test "x$ac_cv_func_signal" != "xyes"])
+AM_CONDITIONAL([NEED_STRERROR], [test "x$ac_cv_func_strerror" != "xyes"])
+AM_CONDITIONAL([NEED_STRLCAT], [test "x$ac_cv_func_strlcat" != "xyes"])
+AM_CONDITIONAL([NEED_STRLCPY], [test "x$ac_cv_func_strlcpy" != "xyes"])
+AM_CONDITIONAL([NEED_STRMODE], [test "x$ac_cv_func_strmode" != "xyes"])
+AM_CONDITIONAL([NEED_STRSEP], [test "x$ac_cv_func_strsep" != "xyes"])
+AM_CONDITIONAL([NEED_STRTONUM], [test "x$ac_cv_func_strtonum" != "xyes"])
+AM_CONDITIONAL([NEED_STRNDUP], [test "x$ac_cv_func_strndup" != "xyes"])
+AM_CONDITIONAL([NEED_STRNLEN], [test "x$ac_cv_func_strnlen" != "xyes"])
+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"])
+
+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
+ mk/mail/Makefile
+ mk/mail/mail.lmtp/Makefile
+ mk/mail/mail.maildir/Makefile
+ mk/mail/mail.mboxfile/Makefile
+ mk/mail/mail.mda/Makefile
+ mk/smtpd/Makefile
+ mk/smtpctl/Makefile
+ mk/smtp/Makefile
+ contrib/Makefile
+ contrib/libexec/Makefile
+ contrib/libexec/mail.local/Makefile
+ contrib/libexec/lockspool/Makefile
+ contrib/libexec/encrypt/Makefile
+ ])
+
+#l4761
+AC_OUTPUT
diff --git a/contrib/Makefile.am b/contrib/Makefile.am
new file mode 100644
index 00000000..37a8e73a
--- /dev/null
+++ b/contrib/Makefile.am
@@ -0,0 +1 @@
+SUBDIRS = libexec
diff --git a/contrib/libexec/Makefile.am b/contrib/libexec/Makefile.am
new file mode 100644
index 00000000..0e3a271f
--- /dev/null
+++ b/contrib/libexec/Makefile.am
@@ -0,0 +1 @@
+SUBDIRS = mail.local lockspool encrypt
diff --git a/contrib/libexec/encrypt/Makefile.am b/contrib/libexec/encrypt/Makefile.am
new file mode 100644
index 00000000..2f96e60d
--- /dev/null
+++ b/contrib/libexec/encrypt/Makefile.am
@@ -0,0 +1,13 @@
+pkglibexec_PROGRAMS = encrypt
+
+encrypt_SOURCES = encrypt.c
+encrypt_SOURCES += $(top_srcdir)/usr.sbin/smtpd/log.c
+
+AM_CPPFLAGS = -I$(top_srcdir)/openbsd-compat
+
+LIBCOMPAT = $(top_builddir)/openbsd-compat/libopenbsd.a
+
+LDADD = $(LIBCOMPAT)
+
+uninstall-hook:
+ rmdir $(DESTDIR)$(pkglibexecdir) 2> /dev/null || /bin/true
diff --git a/contrib/libexec/encrypt/encrypt.c b/contrib/libexec/encrypt/encrypt.c
new file mode 100644
index 00000000..80275921
--- /dev/null
+++ b/contrib/libexec/encrypt/encrypt.c
@@ -0,0 +1,102 @@
+/*
+ * Copyright (c) 2013 Sunil Nimmagadda <sunil@sunilnimmagadda.com>
+ * Copyright (c) 2013 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"
+
+#ifdef HAVE_CRYPT_H
+#include <crypt.h> /* needed for crypt() */
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#define PASSWORD_LEN 128
+#define SALT_LEN 16
+
+static unsigned char itoa64[] = /* 0 ... 63 => ascii - 64 */
+ "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
+
+static void to64(char *, long int, int);
+static void print_passwd(const char *);
+
+int
+main(int argc, char *argv[])
+{
+ char *line;
+ size_t linesz;
+ ssize_t linelen;
+
+ if (argc > 2) {
+ fprintf(stderr, "usage: encrypt <string>\n");
+ return (1);
+ }
+
+ if (argc == 2) {
+ print_passwd(argv[1]);
+ return (0);
+ }
+
+ line = NULL;
+ linesz = 0;
+ while ((linelen = getline(&line, &linesz, stdin)) != -1) {
+ if (line[linelen - 1] == '\n')
+ line[linelen - 1] = '\0';
+ print_passwd(line);
+ }
+ free(line);
+
+ return (0);
+}
+
+void
+print_passwd(const char *string)
+{
+ const char *ids[] = { "2a", "6", "5", "3", "2", "1", NULL };
+ const char *id;
+ char salt[SALT_LEN+1];
+ char buffer[PASSWORD_LEN];
+ int n;
+ const char *p;
+
+ for (n = 0; n < SALT_LEN; ++n)
+ to64(&salt[n], arc4random_uniform(0xff), 1);
+ salt[SALT_LEN] = '\0';
+
+ for (n = 0; ids[n]; n++) {
+ id = ids[n];
+ (void)snprintf(buffer, sizeof buffer, "$%s$%s$", id, salt);
+ if ((p = crypt(string, buffer)) == NULL)
+ continue;
+ if (strncmp(p, buffer, strlen(buffer)) != 0)
+ continue;
+ printf("%s\n", p);
+ return;
+ }
+
+ salt[2] = 0;
+ printf("%s\n", crypt(string, salt));
+}
+
+void
+to64(char *s, long int v, int n)
+{
+ while (--n >= 0) {
+ *s++ = itoa64[v & 0x3f];
+ v >>= 6;
+ }
+}
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/Makefile.am b/contrib/libexec/mail.local/Makefile.am
new file mode 100644
index 00000000..217659c1
--- /dev/null
+++ b/contrib/libexec/mail.local/Makefile.am
@@ -0,0 +1,22 @@
+pkglibexec_PROGRAMS = mail.local
+
+mail_local_SOURCES = mail.local.c
+mail_local_SOURCES += locking.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 -DPATH_LIBEXEC=\"$(pkglibexecdir)\"
+
+LIBCOMPAT = $(top_builddir)/openbsd-compat/libopenbsd.a
+
+LDADD = $(LIBCOMPAT)
+
+# need to define _GNU_SOURCE to get:
+# EAI_NODATA defined
+# {v,}asprintf
+# setres{g,u}id
+#CFLAGS += -D_GNU_SOURCE
+
+uninstall-hook:
+ rmdir $(DESTDIR)$(pkglibexecdir) 2> /dev/null || /bin/true
diff --git a/contrib/libexec/mail.local/locking.c b/contrib/libexec/mail.local/locking.c
new file mode 100644
index 00000000..85a48d5e
--- /dev/null
+++ b/contrib/libexec/mail.local/locking.c
@@ -0,0 +1,182 @@
+/* $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/mail.local/mail.local.8 b/contrib/libexec/mail.local/mail.local.8
new file mode 100644
index 00000000..330a4473
--- /dev/null
+++ b/contrib/libexec/mail.local/mail.local.8
@@ -0,0 +1,183 @@
+.\" $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.
+.\"
+.\" 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: @(#)mail.local.8 6.8 (Berkeley) 4/27/91
+.\"
+.Dd $Mdocdate: September 16 2014 $
+.Dt MAIL.LOCAL 8
+.Os
+.Sh NAME
+.Nm mail.local
+.Nd store mail in a mailbox
+.Sh SYNOPSIS
+.Nm mail.local
+.Op Fl Ll
+.Op Fl f Ar from
+.Ar user ...
+.Sh DESCRIPTION
+.Nm
+reads the standard input up to an end-of-file and appends it to each
+.Ar user Ns 's
+.Pa mail
+file.
+The
+.Ar user
+must be a valid user name.
+.Pp
+The options are as follows:
+.Bl -tag -width Ds
+.It Fl f Ar from
+Specify the sender's name.
+.It Fl L
+Don't create a
+.Pa username.lock
+file while locking the spool.
+.It Fl l
+For compatibility, request that files named
+.Pa username.lock
+be used for locking.
+(This is the default behavior.)
+.El
+.Pp
+Individual mail messages in the mailbox are delimited by an empty
+line followed by a line beginning with the string
+.Dq "From\&\ " .
+A line containing the string
+.Dq "From\&\ " ,
+the sender's name and a timestamp is prepended to each delivered mail message.
+A blank line is appended to each message.
+A greater-than character
+.Pq Ql >
+is prepended to any line in the message which could be mistaken for a
+.Dq "From\&\ "
+delimiter line.
+.Pp
+Significant efforts have been made to ensure that
+.Nm
+acts as securely as possible if the spool directory is mode 1777 or 755.
+The default of mode 755 is more secure, but it prevents mail clients from using
+.Pa username.lock
+style locking.
+The use of 1777 is more flexible in an NFS shared-spool environment,
+so many sites use it.
+However, it does carry some risks, such as attackers filling the spool disk.
+Some of these problems may be alleviated
+by making the spool a separate filesystem, and placing quotas on it.
+The use of any mode other than 1777 and 755 for the spool directory is
+recommended against but may work properly.
+.Pp
+The mailbox is always locked using
+.Xr flock 2
+while mail is appended.
+Unless the
+.Fl L
+flag is specified, a
+.Pa username.lock
+file is also used.
+.Pp
+If the
+.Xr biff 1
+service is returned by
+.Xr getservbyname 3 ,
+the biff server is notified of delivered mail.
+.Sh ENVIRONMENT
+.Bl -tag -width indent
+.It Ev TZ
+Used to set the appropriate time zone on the timestamp.
+.El
+.Sh FILES
+.Bl -tag -width /tmp/local.XXXXXXXXXX -compact
+.It Pa /tmp/local.XXXXXXXXXX
+temporary files
+.It Pa /var/mail/user
+user's mailbox directory
+.El
+.Sh EXIT STATUS
+.Ex -std mail.local
+.Sh SEE ALSO
+.Xr biff 1 ,
+.Xr mail 1 ,
+.Xr flock 2 ,
+.Xr getservbyname 3 ,
+.Xr comsat 8 ,
+.Xr smtpd 8
+.Sh HISTORY
+A superset of
+.Nm
+(handling mailbox reading as well as mail delivery) appeared in
+.At v7
+as the program
+.Xr mail 1 .
+.Sh BUGS
+Using quotas in
+.Pa /var/mail
+can be problematic if using
+.Xr sendmail 8
+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
+delivered to some users but not others due to disk quotas.
+Even though the message was delivered to some of the recipients,
+.Nm
+will exit with an exit code > 0, causing
+.Xr sendmail 8
+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
+.Xr sendmail 8
+and have disk quotas on
+.Pa /var/mail
+it is imperative that you unset the
+.Dq m
+mailer flag for the
+.Sq local
+mailer.
+To do this, locate the line beginning with
+.Dq Mlocal
+in
+.Pa /etc/mail/sendmail.cf
+and remove the
+.Dq m
+from the flags section, denoted by
+.Dq F= .
+Alternately, you can override the default mailer flags by adding the line:
+.Pp
+.Dl define(`LOCAL_MAILER_FLAGS', `rn9S')dnl
+.Pp
+to your
+.Dq \.mc
+file (this is the source file that is used to generate
+.Pa /etc/mail/sendmail.cf ) .
diff --git a/contrib/libexec/mail.local/mail.local.c b/contrib/libexec/mail.local/mail.local.c
new file mode 100644
index 00000000..a574b3fe
--- /dev/null
+++ b/contrib/libexec/mail.local/mail.local.c
@@ -0,0 +1,392 @@
+/* $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>
+ * Copyright (c) 1996-1998 David Mazieres <dm@lcs.mit.edu>
+ * 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.
+ */
+
+#include "includes.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"
+
+int
+main(int argc, char *argv[])
+{
+ struct passwd *pw;
+ 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:")) != -1)
+ switch (ch) {
+ case 'd': /* backward compatible */
+ break;
+ case 'f':
+ case 'r': /* backward compatible */
+ if (from)
+ merr(EX_USAGE, "multiple -f options");
+ from = optarg;
+ break;
+ case 'l':
+ lockfile=1;
+ break;
+ case 'L':
+ lockfile=0;
+ break;
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (!*argv)
+ usage();
+
+ /*
+ * If from not specified, use the name from getlogin() if the
+ * uid matches, otherwise, use the name from the password file
+ * corresponding to the uid.
+ */
+ uid = getuid();
+ if (!from && (!(from = getlogin()) ||
+ !(pw = getpwnam(from)) || pw->pw_uid != uid))
+ from = (pw = getpwuid(uid)) ? pw->pw_name : "???";
+
+ fd = storemail(from);
+ for (eval = 0; *argv; ++argv) {
+ if ((ch = deliver(fd, *argv, lockfile)) != 0)
+ eval = ch;
+ }
+ exit(eval);
+}
+
+int
+storemail(char *from)
+{
+ FILE *fp = NULL;
+ time_t tval;
+ int fd, eline;
+ size_t len;
+ char *line, *tbuf;
+
+ if ((tbuf = strdup(_PATH_LOCTMP)) == NULL)
+ merr(EX_OSERR, "unable to allocate memory");
+ if ((fd = mkstemp(tbuf)) == -1 || !(fp = fdopen(fd, "w+")))
+ 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, tbuf = NULL; (line = fgetln(stdin, &len));) {
+ /* We have to NUL-terminate the line since fgetln does not */
+ if (line[len - 1] == '\n')
+ 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 {
+ if (eline && line[0] == 'F' && len > 5 &&
+ !memcmp(line, "From ", 5))
+ (void)putc('>', fp);
+ eline = 0;
+ }
+ (void)fprintf(fp, "%s\n", line);
+ if (ferror(fp))
+ break;
+ }
+ free(tbuf);
+
+ /* Output a newline; note, empty messages are allowed. */
+ (void)putc('\n', fp);
+ (void)fflush(fp);
+ if (ferror(fp))
+ merr(EX_OSERR, "temporary file write error");
+ return(fd);
+}
+
+int
+deliver(int fd, char *name, int lockfile)
+{
+ struct stat sb, fsb;
+ struct passwd *pw;
+ 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;
+
+ /*
+ * Disallow delivery to unknown names -- special mailboxes can be
+ * handled in the sendmail aliases file.
+ */
+ if (!(pw = getpwnam(name))) {
+ mwarn("unknown name: %s", name);
+ return(EX_NOUSER);
+ }
+
+ (void)snprintf(path, sizeof path, "%s/%s", _PATH_MAILDIR, name);
+
+ if (lockfile) {
+ lfd = lockspool(name, pw);
+ if (lfd == -1)
+ return(EX_OSERR);
+ }
+
+ /* after this point, always exit via bad to remove lockfile */
+retry:
+ if (lstat(path, &sb)) {
+ if (errno != ENOENT) {
+ mwarn("%s: %s", path, strerror(errno));
+ goto bad;
+ }
+ if ((mbfd = open(path, O_APPEND|O_CREAT|O_EXCL|O_WRONLY|O_EXLOCK,
+ S_IRUSR|S_IWUSR)) == -1) {
+#ifndef HAVE_O_EXLOCK
+ /* XXX : do something! */
+#endif
+ if (errno == EEXIST) {
+ /* file appeared since lstat */
+ goto retry;
+ } else {
+ mwarn("%s: %s", path, strerror(errno));
+ rval = EX_CANTCREAT;
+ goto bad;
+ }
+ }
+ /*
+ * Set the owner and group. Historically, binmail repeated
+ * this at each mail delivery. We no longer do this, assuming
+ * that if the ownership or permissions were changed there
+ * was a reason for doing so.
+ */
+ 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)) {
+ mwarn("%s: linked or special file", path);
+ goto bad;
+ }
+ if ((mbfd = open(path, O_APPEND|O_WRONLY|O_EXLOCK,
+ S_IRUSR|S_IWUSR)) == -1) {
+ mwarn("%s: %s", path, strerror(errno));
+ goto bad;
+ }
+ if (fstat(mbfd, &fsb) == -1) {
+ /* relating error to path may be bad style */
+ mwarn("%s: %s", path, strerror(errno));
+ goto bad;
+ }
+ if (sb.st_dev != fsb.st_dev || sb.st_ino != fsb.st_ino) {
+ mwarn("%s: changed after open", path);
+ goto bad;
+ }
+ /* paranoia? */
+ if (fsb.st_nlink != 1 || !S_ISREG(fsb.st_mode)) {
+ mwarn("%s: linked or special file", path);
+ rval = EX_CANTCREAT;
+ goto bad;
+ }
+ }
+
+ curoff = lseek(mbfd, 0, SEEK_END);
+ (void)snprintf(biffmsg, sizeof biffmsg, "%s@%lld\n", name, curoff);
+ if (lseek(fd, 0, SEEK_SET) == (off_t)-1) {
+ mwarn("temporary file: %s", strerror(errno));
+ goto bad;
+ }
+
+ while ((nr = read(fd, buf, sizeof(buf))) > 0)
+ 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;
+ }
+
+ if (nr == 0) {
+ rval = 0;
+ } else {
+ (void)ftruncate(mbfd, curoff);
+ mwarn("temporary file: %s", strerror(errno));
+ }
+
+bad:
+ if (lfd != -1)
+ unlockspool();
+
+ if (mbfd != -1) {
+ (void)fsync(mbfd); /* Don't wait for update. */
+ (void)close(mbfd); /* Implicit unlock. */
+ }
+
+ if (!rval)
+ notifybiff(biffmsg);
+ return(rval);
+}
+
+void
+notifybiff(char *msg)
+{
+ static struct addrinfo *res0;
+ struct addrinfo hints, *res;
+ static int f = -1;
+ size_t len;
+ int error;
+
+ 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;
+ }
+ }
+
+ 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; /* 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(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
new file mode 100644
index 00000000..bc3137cb
--- /dev/null
+++ b/contrib/libexec/mail.local/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/mail.local/pathnames.h b/contrib/libexec/mail.local/pathnames.h
new file mode 100644
index 00000000..0a2c2731
--- /dev/null
+++ b/contrib/libexec/mail.local/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/etc/README.md b/etc/README.md
new file mode 100644
index 00000000..0c53cc6b
--- /dev/null
+++ b/etc/README.md
@@ -0,0 +1,6 @@
+This directory will contain example OpenSMTPD config files that can be used as
+a reference or for testing specific usecases. Tests that are run as part of
+CI/CD process in docker containers will utilize these files.
+
+
+* `aliases` file - default aliases map that is referenced by default OpenSMTPD config.
diff --git a/etc/aliases b/etc/aliases
new file mode 100644
index 00000000..09fb6cf5
--- /dev/null
+++ b/etc/aliases
@@ -0,0 +1,100 @@
+#
+# $OpenBSD: aliases,v 1.67 2019/01/26 10:58:05 florian Exp $
+#
+# Aliases in this file will NOT be expanded in the header from
+# Mail, but WILL be visible over networks or from /usr/libexec/mail.local.
+#
+# >>>>>>>>>> The program "newaliases" must be run after
+# >> NOTE >> this file is updated for any changes to
+# >>>>>>>>>> show through to smtpd.
+#
+
+# Basic system aliases -- these MUST be present
+MAILER-DAEMON: postmaster
+postmaster: root
+
+# General redirections for important pseudo accounts
+daemon: root
+ftp-bugs: root
+operator: root
+www: root
+
+# Redirections for pseudo accounts that should not receive mail
+_bgpd: /dev/null
+_dhcp: /dev/null
+_dpb: /dev/null
+_dvmrpd: /dev/null
+_eigrpd: /dev/null
+_file: /dev/null
+_fingerd: /dev/null
+_ftp: /dev/null
+_hostapd: /dev/null
+_identd: /dev/null
+_iked: /dev/null
+_isakmpd: /dev/null
+_iscsid: /dev/null
+_ldapd: /dev/null
+_ldpd: /dev/null
+_mopd: /dev/null
+_nsd: /dev/null
+_ntp: /dev/null
+_ospfd: /dev/null
+_ospf6d: /dev/null
+_pbuild: /dev/null
+_pfetch: /dev/null
+_pflogd: /dev/null
+_ping: /dev/null
+_pkgfetch: /dev/null
+_pkguntar: /dev/null
+_portmap: /dev/null
+_ppp: /dev/null
+_rad: /dev/null
+_radiusd: /dev/null
+_rbootd: /dev/null
+_relayd: /dev/null
+_rebound: /dev/null
+_ripd: /dev/null
+_rstatd: /dev/null
+_rusersd: /dev/null
+_rwalld: /dev/null
+_smtpd: /dev/null
+_smtpq: /dev/null
+_sndio: /dev/null
+_snmpd: /dev/null
+_spamd: /dev/null
+_switchd: /dev/null
+_syslogd: /dev/null
+_tcpdump: /dev/null
+_traceroute: /dev/null
+_tftpd: /dev/null
+_unbound: /dev/null
+_unwind: /dev/null
+_vmd: /dev/null
+_x11: /dev/null
+_ypldap: /dev/null
+bin: /dev/null
+build: /dev/null
+nobody: /dev/null
+_tftp_proxy: /dev/null
+_ftp_proxy: /dev/null
+_sndiop: /dev/null
+_syspatch: /dev/null
+_slaacd: /dev/null
+sshd: /dev/null
+
+# Well-known aliases -- these should be filled in!
+# root:
+# manager:
+# dumper:
+
+# RFC 2142: NETWORK OPERATIONS MAILBOX NAMES
+abuse: root
+# noc: root
+security: root
+
+# RFC 2142: SUPPORT MAILBOX NAMES FOR SPECIFIC INTERNET SERVICES
+# hostmaster: root
+# usenet: root
+# news: usenet
+# webmaster: root
+# ftp: root
diff --git a/m4/aclocal-openssh.m4 b/m4/aclocal-openssh.m4
new file mode 100644
index 00000000..2944bb99
--- /dev/null
+++ b/m4/aclocal-openssh.m4
@@ -0,0 +1,179 @@
+dnl $Id: aclocal.m4,v 1.13 2014/01/22 10:30:12 djm Exp $
+dnl
+dnl OpenSSH-specific autoconf macros
+dnl
+
+dnl OSSH_CHECK_CFLAG_COMPILE(check_flag[, define_flag])
+dnl Check that $CC accepts a flag 'check_flag'. If it is supported append
+dnl 'define_flag' to $CFLAGS. If 'define_flag' is not specified, then append
+dnl 'check_flag'.
+AC_DEFUN([OSSH_CHECK_CFLAG_COMPILE], [{
+ AC_MSG_CHECKING([if $CC supports compile flag $1])
+ saved_CFLAGS="$CFLAGS"
+ CFLAGS="$CFLAGS $WERROR $1"
+ _define_flag="$2"
+ test "x$_define_flag" = "x" && _define_flag="$1"
+ AC_COMPILE_IFELSE([AC_LANG_SOURCE([[
+#include <stdlib.h>
+#include <stdio.h>
+int main(int argc, char **argv) {
+ /* Some math to catch -ftrapv problems in the toolchain */
+ int i = 123 * argc, j = 456 + argc, k = 789 - argc;
+ float l = i * 2.1;
+ double m = l / 0.5;
+ long long int n = argc * 12345LL, o = 12345LL * (long long int)argc;
+ printf("%d %d %d %f %f %lld %lld\n", i, j, k, l, m, n, o);
+ exit(0);
+}
+ ]])],
+ [
+if `grep -i "unrecognized option" conftest.err >/dev/null`
+then
+ AC_MSG_RESULT([no])
+ CFLAGS="$saved_CFLAGS"
+else
+ AC_MSG_RESULT([yes])
+ CFLAGS="$saved_CFLAGS $_define_flag"
+fi],
+ [ AC_MSG_RESULT([no])
+ CFLAGS="$saved_CFLAGS" ]
+ )
+}])
+
+dnl OSSH_CHECK_CFLAG_LINK(check_flag[, define_flag])
+dnl Check that $CC accepts a flag 'check_flag'. If it is supported append
+dnl 'define_flag' to $CFLAGS. If 'define_flag' is not specified, then append
+dnl 'check_flag'.
+AC_DEFUN([OSSH_CHECK_CFLAG_LINK], [{
+ AC_MSG_CHECKING([if $CC supports compile flag $1 and linking succeeds])
+ saved_CFLAGS="$CFLAGS"
+ CFLAGS="$CFLAGS $WERROR $1"
+ _define_flag="$2"
+ test "x$_define_flag" = "x" && _define_flag="$1"
+ AC_LINK_IFELSE([AC_LANG_SOURCE([[
+#include <stdlib.h>
+#include <stdio.h>
+int main(int argc, char **argv) {
+ /* Some math to catch -ftrapv problems in the toolchain */
+ int i = 123 * argc, j = 456 + argc, k = 789 - argc;
+ float l = i * 2.1;
+ double m = l / 0.5;
+ long long int n = argc * 12345LL, o = 12345LL * (long long int)argc;
+ printf("%d %d %d %f %f %lld %lld\n", i, j, k, l, m, n, o);
+ exit(0);
+}
+ ]])],
+ [
+if `grep -i "unrecognized option" conftest.err >/dev/null`
+then
+ AC_MSG_RESULT([no])
+ CFLAGS="$saved_CFLAGS"
+else
+ AC_MSG_RESULT([yes])
+ CFLAGS="$saved_CFLAGS $_define_flag"
+fi],
+ [ AC_MSG_RESULT([no])
+ CFLAGS="$saved_CFLAGS" ]
+ )
+}])
+
+dnl OSSH_CHECK_LDFLAG_LINK(check_flag[, define_flag])
+dnl Check that $LD accepts a flag 'check_flag'. If it is supported append
+dnl 'define_flag' to $LDFLAGS. If 'define_flag' is not specified, then append
+dnl 'check_flag'.
+AC_DEFUN([OSSH_CHECK_LDFLAG_LINK], [{
+ AC_MSG_CHECKING([if $LD supports link flag $1])
+ saved_LDFLAGS="$LDFLAGS"
+ LDFLAGS="$LDFLAGS $WERROR $1"
+ _define_flag="$2"
+ test "x$_define_flag" = "x" && _define_flag="$1"
+ AC_LINK_IFELSE([AC_LANG_SOURCE([[
+#include <stdlib.h>
+#include <stdio.h>
+int main(int argc, char **argv) {
+ /* Some math to catch -ftrapv problems in the toolchain */
+ int i = 123 * argc, j = 456 + argc, k = 789 - argc;
+ float l = i * 2.1;
+ double m = l / 0.5;
+ long long int n = argc * 12345LL, o = 12345LL * (long long int)argc;
+ printf("%d %d %d %f %f %lld %lld\n", i, j, k, l, m, n, o);
+ exit(0);
+}
+ ]])],
+ [ AC_MSG_RESULT([yes])
+ LDFLAGS="$saved_LDFLAGS $_define_flag"],
+ [ AC_MSG_RESULT([no])
+ LDFLAGS="$saved_LDFLAGS" ]
+ )
+}])
+
+dnl OSSH_CHECK_HEADER_FOR_FIELD(field, header, symbol)
+dnl Does AC_EGREP_HEADER on 'header' for the string 'field'
+dnl If found, set 'symbol' to be defined. Cache the result.
+dnl TODO: This is not foolproof, better to compile and read from there
+AC_DEFUN([OSSH_CHECK_HEADER_FOR_FIELD], [
+# look for field '$1' in header '$2'
+ dnl This strips characters illegal to m4 from the header filename
+ ossh_safe=`echo "$2" | sed 'y%./+-%__p_%'`
+ dnl
+ ossh_varname="ossh_cv_$ossh_safe""_has_"$1
+ AC_MSG_CHECKING(for $1 field in $2)
+ AC_CACHE_VAL($ossh_varname, [
+ AC_EGREP_HEADER($1, $2, [ dnl
+ eval "$ossh_varname=yes" dnl
+ ], [ dnl
+ eval "$ossh_varname=no" dnl
+ ]) dnl
+ ])
+ ossh_result=`eval 'echo $'"$ossh_varname"`
+ if test -n "`echo $ossh_varname`"; then
+ AC_MSG_RESULT($ossh_result)
+ if test "x$ossh_result" = "xyes"; then
+ AC_DEFINE($3, 1, [Define if you have $1 in $2])
+ fi
+ else
+ AC_MSG_RESULT(no)
+ fi
+])
+
+dnl Check for socklen_t: historically on BSD it is an int, and in
+dnl POSIX 1g it is a type of its own, but some platforms use different
+dnl types for the argument to getsockopt, getpeername, etc. So we
+dnl have to test to find something that will work.
+AC_DEFUN([TYPE_SOCKLEN_T],
+[
+ AC_CHECK_TYPE([socklen_t], ,[
+ AC_MSG_CHECKING([for socklen_t equivalent])
+ AC_CACHE_VAL([curl_cv_socklen_t_equiv],
+ [
+ # Systems have either "struct sockaddr *" or
+ # "void *" as the second argument to getpeername
+ curl_cv_socklen_t_equiv=
+ for arg2 in "struct sockaddr" void; do
+ for t in int size_t unsigned long "unsigned long"; do
+ AC_TRY_COMPILE([
+ #include <sys/types.h>
+ #include <sys/socket.h>
+
+ int getpeername (int, $arg2 *, $t *);
+ ],[
+ $t len;
+ getpeername(0,0,&len);
+ ],[
+ curl_cv_socklen_t_equiv="$t"
+ break
+ ])
+ done
+ done
+
+ if test "x$curl_cv_socklen_t_equiv" = x; then
+ AC_MSG_ERROR([Cannot find a type to use in place of socklen_t])
+ fi
+ ])
+ AC_MSG_RESULT($curl_cv_socklen_t_equiv)
+ AC_DEFINE_UNQUOTED(socklen_t, $curl_cv_socklen_t_equiv,
+ [type to use in place of socklen_t if not defined])],
+ [#include <sys/types.h>
+#include <sys/socket.h>])
+])
+
diff --git a/mk/Makefile.am b/mk/Makefile.am
new file mode 100644
index 00000000..f49c5289
--- /dev/null
+++ b/mk/Makefile.am
@@ -0,0 +1,4 @@
+SUBDIRS = smtpd
+SUBDIRS += smtpctl
+SUBDIRS += mail
+SUBDIRS += smtp
diff --git a/mk/mail/Makefile.am b/mk/mail/Makefile.am
new file mode 100644
index 00000000..cc6d96cb
--- /dev/null
+++ b/mk/mail/Makefile.am
@@ -0,0 +1,5 @@
+SUBDIRS = mail.lmtp
+SUBDIRS += mail.maildir
+SUBDIRS += mail.mboxfile
+SUBDIRS += mail.mda
+
diff --git a/mk/mail/mail.lmtp/Makefile.am b/mk/mail/mail.lmtp/Makefile.am
new file mode 100644
index 00000000..29c33dd1
--- /dev/null
+++ b/mk/mail/mail.lmtp/Makefile.am
@@ -0,0 +1,22 @@
+include $(top_srcdir)/mk/pathnames
+
+pkglibexec_PROGRAMS = mail.lmtp
+
+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
+if !NEED_ERR_H
+AM_CPPFLAGS += -I$(top_srcdir)/openbsd-compat/err_h
+endif
+
+LIBCOMPAT = $(top_builddir)/openbsd-compat/libopenbsd.a
+
+LDADD = $(LIBCOMPAT)
+
+
+
+
+uninstall-hook:
+ rmdir $(DESTDIR)$(pkglibexecdir) 2> /dev/null || /bin/true
diff --git a/mk/mail/mail.maildir/Makefile.am b/mk/mail/mail.maildir/Makefile.am
new file mode 100644
index 00000000..c2afe4d0
--- /dev/null
+++ b/mk/mail/mail.maildir/Makefile.am
@@ -0,0 +1,22 @@
+include $(top_srcdir)/mk/pathnames
+
+pkglibexec_PROGRAMS = mail.maildir
+
+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
+if !NEED_ERR_H
+AM_CPPFLAGS += -I$(top_srcdir)/openbsd-compat/err_h
+endif
+
+LIBCOMPAT = $(top_builddir)/openbsd-compat/libopenbsd.a
+
+LDADD = $(LIBCOMPAT)
+
+
+
+
+uninstall-hook:
+ rmdir $(DESTDIR)$(pkglibexecdir) 2> /dev/null || /bin/true
diff --git a/mk/mail/mail.mboxfile/Makefile.am b/mk/mail/mail.mboxfile/Makefile.am
new file mode 100644
index 00000000..de5f4ea5
--- /dev/null
+++ b/mk/mail/mail.mboxfile/Makefile.am
@@ -0,0 +1,22 @@
+include $(top_srcdir)/mk/pathnames
+
+pkglibexec_PROGRAMS = mail.mboxfile
+
+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
+if !NEED_ERR_H
+AM_CPPFLAGS += -I$(top_srcdir)/openbsd-compat/err_h
+endif
+
+LIBCOMPAT = $(top_builddir)/openbsd-compat/libopenbsd.a
+
+LDADD = $(LIBCOMPAT)
+
+
+
+
+uninstall-hook:
+ rmdir $(DESTDIR)$(pkglibexecdir) 2> /dev/null || /bin/true
diff --git a/mk/mail/mail.mda/Makefile.am b/mk/mail/mail.mda/Makefile.am
new file mode 100644
index 00000000..84bbfdb3
--- /dev/null
+++ b/mk/mail/mail.mda/Makefile.am
@@ -0,0 +1,22 @@
+include $(top_srcdir)/mk/pathnames
+
+pkglibexec_PROGRAMS = mail.mda
+
+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
+if !NEED_ERR_H
+AM_CPPFLAGS += -I$(top_srcdir)/openbsd-compat/err_h
+endif
+
+LIBCOMPAT = $(top_builddir)/openbsd-compat/libopenbsd.a
+
+LDADD = $(LIBCOMPAT)
+
+
+
+
+uninstall-hook:
+ rmdir $(DESTDIR)$(pkglibexecdir) 2> /dev/null || /bin/true
diff --git a/mk/mdoc2man.awk b/mk/mdoc2man.awk
new file mode 100644
index 00000000..726f628c
--- /dev/null
+++ b/mk/mdoc2man.awk
@@ -0,0 +1,391 @@
+#!/usr/bin/awk
+#
+# Copyright (c) 2003 Peter Stuge <stuge-mdoc2man@cdy.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.
+
+# Dramatically overhauled by Tim Kientzle. This version almost
+# handles library-style pages with Fn, Ft, etc commands. Still
+# a lot of problems...
+
+BEGIN {
+ displaylines = 0
+ trailer = ""
+ out = ""
+ sep = ""
+ nextsep = " "
+}
+
+# Add a word with appropriate preceding whitespace
+# Maintain a short queue of the expected upcoming word separators.
+function add(str) {
+ out=out sep str
+ sep = nextsep
+ nextsep = " "
+}
+
+# Add a word with no following whitespace
+# Use for opening punctuation such as '('
+function addopen(str) {
+ add(str)
+ sep = ""
+}
+
+# Add a word with no preceding whitespace
+# Use for closing punctuation such as ')' or '.'
+function addclose(str) {
+ sep = ""
+ add(str)
+}
+
+# Add a word with no space before or after
+# Use for separating punctuation such as '='
+function addpunct(str) {
+ sep = ""
+ add(str)
+ sep = ""
+}
+
+# Emit the current line so far
+function endline() {
+ addclose(trailer)
+ trailer = ""
+ if(length(out) > 0) {
+ print out
+ out=""
+ }
+ if(displaylines > 0) {
+ displaylines = displaylines - 1
+ if (displaylines == 0)
+ dispend()
+ }
+ # First word on next line has no preceding whitespace
+ sep = ""
+}
+
+function linecmd(cmd) {
+ endline()
+ add(cmd)
+ endline()
+}
+
+function breakline() {
+ linecmd(".br")
+}
+
+# Start an indented display
+function dispstart() {
+ linecmd(".RS 4")
+}
+
+# End an indented display
+function dispend() {
+ linecmd(".RE")
+}
+
+# Collect rest of input line
+function wtail() {
+ retval=""
+ while(w<nwords) {
+ if(length(retval))
+ retval=retval " "
+ retval=retval words[++w]
+ }
+ return retval
+}
+
+function splitwords(l, dest, n, o, w) {
+ n = 1
+ delete dest
+ while (length(l) > 0) {
+ sub("^[ \t]*", "", l)
+ if (match(l, "^\"")) {
+ l = substr(l, 2)
+ o = index(l, "\"")
+ if (o > 0) {
+ w = substr(l, 1, o-1)
+ l = substr(l, o+1)
+ dest[n++] = w
+ } else {
+ dest[n++] = l
+ l = ""
+ }
+ } else {
+ o = match(l, "[ \t]")
+ if (o > 0) {
+ w = substr(l, 1, o-1)
+ l = substr(l, o+1)
+ dest[n++] = w
+ } else {
+ dest[n++] = l
+ l = ""
+ }
+ }
+ }
+ return n-1
+}
+
+! /^\./ {
+ out = $0
+ endline()
+ next
+}
+
+/^\.\\"/ { next }
+
+{
+ sub("^\\.","")
+ nwords=splitwords($0, words)
+ # TODO: Instead of iterating 'w' over the array, have a separate
+ # function that returns 'next word' and use that. This will allow
+ # proper handling of double-quoted arguments as well.
+ for(w=1;w<=nwords;w++) {
+ if(match(words[w],"^Li$")) { # Literal; rest of line is unformatted
+ dispstart()
+ displaylines = 1
+ } else if(match(words[w],"^Dl$")) { # Display literal
+ dispstart()
+ displaylines = 1
+ } else if(match(words[w],"^Bd$")) { # Begin display
+ if(match(words[w+1],"-literal")) {
+ dispstart()
+ linecmd(".nf")
+ displaylines=10000
+ w=nwords
+ }
+ } else if(match(words[w],"^Ed$")) { # End display
+ displaylines = 0
+ dispend()
+ } else if(match(words[w],"^Ns$")) { # Suppress space after next word
+ nextsep = ""
+ } else if(match(words[w],"^No$")) { # Normal text
+ add(words[++w])
+ } else if(match(words[w],"^Dq$")) { # Quote
+ addopen("``")
+ add(words[++w])
+ while(w<nwords&&!match(words[w+1],"^[\\.,]"))
+ add(words[++w])
+ addclose("''")
+ } else if(match(words[w],"^Do$")) {
+ addopen("``")
+ } else if(match(words[w],"^Dc$")) {
+ addclose("''")
+ } else if(match(words[w],"^Oo$")) {
+ addopen("[")
+ } else if(match(words[w],"^Oc$")) {
+ addclose("]")
+ } else if(match(words[w],"^Ao$")) {
+ addopen("<")
+ } else if(match(words[w],"^Ac$")) {
+ addclose(">")
+ } else if(match(words[w],"^Dd$")) {
+ date=wtail()
+ next
+ } else if(match(words[w],"^Dt$")) {
+ id=wtail()
+ next
+ } else if(match(words[w],"^Ox$")) {
+ add("OpenBSD")
+ } else if(match(words[w],"^Fx$")) {
+ add("FreeBSD")
+ } else if(match(words[w],"^Nx$")) {
+ add("NetBSD")
+ } else if(match(words[w],"^St$")) {
+ if (match(words[w+1], "^-p1003.1$")) {
+ w++
+ add("IEEE Std 1003.1 (``POSIX.1'')")
+ } else if(match(words[w+1], "^-p1003.1-96$")) {
+ w++
+ add("ISO/IEC 9945-1:1996 (``POSIX.1'')")
+ } else if(match(words[w+1], "^-p1003.1-88$")) {
+ w++
+ add("IEEE Std 1003.1-1988 (``POSIX.1'')")
+ } else if(match(words[w+1], "^-p1003.1-2001$")) {
+ w++
+ add("IEEE Std 1003.1-2001 (``POSIX.1'')")
+ } else if(match(words[w+1], "^-susv2$")) {
+ w++
+ add("Version 2 of the Single UNIX Specification (``SUSv2'')")
+ }
+ } else if(match(words[w],"^Ex$")) {
+ if (match(words[w+1], "^-std$")) {
+ w++
+ add("The \\fB" name "\\fP utility exits 0 on success, and >0 if an error occurs.")
+ }
+ } else if(match(words[w],"^Os$")) {
+ add(".TH " id " \"" date "\" \"" wtail() "\"")
+ } else if(match(words[w],"^Sh$")) {
+ section=wtail()
+ add(".SH " section)
+ linecmd(".ad l")
+ } else if(match(words[w],"^Xr$")) {
+ add("\\fB" words[++w] "\\fP(" words[++w] ")" words[++w])
+ } else if(match(words[w],"^Nm$")) {
+ if(match(section,"SYNOPSIS"))
+ breakline()
+ if(w >= nwords)
+ n=name
+ else if (match(words[w+1], "^[A-Z][a-z]$"))
+ n=name
+ else if (match(words[w+1], "^[.,;:]$"))
+ n=name
+ else {
+ n=words[++w]
+ if(!length(name))
+ name=n
+ }
+ if(!length(n))
+ n=name
+ add("\\fB\\%" n "\\fP")
+ } else if(match(words[w],"^Nd$")) {
+ add("\\- " wtail())
+ } else if(match(words[w],"^Fl$")) {
+ add("\\fB\\-" words[++w] "\\fP")
+ } else if(match(words[w],"^Ar$")) {
+ addopen("\\fI")
+ if(w==nwords)
+ add("file ...\\fP")
+ else
+ add(words[++w] "\\fP")
+ } else if(match(words[w],"^Cm$")) {
+ add("\\fB" words[++w] "\\fP")
+ } else if(match(words[w],"^Op$")) {
+ addopen("[")
+ option=1
+ trailer="]" trailer
+ } else if(match(words[w],"^Pp$")) {
+ linecmd(".PP")
+ } else if(match(words[w],"^An$")) {
+ endline()
+ } else if(match(words[w],"^Ss$")) {
+ add(".SS")
+ } else if(match(words[w],"^Ft$")) {
+ if (match(section, "SYNOPSIS")) {
+ breakline()
+ }
+ add("\\fI" wtail() "\\fP")
+ if (match(section, "SYNOPSIS")) {
+ breakline()
+ }
+ } else if(match(words[w],"^Fn$")) {
+ ++w
+ F = "\\fB\\%" words[w] "\\fP("
+ Fsep = ""
+ while(w<nwords) {
+ ++w
+ if (match(words[w], "^[.,:]$")) {
+ --w
+ break
+ }
+ gsub(" ", "\\ ", words[w])
+ F = F Fsep "\\fI\\%" words[w] "\\fP"
+ Fsep = ", "
+ }
+ add(F ")")
+ if (match(section, "SYNOPSIS")) {
+ addclose(";")
+ }
+ } else if(match(words[w],"^Fo$")) {
+ w++
+ F = "\\fB\\%" words[w] "\\fP("
+ Fsep = ""
+ } else if(match(words[w],"^Fa$")) {
+ w++
+ gsub(" ", "\\ ", words[w])
+ F = F Fsep "\\fI\\%" words[w] "\\fP"
+ Fsep = ", "
+ } else if(match(words[w],"^Fc$")) {
+ add(F ")")
+ if (match(section, "SYNOPSIS")) {
+ addclose(";")
+ }
+ } else if(match(words[w],"^Va$")) {
+ w++
+ add("\\fI" words[w] "\\fP")
+ } else if(match(words[w],"^In$")) {
+ w++
+ add("\\fB#include <" words[w] ">\\fP")
+ } else if(match(words[w],"^Pa$")) {
+ addopen("\\fI")
+ w++
+ if(match(words[w],"^\\."))
+ add("\\&")
+ add(words[w] "\\fP")
+ } else if(match(words[w],"^Dv$")) {
+ add(".BR")
+ } else if(match(words[w],"^Em|Ev$")) {
+ add(".IR")
+ } else if(match(words[w],"^Pq$")) {
+ addopen("(")
+ trailer=")" trailer
+ } else if(match(words[w],"^Aq$")) {
+ addopen("\\%<")
+ trailer=">" trailer
+ } else if(match(words[w],"^Brq$")) {
+ addopen("{")
+ trailer="}" trailer
+ } else if(match(words[w],"^S[xy]$")) {
+ add(".B " wtail())
+ } else if(match(words[w],"^Ic$")) {
+ add("\\fB")
+ trailer="\\fP" trailer
+ } else if(match(words[w],"^Bl$")) {
+ oldoptlist=optlist
+ linecmd(".RS 5")
+ if(match(words[w+1],"-bullet"))
+ optlist=1
+ else if(match(words[w+1],"-enum")) {
+ optlist=2
+ enum=0
+ } else if(match(words[w+1],"-tag"))
+ optlist=3
+ else if(match(words[w+1],"-item"))
+ optlist=4
+ else if(match(words[w+1],"-bullet"))
+ optlist=1
+ w=nwords
+ } else if(match(words[w],"^El$")) {
+ linecmd(".RE")
+ optlist=oldoptlist
+ } else if(match(words[w],"^It$")&&optlist) {
+ if(optlist==1)
+ add(".IP \\(bu")
+ else if(optlist==2)
+ add(".IP " ++enum ".")
+ else if(optlist==3) {
+ add(".TP")
+ endline()
+ if(match(words[w+1],"^Pa$|^Ev$")) {
+ add(".B")
+ w++
+ }
+ } else if(optlist==4)
+ add(".IP")
+ } else if(match(words[w],"^Xo$")) {
+ # TODO: Figure out how to handle this
+ } else if(match(words[w],"^Xc$")) {
+ # TODO: Figure out how to handle this
+ } else if(match(words[w],"^[=]$")) {
+ addpunct(words[w])
+ } else if(match(words[w],"^[[{(]$")) {
+ addopen(words[w])
+ } else if(match(words[w],"^[\\])}.,;:]$")) {
+ addclose(words[w])
+ } else {
+ add(words[w])
+ }
+ }
+ if(match(out,"^\\.[^a-zA-Z]"))
+ sub("^\\.","",out)
+ endline()
+}
diff --git a/mk/pathnames b/mk/pathnames
new file mode 100644
index 00000000..5cff32da
--- /dev/null
+++ b/mk/pathnames
@@ -0,0 +1,10 @@
+smtpd_srcdir = $(top_srcdir)/usr.sbin/smtpd
+compat_srcdir = $(top_srcdir)/openbsd-compat
+regress_srcdir = $(top_srcdir)/regress/bin
+
+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
new file mode 100644
index 00000000..875cacdc
--- /dev/null
+++ b/mk/smtp/Makefile.am
@@ -0,0 +1,59 @@
+include $(top_srcdir)/mk/pathnames
+
+bin_PROGRAMS= smtp
+
+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
+
+AM_CPPFLAGS= -I$(top_srcdir)/smtpd \
+ -I$(top_srcdir)/openbsd-compat
+if !NEED_ERR_H
+AM_CPPFLAGS += -I$(top_srcdir)/openbsd-compat/err_h
+endif
+
+LIBCOMPAT= $(top_builddir)/openbsd-compat/libopenbsd.a
+
+LDADD= $(LIBCOMPAT)
+
+# need to define _GNU_SOURCE to get:
+# EAI_NODATA defined
+# {v,}asprintf
+# setres{g,u}id
+CFLAGS+= -D_GNU_SOURCE
+CPPFLAGS= -I$(srcdir) @CPPFLAGS@ $(PATHS) @DEFS@
+
+MANPAGES= smtp.1.out
+MANPAGES_IN= $(top_srcdir)/usr.sbin/smtpd/smtp.1
+
+EXTRA_DIST= $(MANPAGES_IN)
+
+PATHSUBS= -e 's|/var/run/smtpd.sock|$(sockdir)/smtpd.sock|g' \
+ -e 's|/usr/libexec|$(libexecdir)|g' \
+ -e 's|/etc/mail/|$(sysconfdir)/|g'
+
+FIXPATHSCMD= $(SED) $(PATHSUBS)
+
+
+$(MANPAGES): $(MANPAGES_IN)
+ manpage=$(top_srcdir)/usr.sbin/smtpd/`echo $@ | sed 's/\.out$$//'`; \
+ if test "$(MANTYPE)" = "man"; then \
+ $(FIXPATHSCMD) $${manpage} | $(AWK) -f $(srcdir)/../mdoc2man.awk > $@; \
+ else \
+ $(FIXPATHSCMD) $${manpage} > $@; \
+ fi
+
+install-exec-hook: $(CONFIGFILES) $(MANPAGES)
+ $(MKDIR_P) $(DESTDIR)$(mandir)/$(mansubdir)1
+ $(INSTALL) -m 644 smtp.1.out $(DESTDIR)$(mandir)/$(mansubdir)1/smtp.1
+ rm smtp.1.out
+
+uninstall-hook:
+ rm -f $(DESTDIR)$(mandir)/$(mansubdir)1/smtp.1
+ rmdir $(DESTDIR)$(mandir)/$(mansubdir)1 2> /dev/null || /bin/true
diff --git a/mk/smtpctl/Makefile.am b/mk/smtpctl/Makefile.am
new file mode 100644
index 00000000..f7d1b418
--- /dev/null
+++ b/mk/smtpctl/Makefile.am
@@ -0,0 +1,99 @@
+include $(top_srcdir)/mk/pathnames
+
+sbin_PROGRAMS= smtpctl
+
+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+= $(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+= $(top_srcdir)/usr.sbin/smtpd/crypto.c
+
+smtpctl_CFLAGS= -DNO_IO -DCONFIG_MINIMUM
+smtpctl_CFLAGS+= -DPATH_GZCAT=\"$(ZCAT)\" \
+ -DPATH_ENCRYPT=\"$(pkglibexecdir)/encrypt\"
+
+AM_CPPFLAGS= -I$(top_srcdir)/smtpd \
+ -I$(top_srcdir)/openbsd-compat
+if !NEED_ERR_H
+AM_CPPFLAGS += -I$(top_srcdir)/openbsd-compat/err_h
+endif
+
+LIBCOMPAT= $(top_builddir)/openbsd-compat/libopenbsd.a
+
+LDADD= $(LIBCOMPAT)
+if HAVE_DB_API
+LDADD+= $(DB_LIB)
+endif
+
+# need to define _GNU_SOURCE to get:
+# EAI_NODATA defined
+# {v,}asprintf
+# setres{g,u}id
+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= $(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)
+
+PATHSUBS= -e 's|/var/run/smtpd.sock|$(sockdir)/smtpd.sock|g' \
+ -e 's|/usr/libexec|$(libexecdir)|g' \
+ -e 's|/etc/mail/|$(sysconfdir)/|g'
+
+FIXPATHSCMD= $(SED) $(PATHSUBS)
+
+if NEED_LIBASR
+AM_CPPFLAGS+= -I$(top_srcdir)/openbsd-compat/libasr
+endif
+
+$(MANPAGES): $(MANPAGES_IN)
+ manpage=$(top_srcdir)/usr.sbin/smtpd/`echo $@ | sed 's/\.out$$//'`; \
+ if test "$(MANTYPE)" = "man"; then \
+ $(FIXPATHSCMD) $${manpage} | $(AWK) -f $(srcdir)/../mdoc2man.awk > $@; \
+ else \
+ $(FIXPATHSCMD) $${manpage} > $@; \
+ fi
+
+install-exec-hook: $(CONFIGFILES) $(MANPAGES)
+ $(MKDIR_P) $(DESTDIR)$(mandir)/$(mansubdir)8
+ chgrp $(SMTPD_QUEUE_USER) $(DESTDIR)$(sbindir)/smtpctl || true
+ chmod 2555 $(DESTDIR)$(sbindir)/smtpctl || true
+ $(INSTALL) -m 644 smtpctl.8.out $(DESTDIR)$(mandir)/$(mansubdir)8/smtpctl.8
+ $(INSTALL) -m 644 sendmail.8.out $(DESTDIR)$(mandir)/$(mansubdir)8/sendmail.8
+ $(INSTALL) -m 644 makemap.8.out $(DESTDIR)$(mandir)/$(mansubdir)8/makemap.8
+ $(INSTALL) -m 644 newaliases.8.out $(DESTDIR)$(mandir)/$(mansubdir)8/newaliases.8
+ rm smtpctl.8.out sendmail.8.out makemap.8.out newaliases.8.out
+
+uninstall-hook:
+ rm -f $(DESTDIR)$(mandir)/$(mansubdir)8/smtpctl.8
+ rm -f $(DESTDIR)$(mandir)/$(mansubdir)8/sendmail.8
+ rm -f $(DESTDIR)$(mandir)/$(mansubdir)8/makemap.8
+ rm -f $(DESTDIR)$(mandir)/$(mansubdir)8/newaliases.8
+ rmdir $(DESTDIR)$(mandir)/$(mansubdir)8 2> /dev/null || /bin/true
diff --git a/mk/smtpd/Makefile.am b/mk/smtpd/Makefile.am
new file mode 100644
index 00000000..6d652d48
--- /dev/null
+++ b/mk/smtpd/Makefile.am
@@ -0,0 +1,196 @@
+# In OpenBSD, smtpd's files are installed this way:
+#
+# /etc/mail/smtpd.conf
+# /usr/sbin/smtpd
+#
+#
+# For OpenSMTPD portable, here's where files are installed:
+# (assuming PREFIX=/usr/local)
+#
+# /usr/local/etc/smtpd.conf
+# /usr/local/sbin/smtpd
+
+include $(top_srcdir)/mk/pathnames
+
+sbin_PROGRAMS= smtpd
+
+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+= $(top_srcdir)/usr.sbin/smtpd/crypto.c
+smtpd_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/compress_gzip.c
+if HAVE_DB_API
+smtpd_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/table_db.c
+endif
+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$(top_srcdir)/usr.sbin/smtpd \
+ -I$(compat_srcdir)
+if !NEED_ERR_H
+AM_CPPFLAGS += -I$(top_srcdir)/openbsd-compat/err_h
+endif
+if !SUPPORT_PATHS_H
+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)
+
+# need to define _GNU_SOURCE to get:
+# EAI_NODATA defined
+# {v,}asprintf
+# setres{g,u}id
+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= $(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= $(top_srcdir)/usr.sbin/smtpd/smtpd.conf
+
+EXTRA_DIST= $(CONFIGFILES_IN) $(MANPAGES_IN)
+
+
+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|/usr/local/libexec/smtpd/|$(pkglibexecdir)|g'
+
+FIXPATHSCMD= $(SED) $(PATHSUBS)
+
+$(MANPAGES): $(MANPAGES_IN)
+ manpage=$(top_srcdir)/usr.sbin/smtpd/`echo $@ | sed 's/\.out$$//'`; \
+ if test "$(MANTYPE)" = "man"; then \
+ $(FIXPATHSCMD) $${manpage} | $(AWK) -f $(srcdir)/../mdoc2man.awk > $@; \
+ else \
+ $(FIXPATHSCMD) $${manpage} > $@; \
+ fi
+
+$(CONFIGFILES): $(CONFIGFILES_IN)
+ conffile=$(top_srcdir)/usr.sbin/smtpd/`echo $@ | sed 's/.out$$//'`; \
+ $(CAT) $${conffile} > $@
+
+
+# smtpd.conf
+# newaliases makemap
+install-exec-hook: $(CONFIGFILES) $(MANPAGES)
+ $(MKDIR_P) $(DESTDIR)$(sysconfdir)
+ $(MKDIR_P) $(DESTDIR)$(bindir)
+ $(MKDIR_P) $(DESTDIR)$(mandir)/$(mansubdir)5
+ $(MKDIR_P) $(DESTDIR)$(mandir)/$(mansubdir)8
+
+ @if [ ! -f $(DESTDIR)$(sysconfdir)/smtpd.conf ]; then \
+ $(INSTALL) -m 644 smtpd.conf.out $(DESTDIR)$(sysconfdir)/smtpd.conf; \
+ else \
+ echo "$(DESTDIR)$(sysconfdir)/smtpd.conf already exists, install will not overwrite"; \
+ fi
+
+ $(INSTALL) -m 644 aliases.5.out $(DESTDIR)$(mandir)/$(mansubdir)5/aliases.5
+ $(INSTALL) -m 644 forward.5.out $(DESTDIR)$(mandir)/$(mansubdir)5/forward.5
+ $(INSTALL) -m 644 table.5.out $(DESTDIR)$(mandir)/$(mansubdir)5/table.5
+ $(INSTALL) -m 644 smtpd.8.out $(DESTDIR)$(mandir)/$(mansubdir)8/smtpd.8
+ $(INSTALL) -m 644 smtpd.conf.5.out $(DESTDIR)$(mandir)/$(mansubdir)5/smtpd.conf.5
+ rm aliases.5.out forward.5.out table.5.out \
+ smtpd.8.out smtpd.conf.5.out smtpd.conf.out
+
+uninstall-hook:
+# XXX to make "make distcheck" happy we need to rm smtpd.conf
+ 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 \
+ $(DESTDIR)$(mandir)/$(mansubdir)8/smtpd.8
+ rmdir $(DESTDIR)$(mandir)/$(mansubdir)5 \
+ $(DESTDIR)$(mandir)/$(mansubdir)8 2> /dev/null || /bin/true
diff --git a/openbsd-compat/Makefile.am b/openbsd-compat/Makefile.am
new file mode 100644
index 00000000..170c2b6c
--- /dev/null
+++ b/openbsd-compat/Makefile.am
@@ -0,0 +1,235 @@
+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
+
+if NEED_ARC4RANDOM
+libopenbsd_a_SOURCES += arc4random.c
+endif
+
+if NEED_BASE64
+libopenbsd_a_SOURCES += base64.c
+endif
+
+if NEED_BASENAME
+libopenbsd_a_SOURCES += basename.c
+endif
+
+if NEED_CLOCK_GETTIME
+libopenbsd_a_SOURCES += clock_gettime.c
+endif
+
+if NEED_CLOSEFROM
+libopenbsd_a_SOURCES += closefrom.c
+endif
+
+if NEED_CRYPT_CHECKPASS
+libopenbsd_a_SOURCES += crypt_checkpass.c
+endif
+
+if NEED_DAEMON
+libopenbsd_a_SOURCES += daemon.c
+endif
+
+if NEED_DIRNAME
+libopenbsd_a_SOURCES += dirname.c
+endif
+
+if NEED_ERR
+libopenbsd_a_SOURCES += bsd-err.c
+endif
+
+if NEED_ERRC
+libopenbsd_a_SOURCES += errc.c
+endif
+
+if NEED_EVENT_ASR_RUN
+libopenbsd_a_SOURCES += event_asr_run.c
+endif
+
+if NEED_EXPLICIT_BZERO
+libopenbsd_a_SOURCES += explicit_bzero.c
+endif
+
+if NEED_FGETLN
+libopenbsd_a_SOURCES += fgetln.c
+endif
+
+if NEED_FMT_SCALED
+libopenbsd_a_SOURCES += fmt_scaled.c
+endif
+
+if NEED_FPARSELN
+libopenbsd_a_SOURCES += fparseln.c
+endif
+
+if NEED_FREEZERO
+libopenbsd_a_SOURCES += freezero.c
+endif
+
+if NEED_GETOPT
+libopenbsd_a_SOURCES += getopt.c
+endif
+
+if NEED_GETPEEREID
+libopenbsd_a_SOURCES += getpeereid.c
+endif
+
+if NEED_IMSG
+libopenbsd_a_SOURCES += imsg.c
+libopenbsd_a_SOURCES += imsg-buffer.c
+endif
+
+if NEED_INET_NET_PTON
+libopenbsd_a_SOURCES += inet_net_pton.c
+endif
+
+if NEED_NANOSLEEP
+libopenbsd_a_SOURCES += nanosleep.c
+endif
+
+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
+
+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
+
+if NEED_SETRESGID
+libopenbsd_a_SOURCES += setresgid.c
+endif
+
+if NEED_SETRESUID
+libopenbsd_a_SOURCES += setresuid.c
+endif
+
+if NEED_SETEGID
+libopenbsd_a_SOURCES += setegid.c
+endif
+
+if NEED_SETEUID
+libopenbsd_a_SOURCES += seteuid.c
+endif
+
+if NEED_SIGNAL
+libopenbsd_a_SOURCES += signal.c
+endif
+
+if NEED_SSL_CTX_USE_CERTIFICATE_CHAIN_MEM
+libopenbsd_a_SOURCES += SSL_CTX_use_certificate_chain_mem.c
+endif
+
+if NEED_STRERROR
+libopenbsd_a_SOURCES += strerror.c
+endif
+
+if NEED_STRLCAT
+libopenbsd_a_SOURCES += strlcat.c
+endif
+
+if NEED_STRLCPY
+libopenbsd_a_SOURCES += strlcpy.c
+endif
+
+if NEED_STRMODE
+libopenbsd_a_SOURCES += strmode.c
+endif
+
+if NEED_STRSEP
+libopenbsd_a_SOURCES += strsep.c
+endif
+
+if NEED_STRTONUM
+libopenbsd_a_SOURCES += strtonum.c
+endif
+
+if NEED_STRNDUP
+libopenbsd_a_SOURCES += strndup.c
+endif
+
+if NEED_STRNLEN
+libopenbsd_a_SOURCES += strnlen.c
+endif
+
+if NEED_USLEEP
+libopenbsd_a_SOURCES += usleep.c
+endif
+
+if NEED_VIS
+libopenbsd_a_SOURCES += vis.c
+endif
+
+if NEED_WAITPID
+libopenbsd_a_SOURCES += bsd-waitpid.c
+endif
+
+
+
+EXTRA_DIST = base64.h
+EXTRA_DIST += bsd-misc.h
+EXTRA_DIST += bsd-waitpid.h
+EXTRA_DIST += chacha_private.h
+EXTRA_DIST += defines.h
+EXTRA_DIST += entropy.h
+EXTRA_DIST += imsg.h
+EXTRA_DIST += includes.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
+
+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
+endif
diff --git a/openbsd-compat/NOTES b/openbsd-compat/NOTES
new file mode 100644
index 00000000..42aefc7d
--- /dev/null
+++ b/openbsd-compat/NOTES
@@ -0,0 +1,37 @@
+List of files and where they come from
+
+arc4random.c portable openssh
+base64.{c,h} portable openssh
+basename.c portable openssh
+bsd-closefrom.c portable openssh
+bsd-getpeereid.c portable openssh
+bsd-waitpid.{c,h} portable openssh
+clock_gettime.c handmade
+daemon.c portable openssh
+defines.h portable openssh
+dirname.c portable openssh
+entropy.{c,h} portable openssh
+event_asr_run.c end of /usr/src/lib/libevent/event.c
+fgetln.c part of /usr/src/usr.bin/make/util.c
+fmt_scaled.c portable openssh
+fparseln.c part of /usr/src/lib/libutil/fparseln.c
+getopt.c portable openssh
+imsg-buffer.c part of /usr/src/libutil/imsg-buffer.c
+imsg.{c,h} part of /usr/src/libutil/imsg.c
+includes.h portable openssh
+log.h portable openssh
+mktemp.c portable openssh
+openbsd-compat.h portable openssh
+pidfile.c /usr/src/lib/libutil/pidfile.c
+pw_dup.c /usr/src/lib/libc/gen/pw_dup.c
+reallocarray.c /usr/src/lib/libc/stdlib/reallocarray.c
+setproctitle.c portable openssh
+strlcat.c portable openssh
+strlcpy.c portable openssh
+strmode.c portable openssh
+strsep.c portable openssh
+strtonum.c portable openssh
+sys/queue.h portable openssh
+sys/tree.h portable openssh
+vis.{c,h} portable openssh
+xmalloc.{c,h} portable openssh
diff --git a/openbsd-compat/SSL_CTX_use_certificate_chain_mem.c b/openbsd-compat/SSL_CTX_use_certificate_chain_mem.c
new file mode 100644
index 00000000..3a47ff0e
--- /dev/null
+++ b/openbsd-compat/SSL_CTX_use_certificate_chain_mem.c
@@ -0,0 +1,174 @@
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to. The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code. The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * 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 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. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * "This product includes cryptographic software written by
+ * Eric Young (eay@cryptsoft.com)"
+ * The word 'cryptographic' can be left out if the rouines from the library
+ * being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ * the apps directory (application code) you must include an acknowledgement:
+ * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``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 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.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed. i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.]
+ */
+
+/*
+ * SSL operations needed when running in a privilege separated environment.
+ * Adapted from openssl's ssl_rsa.c by Pierre-Yves Ritschard .
+ */
+
+#include "includes.h"
+
+#include <sys/types.h>
+
+#include <limits.h>
+#include <unistd.h>
+#include <stdio.h>
+
+#include <openssl/err.h>
+#include <openssl/bio.h>
+#include <openssl/objects.h>
+#include <openssl/evp.h>
+#include <openssl/x509.h>
+#include <openssl/pem.h>
+#include <openssl/ssl.h>
+
+#include "log.h"
+#include "ssl.h"
+
+#define SSL_ECDH_CURVE "prime256v1"
+
+/*
+ * Read a bio that contains our certificate in "PEM" format,
+ * possibly followed by a sequence of CA certificates that should be
+ * sent to the peer in the Certificate message.
+ */
+static int
+ssl_ctx_use_certificate_chain_bio(SSL_CTX *ctx, BIO *in)
+{
+ int ret = 0;
+ X509 *x = NULL;
+
+ ERR_clear_error(); /* clear error stack for SSL_CTX_use_certificate() */
+
+ x = PEM_read_bio_X509_AUX(in, NULL, SSL_CTX_get_default_passwd_cb(ctx),
+ SSL_CTX_get_default_passwd_cb_userdata(ctx));
+ if (x == NULL) {
+ SSLerr(SSL_F_SSL_CTX_USE_CERTIFICATE_FILE, ERR_R_PEM_LIB);
+ goto end;
+ }
+
+ ret = SSL_CTX_use_certificate(ctx, x);
+
+ if (ERR_peek_error() != 0)
+ ret = 0;
+ /* Key/certificate mismatch doesn't imply ret==0 ... */
+ if (ret) {
+ /*
+ * If we could set up our certificate, now proceed to
+ * the CA certificates.
+ */
+ X509 *ca;
+ STACK_OF(X509) *chain;
+ int r;
+ unsigned long err;
+
+ SSL_CTX_get_extra_chain_certs_only(ctx, &chain);
+ if (chain != NULL) {
+ sk_X509_pop_free(chain, X509_free);
+ SSL_CTX_clear_extra_chain_certs(ctx);
+ }
+
+ while ((ca = PEM_read_bio_X509(in, NULL,
+ SSL_CTX_get_default_passwd_cb(ctx),
+ SSL_CTX_get_default_passwd_cb_userdata(ctx))) != NULL) {
+ r = SSL_CTX_add_extra_chain_cert(ctx, ca);
+ if (!r) {
+ X509_free(ca);
+ ret = 0;
+ goto end;
+ }
+ /*
+ * Note that we must not free r if it was successfully
+ * added to the chain (while we must free the main
+ * certificate, since its reference count is increased
+ * by SSL_CTX_use_certificate).
+ */
+ }
+
+ /* When the while loop ends, it's usually just EOF. */
+ err = ERR_peek_last_error();
+ if (ERR_GET_LIB(err) == ERR_LIB_PEM &&
+ ERR_GET_REASON(err) == PEM_R_NO_START_LINE)
+ ERR_clear_error();
+ else
+ ret = 0; /* some real error */
+ }
+
+end:
+ if (x != NULL)
+ X509_free(x);
+ return (ret);
+}
+
+int
+SSL_CTX_use_certificate_chain_mem(SSL_CTX *ctx, void *buf, int len)
+{
+ BIO *in;
+ int ret = 0;
+
+ in = BIO_new_mem_buf(buf, len);
+ if (in == NULL) {
+ SSLerr(SSL_F_SSL_CTX_USE_CERTIFICATE_FILE, ERR_R_BUF_LIB);
+ goto end;
+ }
+
+ ret = ssl_ctx_use_certificate_chain_bio(ctx, in);
+
+end:
+ BIO_free(in);
+ return (ret);
+}
diff --git a/openbsd-compat/arc4random.c b/openbsd-compat/arc4random.c
new file mode 100644
index 00000000..f5cda877
--- /dev/null
+++ b/openbsd-compat/arc4random.c
@@ -0,0 +1,246 @@
+/* OPENBSD ORIGINAL: lib/libc/crypto/arc4random.c */
+
+/* $OpenBSD: arc4random.c,v 1.25 2013/10/01 18:34:57 markus Exp $ */
+
+/*
+ * Copyright (c) 1996, David Mazieres <dm@uun.org>
+ * Copyright (c) 2008, Damien Miller <djm@openbsd.org>
+ * Copyright (c) 2013, Markus Friedl <markus@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.
+ */
+
+/*
+ * ChaCha based random number generator for OpenBSD.
+ */
+
+#include "includes.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+
+#include <openssl/rand.h>
+#include <openssl/err.h>
+
+#include "log.h"
+
+#define KEYSTREAM_ONLY
+#include "chacha_private.h"
+
+#ifdef __GNUC__
+#define inline __inline
+#else /* !__GNUC__ */
+#define inline
+#endif /* !__GNUC__ */
+
+/* OpenSSH isn't multithreaded */
+#define _ARC4_LOCK()
+#define _ARC4_UNLOCK()
+
+#define KEYSZ 32
+#define IVSZ 8
+#define BLOCKSZ 64
+#define RSBUFSZ (16*BLOCKSZ)
+static int rs_initialized;
+static pid_t rs_stir_pid;
+static chacha_ctx rs; /* chacha context for random keystream */
+static u_char rs_buf[RSBUFSZ]; /* keystream blocks */
+static size_t rs_have; /* valid bytes at end of rs_buf */
+static size_t rs_count; /* bytes till reseed */
+
+static inline void _rs_rekey(u_char *dat, size_t datlen);
+
+static inline void
+_rs_init(u_char *buf, size_t n)
+{
+ if (n < KEYSZ + IVSZ)
+ return;
+ chacha_keysetup(&rs, buf, KEYSZ * 8, 0);
+ chacha_ivsetup(&rs, buf + KEYSZ);
+}
+
+static void
+_rs_stir(void)
+{
+ u_char rnd[KEYSZ + IVSZ];
+
+ if (RAND_bytes(rnd, sizeof(rnd)) <= 0)
+ fatal("Couldn't obtain random bytes (error %ld)",
+ ERR_get_error());
+
+ if (!rs_initialized) {
+ rs_initialized = 1;
+ _rs_init(rnd, sizeof(rnd));
+ } else
+ _rs_rekey(rnd, sizeof(rnd));
+ memset(rnd, 0, sizeof(rnd));
+
+ /* invalidate rs_buf */
+ rs_have = 0;
+ memset(rs_buf, 0, RSBUFSZ);
+
+ rs_count = 1600000;
+}
+
+static inline void
+_rs_stir_if_needed(size_t len)
+{
+ pid_t pid = getpid();
+
+ if (rs_count <= len || !rs_initialized || rs_stir_pid != pid) {
+ rs_stir_pid = pid;
+ _rs_stir();
+ } else
+ rs_count -= len;
+}
+
+static inline void
+_rs_rekey(u_char *dat, size_t datlen)
+{
+#ifndef KEYSTREAM_ONLY
+ memset(rs_buf, 0,RSBUFSZ);
+#endif
+ /* fill rs_buf with the keystream */
+ chacha_encrypt_bytes(&rs, rs_buf, rs_buf, RSBUFSZ);
+ /* mix in optional user provided data */
+ if (dat) {
+ size_t i, m;
+
+ m = MIN(datlen, KEYSZ + IVSZ);
+ for (i = 0; i < m; i++)
+ rs_buf[i] ^= dat[i];
+ }
+ /* immediately reinit for backtracking resistance */
+ _rs_init(rs_buf, KEYSZ + IVSZ);
+ memset(rs_buf, 0, KEYSZ + IVSZ);
+ rs_have = RSBUFSZ - KEYSZ - IVSZ;
+}
+
+static inline void
+_rs_random_buf(void *_buf, size_t n)
+{
+ u_char *buf = (u_char *)_buf;
+ size_t m;
+
+ _rs_stir_if_needed(n);
+ while (n > 0) {
+ if (rs_have > 0) {
+ m = MIN(n, rs_have);
+ memcpy(buf, rs_buf + RSBUFSZ - rs_have, m);
+ memset(rs_buf + RSBUFSZ - rs_have, 0, m);
+ buf += m;
+ n -= m;
+ rs_have -= m;
+ }
+ if (rs_have == 0)
+ _rs_rekey(NULL, 0);
+ }
+}
+
+static inline void
+_rs_random_u32(uint32_t *val)
+{
+ _rs_stir_if_needed(sizeof(*val));
+ if (rs_have < sizeof(*val))
+ _rs_rekey(NULL, 0);
+ memcpy(val, rs_buf + RSBUFSZ - rs_have, sizeof(*val));
+ memset(rs_buf + RSBUFSZ - rs_have, 0, sizeof(*val));
+ rs_have -= sizeof(*val);
+ return;
+}
+
+void
+arc4random_stir(void)
+{
+ _ARC4_LOCK();
+ _rs_stir();
+ _ARC4_UNLOCK();
+}
+
+void
+arc4random_addrandom(u_char *dat, int datlen)
+{
+ int m;
+
+ _ARC4_LOCK();
+ if (!rs_initialized)
+ _rs_stir();
+ while (datlen > 0) {
+ m = MIN(datlen, KEYSZ + IVSZ);
+ _rs_rekey(dat, m);
+ dat += m;
+ datlen -= m;
+ }
+ _ARC4_UNLOCK();
+}
+
+uint32_t
+arc4random(void)
+{
+ uint32_t val;
+
+ _ARC4_LOCK();
+ _rs_random_u32(&val);
+ _ARC4_UNLOCK();
+ return val;
+}
+
+/*
+ * If we are providing arc4random, then we can provide a more efficient
+ * arc4random_buf().
+ */
+void
+arc4random_buf(void *buf, size_t n)
+{
+ _ARC4_LOCK();
+ _rs_random_buf(buf, n);
+ _ARC4_UNLOCK();
+}
+
+/*
+ * Calculate a uniformly distributed random number less than upper_bound
+ * avoiding "modulo bias".
+ *
+ * Uniformity is achieved by generating new random numbers until the one
+ * returned is outside the range [0, 2**32 % upper_bound). This
+ * guarantees the selected random number will be inside
+ * [2**32 % upper_bound, 2**32) which maps back to [0, upper_bound)
+ * after reduction modulo upper_bound.
+ */
+uint32_t
+arc4random_uniform(uint32_t upper_bound)
+{
+ uint32_t r, min;
+
+ if (upper_bound < 2)
+ return 0;
+
+ /* 2**32 % x == (2**32 - x) % x */
+ min = -upper_bound % upper_bound;
+
+ /*
+ * This could theoretically loop forever but each retry has
+ * p > 0.5 (worst case, usually far better) of selecting a
+ * number inside the range we need, so it should rarely need
+ * to re-roll.
+ */
+ for (;;) {
+ r = arc4random();
+ if (r >= min)
+ break;
+ }
+
+ return r % upper_bound;
+}
diff --git a/openbsd-compat/base64.c b/openbsd-compat/base64.c
new file mode 100644
index 00000000..a3c5782b
--- /dev/null
+++ b/openbsd-compat/base64.c
@@ -0,0 +1,306 @@
+/* $OpenBSD: base64.c,v 1.5 2006/10/21 09:55:03 otto Exp $ */
+
+/*
+ * Copyright (c) 1996 by 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.
+ */
+
+/*
+ * Portions Copyright (c) 1995 by International Business Machines, Inc.
+ *
+ * International Business Machines, Inc. (hereinafter called IBM) grants
+ * permission under its copyrights to use, copy, modify, and distribute this
+ * Software with or without fee, provided that the above copyright notice and
+ * all paragraphs of this notice appear in all copies, and that the name of IBM
+ * not be used in connection with the marketing of any product incorporating
+ * the Software or modifications thereof, without specific, written prior
+ * permission.
+ *
+ * To the extent it has a right to do so, IBM grants an immunity from suit
+ * under its patents, if any, for the use, sale or manufacture of products to
+ * the extent that such products are used for performing Domain Name System
+ * dynamic updates in TCP/IP networks by means of the Software. No immunity is
+ * granted for any product per se or for any other function of any product.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", AND IBM DISCLAIMS ALL WARRANTIES,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE. IN NO EVENT SHALL IBM BE LIABLE FOR ANY SPECIAL,
+ * DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE, EVEN
+ * IF IBM IS APPRISED OF THE POSSIBILITY OF SUCH DAMAGES.
+ */
+
+/* OPENBSD ORIGINAL: lib/libc/net/base64.c */
+
+#include "includes.h"
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <ctype.h>
+#include <stdio.h>
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "base64.h"
+
+static const char Base64[] =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+static const char Pad64 = '=';
+
+/* (From RFC1521 and draft-ietf-dnssec-secext-03.txt)
+ The following encoding technique is taken from RFC 1521 by Borenstein
+ and Freed. It is reproduced here in a slightly edited form for
+ convenience.
+
+ A 65-character subset of US-ASCII is used, enabling 6 bits to be
+ represented per printable character. (The extra 65th character, "=",
+ is used to signify a special processing function.)
+
+ The encoding process represents 24-bit groups of input bits as output
+ strings of 4 encoded characters. Proceeding from left to right, a
+ 24-bit input group is formed by concatenating 3 8-bit input groups.
+ These 24 bits are then treated as 4 concatenated 6-bit groups, each
+ of which is translated into a single digit in the base64 alphabet.
+
+ Each 6-bit group is used as an index into an array of 64 printable
+ characters. The character referenced by the index is placed in the
+ output string.
+
+ Table 1: The Base64 Alphabet
+
+ Value Encoding Value Encoding Value Encoding Value Encoding
+ 0 A 17 R 34 i 51 z
+ 1 B 18 S 35 j 52 0
+ 2 C 19 T 36 k 53 1
+ 3 D 20 U 37 l 54 2
+ 4 E 21 V 38 m 55 3
+ 5 F 22 W 39 n 56 4
+ 6 G 23 X 40 o 57 5
+ 7 H 24 Y 41 p 58 6
+ 8 I 25 Z 42 q 59 7
+ 9 J 26 a 43 r 60 8
+ 10 K 27 b 44 s 61 9
+ 11 L 28 c 45 t 62 +
+ 12 M 29 d 46 u 63 /
+ 13 N 30 e 47 v
+ 14 O 31 f 48 w (pad) =
+ 15 P 32 g 49 x
+ 16 Q 33 h 50 y
+
+ Special processing is performed if fewer than 24 bits are available
+ at the end of the data being encoded. A full encoding quantum is
+ always completed at the end of a quantity. When fewer than 24 input
+ bits are available in an input group, zero bits are added (on the
+ right) to form an integral number of 6-bit groups. Padding at the
+ end of the data is performed using the '=' character.
+
+ Since all base64 input is an integral number of octets, only the
+ -------------------------------------------------
+ following cases can arise:
+
+ (1) the final quantum of encoding input is an integral
+ multiple of 24 bits; here, the final unit of encoded
+ output will be an integral multiple of 4 characters
+ with no "=" padding,
+ (2) the final quantum of encoding input is exactly 8 bits;
+ here, the final unit of encoded output will be two
+ characters followed by two "=" padding characters, or
+ (3) the final quantum of encoding input is exactly 16 bits;
+ here, the final unit of encoded output will be three
+ characters followed by one "=" padding character.
+ */
+
+int
+b64_ntop(u_char const *src, size_t srclength, char *target, size_t targsize)
+{
+ size_t datalength = 0;
+ u_char input[3];
+ u_char output[4];
+ u_int i;
+
+ while (2 < srclength) {
+ input[0] = *src++;
+ input[1] = *src++;
+ input[2] = *src++;
+ srclength -= 3;
+
+ output[0] = input[0] >> 2;
+ output[1] = ((input[0] & 0x03) << 4) + (input[1] >> 4);
+ output[2] = ((input[1] & 0x0f) << 2) + (input[2] >> 6);
+ output[3] = input[2] & 0x3f;
+
+ if (datalength + 4 > targsize)
+ return (-1);
+ target[datalength++] = Base64[output[0]];
+ target[datalength++] = Base64[output[1]];
+ target[datalength++] = Base64[output[2]];
+ target[datalength++] = Base64[output[3]];
+ }
+
+ /* Now we worry about padding. */
+ if (0 != srclength) {
+ /* Get what's left. */
+ input[0] = input[1] = input[2] = '\0';
+ for (i = 0; i < srclength; i++)
+ input[i] = *src++;
+
+ output[0] = input[0] >> 2;
+ output[1] = ((input[0] & 0x03) << 4) + (input[1] >> 4);
+ output[2] = ((input[1] & 0x0f) << 2) + (input[2] >> 6);
+
+ if (datalength + 4 > targsize)
+ return (-1);
+ target[datalength++] = Base64[output[0]];
+ target[datalength++] = Base64[output[1]];
+ if (srclength == 1)
+ target[datalength++] = Pad64;
+ else
+ target[datalength++] = Base64[output[2]];
+ target[datalength++] = Pad64;
+ }
+ if (datalength >= targsize)
+ return (-1);
+ target[datalength] = '\0'; /* Returned value doesn't count \0. */
+ return (datalength);
+}
+
+/* skips all whitespace anywhere.
+ converts characters, four at a time, starting at (or after)
+ src from base - 64 numbers into three 8 bit bytes in the target area.
+ it returns the number of data bytes stored at the target, or -1 on error.
+ */
+
+int
+b64_pton(char const *src, u_char *target, size_t targsize)
+{
+ u_int tarindex, state;
+ int ch;
+ char *pos;
+
+ state = 0;
+ tarindex = 0;
+
+ while ((ch = *src++) != '\0') {
+ if (isspace(ch)) /* Skip whitespace anywhere. */
+ continue;
+
+ if (ch == Pad64)
+ break;
+
+ pos = strchr(Base64, ch);
+ if (pos == 0) /* A non-base64 character. */
+ return (-1);
+
+ switch (state) {
+ case 0:
+ if (target) {
+ if (tarindex >= targsize)
+ return (-1);
+ target[tarindex] = (pos - Base64) << 2;
+ }
+ state = 1;
+ break;
+ case 1:
+ if (target) {
+ if (tarindex + 1 >= targsize)
+ return (-1);
+ target[tarindex] |= (pos - Base64) >> 4;
+ target[tarindex+1] = ((pos - Base64) & 0x0f)
+ << 4 ;
+ }
+ tarindex++;
+ state = 2;
+ break;
+ case 2:
+ if (target) {
+ if (tarindex + 1 >= targsize)
+ return (-1);
+ target[tarindex] |= (pos - Base64) >> 2;
+ target[tarindex+1] = ((pos - Base64) & 0x03)
+ << 6;
+ }
+ tarindex++;
+ state = 3;
+ break;
+ case 3:
+ if (target) {
+ if (tarindex >= targsize)
+ return (-1);
+ target[tarindex] |= (pos - Base64);
+ }
+ tarindex++;
+ state = 0;
+ break;
+ }
+ }
+
+ /*
+ * We are done decoding Base-64 chars. Let's see if we ended
+ * on a byte boundary, and/or with erroneous trailing characters.
+ */
+
+ if (ch == Pad64) { /* We got a pad char. */
+ ch = *src++; /* Skip it, get next. */
+ switch (state) {
+ case 0: /* Invalid = in first position */
+ case 1: /* Invalid = in second position */
+ return (-1);
+
+ case 2: /* Valid, means one byte of info */
+ /* Skip any number of spaces. */
+ for (; ch != '\0'; ch = *src++)
+ if (!isspace(ch))
+ break;
+ /* Make sure there is another trailing = sign. */
+ if (ch != Pad64)
+ return (-1);
+ ch = *src++; /* Skip the = */
+ /* Fall through to "single trailing =" case. */
+ /* FALLTHROUGH */
+
+ case 3: /* Valid, means two bytes of info */
+ /*
+ * We know this char is an =. Is there anything but
+ * whitespace after it?
+ */
+ for (; ch != '\0'; ch = *src++)
+ if (!isspace(ch))
+ return (-1);
+
+ /*
+ * Now make sure for cases 2 and 3 that the "extra"
+ * bits that slopped past the last full byte were
+ * zeros. If we don't check them, they become a
+ * subliminal channel.
+ */
+ if (target && target[tarindex] != 0)
+ return (-1);
+ }
+ } else {
+ /*
+ * We ended by seeing the end of the string. Make sure we
+ * have no partial bytes lying around.
+ */
+ if (state != 0)
+ return (-1);
+ }
+
+ return (tarindex);
+}
diff --git a/openbsd-compat/base64.h b/openbsd-compat/base64.h
new file mode 100644
index 00000000..732c6b3f
--- /dev/null
+++ b/openbsd-compat/base64.h
@@ -0,0 +1,65 @@
+/* $Id: base64.h,v 1.6 2003/08/29 16:59:52 mouring Exp $ */
+
+/*
+ * Copyright (c) 1996 by 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.
+ */
+
+/*
+ * Portions Copyright (c) 1995 by International Business Machines, Inc.
+ *
+ * International Business Machines, Inc. (hereinafter called IBM) grants
+ * permission under its copyrights to use, copy, modify, and distribute this
+ * Software with or without fee, provided that the above copyright notice and
+ * all paragraphs of this notice appear in all copies, and that the name of IBM
+ * not be used in connection with the marketing of any product incorporating
+ * the Software or modifications thereof, without specific, written prior
+ * permission.
+ *
+ * To the extent it has a right to do so, IBM grants an immunity from suit
+ * under its patents, if any, for the use, sale or manufacture of products to
+ * the extent that such products are used for performing Domain Name System
+ * dynamic updates in TCP/IP networks by means of the Software. No immunity is
+ * granted for any product per se or for any other function of any product.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", AND IBM DISCLAIMS ALL WARRANTIES,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE. IN NO EVENT SHALL IBM BE LIABLE FOR ANY SPECIAL,
+ * DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE, EVEN
+ * IF IBM IS APPRISED OF THE POSSIBILITY OF SUCH DAMAGES.
+ */
+
+#ifndef _BSD_BASE64_H
+#define _BSD_BASE64_H
+
+#include "includes.h"
+
+#ifndef HAVE___B64_NTOP
+# ifndef HAVE_B64_NTOP
+int b64_ntop(u_char const *src, size_t srclength, char *target,
+ size_t targsize);
+# endif /* !HAVE_B64_NTOP */
+# define __b64_ntop(a,b,c,d) b64_ntop(a,b,c,d)
+#endif /* HAVE___B64_NTOP */
+
+#ifndef HAVE___B64_PTON
+# ifndef HAVE_B64_PTON
+int b64_pton(char const *src, u_char *target, size_t targsize);
+# endif /* !HAVE_B64_PTON */
+# define __b64_pton(a,b,c) b64_pton(a,b,c)
+#endif /* HAVE___B64_PTON */
+
+#endif /* _BSD_BASE64_H */
diff --git a/openbsd-compat/basename.c b/openbsd-compat/basename.c
new file mode 100644
index 00000000..ffa5c898
--- /dev/null
+++ b/openbsd-compat/basename.c
@@ -0,0 +1,67 @@
+/* $OpenBSD: basename.c,v 1.14 2005/08/08 08:05:33 espie Exp $ */
+
+/*
+ * Copyright (c) 1997, 2004 Todd C. Miller <Todd.Miller@courtesan.com>
+ *
+ * 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.
+ */
+
+/* OPENBSD ORIGINAL: lib/libc/gen/basename.c */
+
+#include "includes.h"
+#ifndef HAVE_BASENAME
+#include <errno.h>
+#include <string.h>
+
+char *
+basename(const char *path)
+{
+ static char bname[MAXPATHLEN];
+ size_t len;
+ const char *endp, *startp;
+
+ /* Empty or NULL string gets treated as "." */
+ if (path == NULL || *path == '\0') {
+ bname[0] = '.';
+ bname[1] = '\0';
+ return (bname);
+ }
+
+ /* Strip any trailing slashes */
+ endp = path + strlen(path) - 1;
+ while (endp > path && *endp == '/')
+ endp--;
+
+ /* All slashes becomes "/" */
+ if (endp == path && *endp == '/') {
+ bname[0] = '/';
+ bname[1] = '\0';
+ return (bname);
+ }
+
+ /* Find the start of the base */
+ startp = endp;
+ while (startp > path && *(startp - 1) != '/')
+ startp--;
+
+ len = endp - startp + 1;
+ if (len >= sizeof(bname)) {
+ errno = ENAMETOOLONG;
+ return (NULL);
+ }
+ memcpy(bname, startp, len);
+ bname[len] = '\0';
+ return (bname);
+}
+
+#endif /* !defined(HAVE_BASENAME) */
diff --git a/openbsd-compat/bsd-err.c b/openbsd-compat/bsd-err.c
new file mode 100644
index 00000000..a7823fad
--- /dev/null
+++ b/openbsd-compat/bsd-err.c
@@ -0,0 +1,94 @@
+/*
+ * Copyright (c) 2015 Tim Rice <tim@multitalents.net>
+ *
+ * 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 author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * 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.
+ */
+
+#include "includes.h"
+
+#include <errno.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+extern char *__progname;
+
+void
+err(int r, const char *fmt, ...)
+{
+ va_list args;
+
+ va_start(args, fmt);
+ fprintf(stderr, "%s: ", __progname);
+ if (fmt != NULL) {
+ vfprintf(stderr, fmt, args);
+ fprintf(stderr, ": ");
+ }
+ fprintf(stderr, "%s\n", strerror(errno));
+ va_end(args);
+ exit(r);
+}
+
+void
+errx(int r, const char *fmt, ...)
+{
+ va_list args;
+
+ va_start(args, fmt);
+ fprintf(stderr, "%s: ", __progname);
+ if (fmt != NULL)
+ vfprintf(stderr, fmt, args);
+ fprintf(stderr, "\n");
+ va_end(args);
+ exit(r);
+}
+
+void
+warn(const char *fmt, ...)
+{
+ va_list args;
+
+ va_start(args, fmt);
+ fprintf(stderr, "%s: ", __progname);
+ if (fmt != NULL) {
+ vfprintf(stderr, fmt, args);
+ fprintf(stderr, ": ");
+ }
+ fprintf(stderr, "%s\n", strerror(errno));
+ va_end(args);
+}
+
+void
+warnx(const char *fmt, ...)
+{
+ va_list args;
+
+ va_start(args, fmt);
+ fprintf(stderr, "%s: ", __progname);
+ if (fmt != NULL)
+ vfprintf(stderr, fmt, args);
+ fprintf(stderr, "\n");
+ va_end(args);
+}
diff --git a/openbsd-compat/bsd-err.h b/openbsd-compat/bsd-err.h
new file mode 100644
index 00000000..f75d0eb4
--- /dev/null
+++ b/openbsd-compat/bsd-err.h
@@ -0,0 +1,29 @@
+/*
+ * Public domain
+ * err.h compatibility shim
+ */
+
+#ifndef HAVE_ERR_H
+
+#ifndef LIBCRYPTOCOMPAT_ERR_H
+#define LIBCRYPTOCOMPAT_ERR_H
+
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+
+#define err(exitcode, format, args...) \
+ errx(exitcode, format ": %s", ## args, strerror(errno))
+
+#define errx(exitcode, format, args...) \
+ do { warnx(format, ## args); exit(exitcode); } while (0)
+
+#define warn(format, args...) \
+ warnx(format ": %s", ## args, strerror(errno))
+
+#define warnx(format, args...) \
+ fprintf(stderr, format "\n", ## args)
+
+#endif
+
+#endif
diff --git a/openbsd-compat/bsd-misc.h b/openbsd-compat/bsd-misc.h
new file mode 100644
index 00000000..c638462d
--- /dev/null
+++ b/openbsd-compat/bsd-misc.h
@@ -0,0 +1,24 @@
+/* $Id: bsd-misc.h,v 1.25 2013/08/04 11:48:41 dtucker Exp $ */
+
+/*
+ * Copyright (c) 1999-2004 Damien Miller <djm@mindrot.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.
+ */
+
+#ifndef _BSD_MISC_H
+#define _BSD_MISC_H
+
+#include "includes.h"
+
+#endif /* _BSD_MISC_H */
diff --git a/openbsd-compat/bsd-vis.h b/openbsd-compat/bsd-vis.h
new file mode 100644
index 00000000..d1286c99
--- /dev/null
+++ b/openbsd-compat/bsd-vis.h
@@ -0,0 +1,95 @@
+/* $OpenBSD: vis.h,v 1.11 2005/08/09 19:38:31 millert Exp $ */
+/* $NetBSD: vis.h,v 1.4 1994/10/26 00:56:41 cgd 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.
+ *
+ * @(#)vis.h 5.9 (Berkeley) 4/3/91
+ */
+
+/* OPENBSD ORIGINAL: include/vis.h */
+
+#include "includes.h"
+#if !defined(HAVE_STRNVIS) || defined(BROKEN_STRNVIS)
+
+#ifndef _VIS_H_
+#define _VIS_H_
+
+#include <sys/types.h>
+#include <limits.h>
+
+/*
+ * to select alternate encoding format
+ */
+#define VIS_OCTAL 0x01 /* use octal \ddd format */
+#define VIS_CSTYLE 0x02 /* use \[nrft0..] where appropriate */
+
+/*
+ * to alter set of characters encoded (default is to encode all
+ * non-graphic except space, tab, and newline).
+ */
+#define VIS_SP 0x04 /* also encode space */
+#define VIS_TAB 0x08 /* also encode tab */
+#define VIS_NL 0x10 /* also encode newline */
+#define VIS_WHITE (VIS_SP | VIS_TAB | VIS_NL)
+#define VIS_SAFE 0x20 /* only encode "unsafe" characters */
+
+/*
+ * other
+ */
+#define VIS_NOSLASH 0x40 /* inhibit printing '\' */
+#define VIS_GLOB 0x100 /* encode glob(3) magics and '#' */
+
+/*
+ * unvis return codes
+ */
+#define UNVIS_VALID 1 /* character valid */
+#define UNVIS_VALIDPUSH 2 /* character valid, push back passed char */
+#define UNVIS_NOCHAR 3 /* valid sequence, no character produced */
+#define UNVIS_SYNBAD -1 /* unrecognized escape sequence */
+#define UNVIS_ERROR -2 /* decoder in unknown state (unrecoverable) */
+
+/*
+ * unvis flags
+ */
+#define UNVIS_END 1 /* no more characters */
+
+char *vis(char *, int, int, int);
+int strvis(char *, const char *, int);
+int strnvis(char *, const char *, size_t, int)
+ __attribute__ ((__bounded__(__string__,1,3)));
+int strvisx(char *, const char *, size_t, int)
+ __attribute__ ((__bounded__(__string__,1,3)));
+int strunvis(char *, const char *);
+int unvis(char *, char, int *, int);
+ssize_t strnunvis(char *, const char *, size_t)
+ __attribute__ ((__bounded__(__string__,1,3)));
+
+#endif /* !_VIS_H_ */
+
+#endif /* !HAVE_STRNVIS || BROKEN_STRNVIS */
diff --git a/openbsd-compat/bsd-waitpid.c b/openbsd-compat/bsd-waitpid.c
new file mode 100644
index 00000000..3ef68a53
--- /dev/null
+++ b/openbsd-compat/bsd-waitpid.c
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2000 Ben Lindstrom. 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.
+ */
+
+#include "includes.h"
+
+#include <errno.h>
+#include <sys/wait.h>
+#include "bsd-waitpid.h"
+
+pid_t
+waitpid(int pid, int *stat_loc, int options)
+{
+ union wait statusp;
+ pid_t wait_pid;
+
+ if (pid <= 0) {
+ if (pid != -1) {
+ errno = EINVAL;
+ return (-1);
+ }
+ /* wait4() wants pid=0 for indiscriminate wait. */
+ pid = 0;
+ }
+ wait_pid = wait4(pid, &statusp, options, NULL);
+ if (stat_loc)
+ *stat_loc = (int) statusp.w_status;
+
+ return (wait_pid);
+}
diff --git a/openbsd-compat/bsd-waitpid.h b/openbsd-compat/bsd-waitpid.h
new file mode 100644
index 00000000..2d853db6
--- /dev/null
+++ b/openbsd-compat/bsd-waitpid.h
@@ -0,0 +1,51 @@
+/* $Id: bsd-waitpid.h,v 1.5 2003/08/29 16:59:52 mouring Exp $ */
+
+/*
+ * Copyright (c) 2000 Ben Lindstrom. 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.
+ *
+ */
+
+#ifndef _BSD_WAITPID_H
+#define _BSD_WAITPID_H
+
+#ifndef HAVE_WAITPID
+/* Clean out any potental issues */
+#undef WIFEXITED
+#undef WIFSTOPPED
+#undef WIFSIGNALED
+
+/* Define required functions to mimic a POSIX look and feel */
+#define _W_INT(w) (*(int*)&(w)) /* convert union wait to int */
+#define WIFEXITED(w) (!((_W_INT(w)) & 0377))
+#define WIFSTOPPED(w) ((_W_INT(w)) & 0100)
+#define WIFSIGNALED(w) (!WIFEXITED(w) && !WIFSTOPPED(w))
+#define WEXITSTATUS(w) (int)(WIFEXITED(w) ? ((_W_INT(w) >> 8) & 0377) : -1)
+#define WTERMSIG(w) (int)(WIFSIGNALED(w) ? (_W_INT(w) & 0177) : -1)
+#define WCOREFLAG 0x80
+#define WCOREDUMP(w) ((_W_INT(w)) & WCOREFLAG)
+
+/* Prototype */
+pid_t waitpid(int, int *, int);
+
+#endif /* !HAVE_WAITPID */
+#endif /* _BSD_WAITPID_H */
diff --git a/openbsd-compat/chacha_private.h b/openbsd-compat/chacha_private.h
new file mode 100644
index 00000000..46613646
--- /dev/null
+++ b/openbsd-compat/chacha_private.h
@@ -0,0 +1,224 @@
+/*
+chacha-merged.c version 20080118
+D. J. Bernstein
+Public domain.
+*/
+
+/* $OpenBSD: chacha_private.h,v 1.2 2013/10/04 07:02:27 djm Exp $ */
+
+#include <sys/types.h>
+
+typedef unsigned char u8;
+typedef unsigned int u32;
+
+typedef struct
+{
+ u32 input[16]; /* could be compressed */
+} chacha_ctx;
+
+#define U8C(v) (v##U)
+#define U32C(v) (v##U)
+
+#define U8V(v) ((u8)(v) & U8C(0xFF))
+#define U32V(v) ((u32)(v) & U32C(0xFFFFFFFF))
+
+#define ROTL32(v, n) \
+ (U32V((v) << (n)) | ((v) >> (32 - (n))))
+
+#define U8TO32_LITTLE(p) \
+ (((u32)((p)[0]) ) | \
+ ((u32)((p)[1]) << 8) | \
+ ((u32)((p)[2]) << 16) | \
+ ((u32)((p)[3]) << 24))
+
+#define U32TO8_LITTLE(p, v) \
+ do { \
+ (p)[0] = U8V((v) ); \
+ (p)[1] = U8V((v) >> 8); \
+ (p)[2] = U8V((v) >> 16); \
+ (p)[3] = U8V((v) >> 24); \
+ } while (0)
+
+#define ROTATE(v,c) (ROTL32(v,c))
+#define XOR(v,w) ((v) ^ (w))
+#define PLUS(v,w) (U32V((v) + (w)))
+#define PLUSONE(v) (PLUS((v),1))
+
+#define QUARTERROUND(a,b,c,d) \
+ a = PLUS(a,b); d = ROTATE(XOR(d,a),16); \
+ c = PLUS(c,d); b = ROTATE(XOR(b,c),12); \
+ a = PLUS(a,b); d = ROTATE(XOR(d,a), 8); \
+ c = PLUS(c,d); b = ROTATE(XOR(b,c), 7);
+
+static const char sigma[16] = "expand 32-byte k";
+static const char tau[16] = "expand 16-byte k";
+
+static void
+chacha_keysetup(chacha_ctx *x,const u8 *k,u32 kbits,u32 ivbits)
+{
+ const char *constants;
+
+ x->input[4] = U8TO32_LITTLE(k + 0);
+ x->input[5] = U8TO32_LITTLE(k + 4);
+ x->input[6] = U8TO32_LITTLE(k + 8);
+ x->input[7] = U8TO32_LITTLE(k + 12);
+ if (kbits == 256) { /* recommended */
+ k += 16;
+ constants = sigma;
+ } else { /* kbits == 128 */
+ constants = tau;
+ }
+ x->input[8] = U8TO32_LITTLE(k + 0);
+ x->input[9] = U8TO32_LITTLE(k + 4);
+ x->input[10] = U8TO32_LITTLE(k + 8);
+ x->input[11] = U8TO32_LITTLE(k + 12);
+ x->input[0] = U8TO32_LITTLE(constants + 0);
+ x->input[1] = U8TO32_LITTLE(constants + 4);
+ x->input[2] = U8TO32_LITTLE(constants + 8);
+ x->input[3] = U8TO32_LITTLE(constants + 12);
+}
+
+static void
+chacha_ivsetup(chacha_ctx *x,const u8 *iv)
+{
+ x->input[12] = 0;
+ x->input[13] = 0;
+ x->input[14] = U8TO32_LITTLE(iv + 0);
+ x->input[15] = U8TO32_LITTLE(iv + 4);
+}
+
+static void
+chacha_encrypt_bytes(chacha_ctx *x,const u8 *m,u8 *c,u32 bytes)
+{
+ u32 x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15;
+ u32 j0, j1, j2, j3, j4, j5, j6, j7, j8, j9, j10, j11, j12, j13, j14, j15;
+ u8 *ctarget = NULL;
+ u8 tmp[64];
+ u_int i;
+
+ if (!bytes) return;
+
+ j0 = x->input[0];
+ j1 = x->input[1];
+ j2 = x->input[2];
+ j3 = x->input[3];
+ j4 = x->input[4];
+ j5 = x->input[5];
+ j6 = x->input[6];
+ j7 = x->input[7];
+ j8 = x->input[8];
+ j9 = x->input[9];
+ j10 = x->input[10];
+ j11 = x->input[11];
+ j12 = x->input[12];
+ j13 = x->input[13];
+ j14 = x->input[14];
+ j15 = x->input[15];
+
+ for (;;) {
+ if (bytes < 64) {
+ for (i = 0;i < bytes;++i) tmp[i] = m[i];
+ m = tmp;
+ ctarget = c;
+ c = tmp;
+ }
+ x0 = j0;
+ x1 = j1;
+ x2 = j2;
+ x3 = j3;
+ x4 = j4;
+ x5 = j5;
+ x6 = j6;
+ x7 = j7;
+ x8 = j8;
+ x9 = j9;
+ x10 = j10;
+ x11 = j11;
+ x12 = j12;
+ x13 = j13;
+ x14 = j14;
+ x15 = j15;
+ for (i = 20;i > 0;i -= 2) {
+ QUARTERROUND( x0, x4, x8,x12)
+ QUARTERROUND( x1, x5, x9,x13)
+ QUARTERROUND( x2, x6,x10,x14)
+ QUARTERROUND( x3, x7,x11,x15)
+ QUARTERROUND( x0, x5,x10,x15)
+ QUARTERROUND( x1, x6,x11,x12)
+ QUARTERROUND( x2, x7, x8,x13)
+ QUARTERROUND( x3, x4, x9,x14)
+ }
+ x0 = PLUS(x0,j0);
+ x1 = PLUS(x1,j1);
+ x2 = PLUS(x2,j2);
+ x3 = PLUS(x3,j3);
+ x4 = PLUS(x4,j4);
+ x5 = PLUS(x5,j5);
+ x6 = PLUS(x6,j6);
+ x7 = PLUS(x7,j7);
+ x8 = PLUS(x8,j8);
+ x9 = PLUS(x9,j9);
+ x10 = PLUS(x10,j10);
+ x11 = PLUS(x11,j11);
+ x12 = PLUS(x12,j12);
+ x13 = PLUS(x13,j13);
+ x14 = PLUS(x14,j14);
+ x15 = PLUS(x15,j15);
+
+#ifndef KEYSTREAM_ONLY
+ x0 = XOR(x0,U8TO32_LITTLE(m + 0));
+ x1 = XOR(x1,U8TO32_LITTLE(m + 4));
+ x2 = XOR(x2,U8TO32_LITTLE(m + 8));
+ x3 = XOR(x3,U8TO32_LITTLE(m + 12));
+ x4 = XOR(x4,U8TO32_LITTLE(m + 16));
+ x5 = XOR(x5,U8TO32_LITTLE(m + 20));
+ x6 = XOR(x6,U8TO32_LITTLE(m + 24));
+ x7 = XOR(x7,U8TO32_LITTLE(m + 28));
+ x8 = XOR(x8,U8TO32_LITTLE(m + 32));
+ x9 = XOR(x9,U8TO32_LITTLE(m + 36));
+ x10 = XOR(x10,U8TO32_LITTLE(m + 40));
+ x11 = XOR(x11,U8TO32_LITTLE(m + 44));
+ x12 = XOR(x12,U8TO32_LITTLE(m + 48));
+ x13 = XOR(x13,U8TO32_LITTLE(m + 52));
+ x14 = XOR(x14,U8TO32_LITTLE(m + 56));
+ x15 = XOR(x15,U8TO32_LITTLE(m + 60));
+#endif
+
+ j12 = PLUSONE(j12);
+ if (!j12) {
+ j13 = PLUSONE(j13);
+ /* stopping at 2^70 bytes per nonce is user's responsibility */
+ }
+
+ U32TO8_LITTLE(c + 0,x0);
+ U32TO8_LITTLE(c + 4,x1);
+ U32TO8_LITTLE(c + 8,x2);
+ U32TO8_LITTLE(c + 12,x3);
+ U32TO8_LITTLE(c + 16,x4);
+ U32TO8_LITTLE(c + 20,x5);
+ U32TO8_LITTLE(c + 24,x6);
+ U32TO8_LITTLE(c + 28,x7);
+ U32TO8_LITTLE(c + 32,x8);
+ U32TO8_LITTLE(c + 36,x9);
+ U32TO8_LITTLE(c + 40,x10);
+ U32TO8_LITTLE(c + 44,x11);
+ U32TO8_LITTLE(c + 48,x12);
+ U32TO8_LITTLE(c + 52,x13);
+ U32TO8_LITTLE(c + 56,x14);
+ U32TO8_LITTLE(c + 60,x15);
+
+ if (bytes <= 64) {
+ if (bytes < 64) {
+ for (i = 0;i < bytes;++i) ctarget[i] = c[i];
+ }
+ x->input[12] = j12;
+ x->input[13] = j13;
+ return;
+ }
+ bytes -= 64;
+ c += 64;
+#ifndef KEYSTREAM_ONLY
+ m += 64;
+#endif
+ }
+}
diff --git a/openbsd-compat/clock_gettime.c b/openbsd-compat/clock_gettime.c
new file mode 100644
index 00000000..6c1ef0d4
--- /dev/null
+++ b/openbsd-compat/clock_gettime.c
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2012 Charles Longeau <chl@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"
+
+#ifdef HAVE_MACH_MACH_TIME_H
+#include <mach/mach_time.h>
+#endif
+#include <sys/time.h>
+#include <time.h>
+
+#if !defined(HAVE_CLOCK_GETTIME)
+int
+clock_gettime(int clock_id, struct timespec *tp)
+{
+ int ret = 0;
+ uint64_t time;
+ mach_timebase_info_data_t info;
+ static double scaling_factor = 0;
+
+#if 0
+ struct timeval tv;
+
+ ret = gettimeofday(&tv, NULL);
+ TIMEVAL_TO_TIMESPEC(&tv, tp);
+#endif
+
+/* based on http://code-factor.blogspot.fr/2009/11/monotonic-timers.html */
+
+ time = mach_absolute_time();
+
+ if (scaling_factor == 0) {
+ ret = (int) mach_timebase_info(&info);
+ if (ret != 0)
+ fatal("mach_timebase_info failed");
+ scaling_factor = info.numer/info.denom;
+ }
+
+ time *= scaling_factor;
+
+ tp->tv_sec = time / 1000000000;
+ tp->tv_nsec = time % 1000000000;
+
+ return (ret);
+}
+#endif
diff --git a/openbsd-compat/closefrom.c b/openbsd-compat/closefrom.c
new file mode 100644
index 00000000..528949a6
--- /dev/null
+++ b/openbsd-compat/closefrom.c
@@ -0,0 +1,107 @@
+/*
+ * Copyright (c) 2004-2005 Todd C. Miller <Todd.Miller@courtesan.com>
+ *
+ * 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/param.h>
+#include <unistd.h>
+#include <stdio.h>
+#ifdef HAVE_FCNTL_H
+# include <fcntl.h>
+#endif
+#include <limits.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <string.h>
+#include <unistd.h>
+#ifdef HAVE_DIRENT_H
+# include <dirent.h>
+# define NAMLEN(dirent) strlen((dirent)->d_name)
+#else
+# define dirent direct
+# define NAMLEN(dirent) (dirent)->d_namlen
+# ifdef HAVE_SYS_NDIR_H
+# include <sys/ndir.h>
+# endif
+# ifdef HAVE_SYS_DIR_H
+# include <sys/dir.h>
+# endif
+# ifdef HAVE_NDIR_H
+# include <ndir.h>
+# endif
+#endif
+
+#ifndef OPEN_MAX
+# define OPEN_MAX 256
+#endif
+
+#if 0
+__unused static const char rcsid[] = "$Sudo: closefrom.c,v 1.11 2006/08/17 15:26:54 millert Exp $";
+#endif /* lint */
+
+/*
+ * Close all file descriptors greater than or equal to lowfd.
+ */
+#ifdef HAVE_FCNTL_CLOSEM
+void
+closefrom(int lowfd)
+{
+ (void) fcntl(lowfd, F_CLOSEM, 0);
+}
+#else
+void
+closefrom(int lowfd)
+{
+ long fd, maxfd;
+#if defined(HAVE_DIRFD) && defined(HAVE_PROC_PID)
+ char fdpath[PATH_MAX], *endp;
+ struct dirent *dent;
+ DIR *dirp;
+ int len;
+
+ /* Check for a /proc/$$/fd directory. */
+ len = snprintf(fdpath, sizeof(fdpath), "/proc/%ld/fd", (long)getpid());
+ if (len > 0 && (size_t)len <= sizeof(fdpath) && (dirp = opendir(fdpath))) {
+ while ((dent = readdir(dirp)) != NULL) {
+ fd = strtol(dent->d_name, &endp, 10);
+ if (dent->d_name != endp && *endp == '\0' &&
+ fd >= 0 && fd < INT_MAX && fd >= lowfd && fd != dirfd(dirp))
+ (void) close((int) fd);
+ }
+ (void) closedir(dirp);
+ } else
+#endif
+ {
+ /*
+ * Fall back on sysconf() or getdtablesize(). We avoid checking
+ * resource limits since it is possible to open a file descriptor
+ * and then drop the rlimit such that it is below the open fd.
+ */
+#ifdef HAVE_SYSCONF
+ maxfd = sysconf(_SC_OPEN_MAX);
+#else
+ maxfd = getdtablesize();
+#endif /* HAVE_SYSCONF */
+ if (maxfd < 0)
+ maxfd = OPEN_MAX;
+
+ for (fd = lowfd; fd < maxfd; fd++)
+ (void) close((int) fd);
+ }
+}
+#endif /* !HAVE_FCNTL_CLOSEM */
+
diff --git a/openbsd-compat/crypt_checkpass.c b/openbsd-compat/crypt_checkpass.c
new file mode 100644
index 00000000..d10b3a57
--- /dev/null
+++ b/openbsd-compat/crypt_checkpass.c
@@ -0,0 +1,33 @@
+/* OPENBSD ORIGINAL: lib/libc/crypt/cryptutil.c */
+
+#include "includes.h"
+#include <errno.h>
+#ifdef HAVE_CRYPT_H
+#include <crypt.h>
+#endif
+#include <string.h>
+#include <unistd.h>
+
+int
+crypt_checkpass(const char *pass, const char *goodhash)
+{
+ char *c;
+
+ if (goodhash == NULL)
+ goto fail;
+
+ /* empty password */
+ if (strlen(goodhash) == 0 && strlen(pass) == 0)
+ return 0;
+
+ c = crypt(pass, goodhash);
+ if (c == NULL)
+ goto fail;
+
+ if (strcmp(c, goodhash) == 0)
+ return 0;
+
+fail:
+ errno = EACCES;
+ return -1;
+}
diff --git a/openbsd-compat/daemon.c b/openbsd-compat/daemon.c
new file mode 100644
index 00000000..3efe14c6
--- /dev/null
+++ b/openbsd-compat/daemon.c
@@ -0,0 +1,82 @@
+/* $OpenBSD: daemon.c,v 1.6 2005/08/08 08:05:33 espie Exp $ */
+/*-
+ * Copyright (c) 1990, 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.
+ */
+
+/* OPENBSD ORIGINAL: lib/libc/gen/daemon.c */
+
+#include "includes.h"
+
+#ifndef HAVE_DAEMON
+
+#include <sys/types.h>
+
+#ifdef HAVE_SYS_STAT_H
+# include <sys/stat.h>
+#endif
+
+#ifdef HAVE_FCNTL_H
+# include <fcntl.h>
+#endif
+
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+
+int
+daemon(int nochdir, int noclose)
+{
+ int fd;
+
+ switch (fork()) {
+ case -1:
+ return (-1);
+ case 0:
+ break;
+ default:
+ _exit(0);
+ }
+
+ if (setsid() == -1)
+ return (-1);
+
+ if (!nochdir)
+ (void)chdir("/");
+
+ if (!noclose && (fd = open(_PATH_DEVNULL, O_RDWR, 0)) != -1) {
+ (void)dup2(fd, STDIN_FILENO);
+ (void)dup2(fd, STDOUT_FILENO);
+ (void)dup2(fd, STDERR_FILENO);
+ if (fd > 2)
+ (void)close (fd);
+ }
+ return (0);
+}
+
+#endif /* !HAVE_DAEMON */
+
diff --git a/openbsd-compat/defines.h b/openbsd-compat/defines.h
new file mode 100644
index 00000000..a6e528eb
--- /dev/null
+++ b/openbsd-compat/defines.h
@@ -0,0 +1,510 @@
+/*
+ * Copyright (c) 2016 Gilles Chehade <gilles@poolp.org>. All rights reserved.
+ * Copyright (c) 1999-2003 Damien Miller. 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.
+ */
+
+#ifndef _DEFINES_H
+#define _DEFINES_H
+
+/* $Id: defines.h,v 1.181 2014/06/11 19:22:50 dtucker Exp $ */
+
+
+/* Constants */
+#ifndef EAUTH
+# 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
+# endif
+#endif
+
+#ifndef PATH_MAX
+# ifdef _POSIX_PATH_MAX
+# define PATH_MAX _POSIX_PATH_MAX
+# endif
+#endif
+
+#ifndef MAXPATHLEN
+# ifdef PATH_MAX
+# define MAXPATHLEN PATH_MAX
+# else /* PATH_MAX */
+# define MAXPATHLEN 64
+# define PATH_MAX 64
+/* realpath uses a fixed buffer of size MAXPATHLEN, so force use of ours */
+# ifndef BROKEN_REALPATH
+# define BROKEN_REALPATH 1
+# endif /* BROKEN_REALPATH */
+# endif /* PATH_MAX */
+#endif /* MAXPATHLEN */
+
+#ifndef MAXHOSTNAMELEN
+# define MAXHOSTNAMELEN 64
+#endif
+
+#ifndef LOGIN_NAME_MAX
+# define LOGIN_NAME_MAX 32
+#endif
+
+#ifndef MAXLOGNAME
+#define MAXLOGNAME LOGIN_NAME_MAX
+#endif
+
+#ifndef UID_MAX
+#define UID_MAX UINT_MAX
+#endif
+#ifndef GID_MAX
+#define GID_MAX UINT_MAX
+#endif
+
+#ifndef STDIN_FILENO
+# define STDIN_FILENO 0
+#endif
+#ifndef STDOUT_FILENO
+# define STDOUT_FILENO 1
+#endif
+#ifndef STDERR_FILENO
+# define STDERR_FILENO 2
+#endif
+
+#if !HAVE_DECL_O_NONBLOCK
+# 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 */
+
+#ifndef S_ISREG
+# define S_ISREG(mode) (((mode) & (_S_IFMT)) == (_S_IFREG))
+#endif /* S_ISREG */
+
+#ifndef S_ISLNK
+# define S_ISLNK(mode) (((mode) & S_IFMT) == S_IFLNK)
+#endif /* S_ISLNK */
+
+#ifndef S_IXUSR
+# define S_ISUID 0004000 /* set-uid */
+# define S_ISGID 0002000 /* set-gid */
+# define S_ISVTX 0001000 /* sticky */
+# define S_IXUSR 0000100 /* execute/search permission, */
+# define S_IXGRP 0000010 /* execute/search permission, */
+# define S_IXOTH 0000001 /* execute/search permission, */
+# define _S_IWUSR 0000200 /* write permission, */
+# define S_IWUSR _S_IWUSR /* write permission, owner */
+# define S_IWGRP 0000020 /* write permission, group */
+# define S_IWOTH 0000002 /* write permission, other */
+# define S_IRUSR 0000400 /* read permission, owner */
+# define S_IRGRP 0000040 /* read permission, group */
+# define S_IROTH 0000004 /* read permission, other */
+# define S_IRWXU 0000700 /* read, write, execute */
+# define S_IRWXG 0000070 /* read, write, execute */
+# define S_IRWXO 0000007 /* read, write, execute */
+#endif /* S_IXUSR */
+
+#if !defined(MAP_ANON) && defined(MAP_ANONYMOUS)
+#define MAP_ANON MAP_ANONYMOUS
+#endif
+
+#ifndef MAP_FAILED
+# define MAP_FAILED ((void *)-1)
+#endif
+
+/*
+SCO Open Server 3 has INADDR_LOOPBACK defined in rpc/rpc.h but
+including rpc/rpc.h breaks Solaris 6
+*/
+#ifndef INADDR_LOOPBACK
+#define INADDR_LOOPBACK ((u_long)0x7f000001)
+#endif
+
+
+/* Types */
+#ifndef HAVE_U_CHAR
+typedef unsigned char u_char;
+# define HAVE_U_CHAR
+#endif /* HAVE_U_CHAR */
+
+#ifndef HAVE_U_INT
+typedef unsigned int u_int;
+# define HAVE_U_INT
+#endif
+
+#ifndef HAVE_INTMAX_T
+typedef long long intmax_t;
+# define HAVE_INTMAX_T
+#endif
+
+#ifndef HAVE_UINTMAX_T
+typedef unsigned long long uintmax_t;
+# define HAVE_UINTMAX_T
+#endif
+
+#ifndef HAVE_SA_FAMILY_T
+typedef int sa_family_t;
+# define HAVE_SA_FAMILY_T
+#endif /* HAVE_SA_FAMILY_T */
+
+#ifndef HAVE_SIG_ATOMIC_T
+typedef int sig_atomic_t;
+# define HAVE_SIG_ATOMIC_T
+#endif /* HAVE_SIG_ATOMIC_T */
+
+
+#ifndef ULLONG_MAX
+# define ULLONG_MAX ((unsigned long long)-1)
+#endif
+
+#ifndef SIZE_T_MAX
+#define SIZE_T_MAX ULONG_MAX
+#endif /* SIZE_T_MAX */
+
+#ifndef SIZE_MAX
+#define SIZE_MAX SIZE_T_MAX
+#endif
+
+
+
+#if !defined(HAVE_SS_FAMILY_IN_SS) && defined(HAVE___SS_FAMILY_IN_SS)
+# define ss_family __ss_family
+#endif /* !defined(HAVE_SS_FAMILY_IN_SS) && defined(HAVE_SA_FAMILY_IN_SS) */
+
+#ifndef HAVE_SYS_UN_H
+struct sockaddr_un {
+ short sun_family; /* AF_UNIX */
+ char sun_path[108]; /* path name (gag) */
+};
+#endif /* HAVE_SYS_UN_H */
+
+#ifndef HAVE_IN_ADDR_T
+typedef uint32_t in_addr_t;
+#endif
+
+#ifndef HAVE_IN_PORT_T
+typedef uint16_t in_port_t;
+#endif
+
+
+/* Paths */
+
+/* needed by compat/daemon.c */
+#ifndef _PATH_DEVNULL
+# define _PATH_DEVNULL "/dev/null"
+#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) */
+
+#ifdef MAIL_DIRECTORY
+# define _PATH_MAILDIR MAIL_DIRECTORY
+#endif
+
+#ifdef MAILDIR
+# undef MAILDIR
+#endif
+
+
+
+/* Macros */
+
+/* needed by compat */
+#ifndef MAX
+# define MAX(a,b) (((a)>(b))?(a):(b))
+#endif
+#ifndef MIN
+# define MIN(a,b) (((a)<(b))?(a):(b))
+#endif
+
+/* needed by smtpd */
+#ifndef timespeccmp
+#define timespeccmp(a, b, cmp) \
+ (((a)->tv_sec == (b)->tv_sec) ? \
+ ((a)->tv_nsec cmp (b)->tv_nsec) : \
+ ((a)->tv_sec cmp (b)->tv_sec))
+#endif
+
+/* needed by smtpd */
+#ifndef timespecsub
+#define timespecsub(a, b, result) \
+ do { \
+ (result)->tv_sec = (a)->tv_sec - (b)->tv_sec; \
+ (result)->tv_nsec = (a)->tv_nsec - (b)->tv_nsec; \
+ if ((result)->tv_nsec < 0) { \
+ --(result)->tv_sec; \
+ (result)->tv_nsec += 1000000000L; \
+ } \
+ } while (0)
+#endif
+
+/* needed by smtpd */
+#ifndef TIMEVAL_TO_TIMESPEC
+#define TIMEVAL_TO_TIMESPEC(tv, ts) { \
+ (ts)->tv_sec = (tv)->tv_sec; \
+ (ts)->tv_nsec = (tv)->tv_usec * 1000; \
+}
+#endif
+
+/* needed by compat */
+#ifndef TIMESPEC_TO_TIMEVAL
+#define TIMESPEC_TO_TIMEVAL(tv, ts) { \
+ (tv)->tv_sec = (ts)->tv_sec; \
+ (tv)->tv_usec = (ts)->tv_nsec / 1000; \
+}
+#endif
+
+#ifndef __P
+# define __P(x) x
+#endif
+
+#if !defined(IN6_IS_ADDR_V4MAPPED)
+# define IN6_IS_ADDR_V4MAPPED(a) \
+ ((((uint32_t *) (a))[0] == 0) && (((uint32_t *) (a))[1] == 0) && \
+ (((uint32_t *) (a))[2] == htonl (0xffff)))
+#endif /* !defined(IN6_IS_ADDR_V4MAPPED) */
+
+#if !defined(__GNUC__) || (__GNUC__ < 2)
+# define __attribute__(x)
+#endif /* !defined(__GNUC__) || (__GNUC__ < 2) */
+
+#ifndef __dead
+# define __dead __attribute__((noreturn))
+#endif
+
+#if !defined(HAVE_ATTRIBUTE__SENTINEL__) && !defined(__sentinel__)
+# define __sentinel__
+#endif
+
+#if !defined(HAVE_ATTRIBUTE__BOUNDED__) && !defined(__bounded__)
+# define __bounded__(x, y, z)
+#endif
+
+#if !defined(HAVE_ATTRIBUTE__NONNULL__) && !defined(__nonnull__)
+# define __nonnull__(x)
+#endif
+
+#ifndef OSSH_ALIGNBYTES
+#define OSSH_ALIGNBYTES (sizeof(int) - 1)
+#endif
+#ifndef __CMSG_ALIGN
+#define __CMSG_ALIGN(p) (((u_int)(p) + OSSH_ALIGNBYTES) &~ OSSH_ALIGNBYTES)
+#endif
+
+/* Length of the contents of a control message of length len */
+#ifndef CMSG_LEN
+#define CMSG_LEN(len) (__CMSG_ALIGN(sizeof(struct cmsghdr)) + (len))
+#endif
+
+/* Length of the space taken up by a padded control message of length len */
+#ifndef CMSG_SPACE
+#define CMSG_SPACE(len) (__CMSG_ALIGN(sizeof(struct cmsghdr)) + __CMSG_ALIGN(len))
+#endif
+
+/* given pointer to struct cmsghdr, return pointer to data */
+#ifndef CMSG_DATA
+#define CMSG_DATA(cmsg) ((u_char *)(cmsg) + __CMSG_ALIGN(sizeof(struct cmsghdr)))
+#endif /* CMSG_DATA */
+
+/*
+ * RFC 2292 requires to check msg_controllen, in case that the kernel returns
+ * an empty list for some reasons.
+ */
+#ifndef CMSG_FIRSTHDR
+#define CMSG_FIRSTHDR(mhdr) \
+ ((mhdr)->msg_controllen >= sizeof(struct cmsghdr) ? \
+ (struct cmsghdr *)(mhdr)->msg_control : \
+ (struct cmsghdr *)NULL)
+#endif /* CMSG_FIRSTHDR */
+
+
+/* Set up BSD-style BYTE_ORDER definition if it isn't there already */
+/* XXX: doesn't try to cope with strange byte orders (PDP_ENDIAN) */
+#ifndef BYTE_ORDER
+# ifndef LITTLE_ENDIAN
+# define LITTLE_ENDIAN 1234
+# endif /* LITTLE_ENDIAN */
+# ifndef BIG_ENDIAN
+# define BIG_ENDIAN 4321
+# endif /* BIG_ENDIAN */
+# ifdef WORDS_BIGENDIAN
+# define BYTE_ORDER BIG_ENDIAN
+# else /* WORDS_BIGENDIAN */
+# define BYTE_ORDER LITTLE_ENDIAN
+# endif /* WORDS_BIGENDIAN */
+#endif /* BYTE_ORDER */
+
+/* Function replacement / compatibility hacks */
+
+#if defined(BROKEN_GETADDRINFO) && defined(HAVE_GETADDRINFO)
+# undef HAVE_GETADDRINFO
+#endif
+#if defined(BROKEN_GETADDRINFO) && defined(HAVE_FREEADDRINFO)
+# undef HAVE_FREEADDRINFO
+#endif
+#if defined(BROKEN_GETADDRINFO) && defined(HAVE_GAI_STRERROR)
+# undef HAVE_GAI_STRERROR
+#endif
+
+#if !defined(HAVE_MEMMOVE) && defined(HAVE_BCOPY)
+# define memmove(s1, s2, n) bcopy((s2), (s1), (n))
+#endif /* !defined(HAVE_MEMMOVE) && defined(HAVE_BCOPY) */
+
+#if !defined(HAVE___func__) && defined(HAVE___FUNCTION__)
+# define __func__ __FUNCTION__
+#elif !defined(HAVE___func__)
+# define __func__ ""
+#endif
+
+
+/* Maximum number of file descriptors available */
+/* needed by compat/bsd-closefrom.c */
+#ifndef OPEN_MAX
+# ifdef HAVE_SYSCONF
+# define OPEN_MAX sysconf(_SC_OPEN_MAX)
+# else
+# define OPEN_MAX 256
+# endif
+#endif
+
+
+
+/** end of login recorder definitions */
+
+#ifndef IOV_MAX
+# if defined(_XOPEN_IOV_MAX)
+# define IOV_MAX _XOPEN_IOV_MAX
+# elif defined(DEF_IOV_MAX)
+# define IOV_MAX DEF_IOV_MAX
+# else
+# define IOV_MAX 16
+# endif
+#endif
+
+#ifndef EWOULDBLOCK
+# define EWOULDBLOCK EAGAIN
+#endif
+
+#ifndef INET6_ADDRSTRLEN /* for non IPv6 machines */
+#define INET6_ADDRSTRLEN 46
+#endif
+
+#ifndef HAVE_VA_COPY
+# ifdef HAVE___VA_COPY
+# define va_copy(dest, src) __va_copy(dest, src)
+# else
+# define va_copy(dest, src) (dest) = (src)
+# endif
+#endif
+
+/* OpenSMTPD-portable specific entries */
+
+/* From OpenNTPD portable */
+#if !defined(SA_LEN)
+# if defined(HAVE_STRUCT_SOCKADDR_SA_LEN)
+# define SA_LEN(x) ((x)->sa_len)
+# else
+# define SA_LEN(x) ((x)->sa_family == AF_INET6 ? \
+ sizeof(struct sockaddr_in6) : \
+ sizeof(struct sockaddr_in))
+# endif
+#endif
+
+/* From OpenBGPD portable */
+#if !defined(SS_LEN)
+# if defined(HAVE_STRUCT_SOCKADDR_STORAGE_SS_LEN)
+# define SS_LEN(x) ((x)->ss_len)
+# else
+# define SS_LEN(x) SA_LEN((struct sockaddr *)(x))
+# endif
+#endif
+
+#ifdef HAVE_SS_LEN
+# define STORAGE_LEN(X) ((X).ss_len)
+# define SET_STORAGE_LEN(X, Y) do { STORAGE_LEN(X) = (Y); } while(0)
+#elif defined(HAVE___SS_LEN)
+# define STORAGE_LEN(X) ((X).__ss_len)
+# define SET_STORAGE_LEN(X, Y) do { STORAGE_LEN(X) = (Y); } while(0)
+#else
+# define STORAGE_LEN(X) (STORAGE_FAMILY(X) == AF_INET ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6))
+# define SET_STORAGE_LEN(X, Y) (void) 0
+#endif
+
+/* chl parts */
+#ifndef EAI_NODATA
+# ifdef EAI_NONAME
+# define EAI_NODATA EAI_NONAME
+# else
+# error "Neither EAI_NODATA and EAI_NONAME are defined! :("
+# endif
+#endif
+/* end of chl */
+
+#ifndef HAVE_FPARSELN
+/*
+ * fparseln() specific operation flags.
+ */
+#define FPARSELN_UNESCESC 0x01
+#define FPARSELN_UNESCCONT 0x02
+#define FPARSELN_UNESCCOMM 0x04
+#define FPARSELN_UNESCREST 0x08
+#define FPARSELN_UNESCALL 0x0f
+#endif
+
+#ifdef HAVE_M_DATA
+#undef M_DATA
+#endif
+
+#ifndef SCOPE_DELIMITER
+#define SCOPE_DELIMITER '%'
+#endif
+
+#ifndef HAVE_FLOCK
+#define LOCK_SH 0x01 /* shared file lock */
+#define LOCK_EX 0x02 /* exclusive file lock */
+#define LOCK_NB 0x04 /* don't block when locking */
+#define LOCK_UN 0x08 /* unlock file */
+#endif
+
+#if !HAVE_DECL_LOG_PERROR
+#define LOG_PERROR 0
+#endif
+
+#ifndef MAXDNAME
+#define MAXDNAME 1025
+#endif
+
+#endif /* _DEFINES_H */
diff --git a/openbsd-compat/dirname.c b/openbsd-compat/dirname.c
new file mode 100644
index 00000000..30fcb496
--- /dev/null
+++ b/openbsd-compat/dirname.c
@@ -0,0 +1,72 @@
+/* $OpenBSD: dirname.c,v 1.13 2005/08/08 08:05:33 espie Exp $ */
+
+/*
+ * Copyright (c) 1997, 2004 Todd C. Miller <Todd.Miller@courtesan.com>
+ *
+ * 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.
+ */
+
+/* OPENBSD ORIGINAL: lib/libc/gen/dirname.c */
+
+#include "includes.h"
+#ifndef HAVE_DIRNAME
+
+#include <errno.h>
+#include <string.h>
+#include <sys/param.h>
+
+char *
+dirname(const char *path)
+{
+ static char dname[MAXPATHLEN];
+ size_t len;
+ const char *endp;
+
+ /* Empty or NULL string gets treated as "." */
+ if (path == NULL || *path == '\0') {
+ dname[0] = '.';
+ dname[1] = '\0';
+ return (dname);
+ }
+
+ /* Strip any trailing slashes */
+ endp = path + strlen(path) - 1;
+ while (endp > path && *endp == '/')
+ endp--;
+
+ /* Find the start of the dir */
+ while (endp > path && *endp != '/')
+ endp--;
+
+ /* Either the dir is "/" or there are no slashes */
+ if (endp == path) {
+ dname[0] = *endp == '/' ? '/' : '.';
+ dname[1] = '\0';
+ return (dname);
+ } else {
+ /* Move forward past the separating slashes */
+ do {
+ endp--;
+ } while (endp > path && *endp == '/');
+ }
+
+ len = endp - path + 1;
+ if (len >= sizeof(dname)) {
+ errno = ENAMETOOLONG;
+ return (NULL);
+ }
+ memcpy(dname, path, len);
+ dname[len] = '\0';
+ return (dname);
+}
+#endif
diff --git a/openbsd-compat/empty.c b/openbsd-compat/empty.c
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/openbsd-compat/empty.c
diff --git a/openbsd-compat/entropy.c b/openbsd-compat/entropy.c
new file mode 100644
index 00000000..367d7135
--- /dev/null
+++ b/openbsd-compat/entropy.c
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2001 Damien Miller. 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.
+ */
+
+#include "includes.h"
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#ifdef HAVE_SYS_UN_H
+# include <sys/un.h>
+#endif
+
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <errno.h>
+#include <signal.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <openssl/rand.h>
+#include <openssl/crypto.h>
+#include <openssl/err.h>
+
+#include "smtpd/log.h"
+
+void
+seed_rng(void)
+{
+#ifndef LIBRESSL_VERSION
+ u_long mask;
+
+ /*
+ * OpenSSL version numbers: MNNFFPPS: major minor fix patch status
+ * We match major, minor, fix and status (not patch) for <1.0.0.
+ * After that, we acceptable compatible fix versions (so we
+ * allow 1.0.1 to work with 1.0.0). Going backwards is only allowed
+ * within a patch series.
+ */
+ mask = SSLeay() >= 0x1000000f ? 0xfff00000L : 0xfffff00fL;
+ if ((SSLeay() & mask) < (OPENSSL_VERSION_NUMBER & mask)) {
+ fatalx("OpenSSL version mismatch. Built against %lx, you have %lx\n",
+ (u_long)OPENSSL_VERSION_NUMBER, SSLeay());
+ }
+#endif
+
+ if (RAND_status() != 1)
+ fatal("PRNG is not seeded");
+}
diff --git a/openbsd-compat/entropy.h b/openbsd-compat/entropy.h
new file mode 100644
index 00000000..496bed66
--- /dev/null
+++ b/openbsd-compat/entropy.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 1999-2000 Damien Miller. 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.
+ */
+
+/* $Id: entropy.h,v 1.6 2011/09/09 01:29:41 dtucker Exp $ */
+
+#ifndef _RANDOMS_H
+#define _RANDOMS_H
+
+/* #include "buffer.h" */
+
+void seed_rng(void);
+
+/* void rexec_send_rng_seed(Buffer *); */
+/* void rexec_recv_rng_seed(Buffer *); */
+
+#endif /* _RANDOMS_H */
diff --git a/openbsd-compat/err_h/err.h b/openbsd-compat/err_h/err.h
new file mode 100644
index 00000000..92aa69f6
--- /dev/null
+++ b/openbsd-compat/err_h/err.h
@@ -0,0 +1,21 @@
+#ifndef ERR_H
+#define ERR_H
+
+#ifndef LIBCRYPTOCOMPAT_ERR_H
+#define LIBCRYPTOCOMPAT_ERR_H
+
+__attribute__ ((noreturn))
+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 *, ...);
+
+#endif
+
+#endif
diff --git a/openbsd-compat/errc.c b/openbsd-compat/errc.c
new file mode 100644
index 00000000..658a55b4
--- /dev/null
+++ b/openbsd-compat/errc.c
@@ -0,0 +1,56 @@
+/* $OpenBSD: basename.c,v 1.14 2005/08/08 08:05:33 espie Exp $ */
+
+/*
+ * Copyright (c) 1997, 2004 Todd C. Miller <Todd.Miller@courtesan.com>
+ *
+ * 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.
+ */
+
+/* OPENBSD ORIGINAL: lib/libc/gen/errc.c */
+
+#include "includes.h"
+
+#ifndef HAVE_ERRC
+
+#include <errno.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+extern char *__progname;
+
+__attribute__((noreturn))
+static void
+_verrc(int eval, int code, const char *fmt, va_list ap)
+{
+ (void)fprintf(stderr, "%s: ", __progname);
+ if (fmt != NULL) {
+ (void)vfprintf(stderr, fmt, ap);
+ (void)fprintf(stderr, ": ");
+ }
+ (void)fprintf(stderr, "%s\n", strerror(code));
+ exit(eval);
+}
+
+void
+errc(int eval, int code, const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ _verrc(eval, code, fmt, ap);
+ va_end(ap);
+}
+
+#endif
diff --git a/openbsd-compat/event_asr_run.c b/openbsd-compat/event_asr_run.c
new file mode 100644
index 00000000..aef86154
--- /dev/null
+++ b/openbsd-compat/event_asr_run.c
@@ -0,0 +1,88 @@
+/* $OpenBSD$ */
+/*
+ * 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 <netdb.h>
+
+#include <asr.h>
+#include <event.h>
+#include <stdlib.h>
+
+struct event_asr {
+ struct event ev;
+ struct asr_query *async;
+ void (*cb)(struct asr_result *, void *);
+ void *arg;
+};
+
+struct event_asr * event_asr_run(struct asr_query *,
+ void (*)(struct asr_result *, void *), void *);
+void event_asr_abort(struct event_asr *);
+
+static void
+event_asr_dispatch(int fd __attribute__((__unused__)),
+ short ev __attribute__((__unused__)), void *arg)
+{
+ struct event_asr *eva = arg;
+ struct asr_result ar;
+ struct timeval tv;
+
+ event_del(&eva->ev);
+
+ if (asr_run(eva->async, &ar)) {
+ eva->cb(&ar, eva->arg);
+ free(eva);
+ } else {
+ event_set(&eva->ev, ar.ar_fd,
+ ar.ar_cond == ASR_WANT_READ ? EV_READ : EV_WRITE,
+ event_asr_dispatch, eva);
+ tv.tv_sec = ar.ar_timeout / 1000;
+ tv.tv_usec = (ar.ar_timeout % 1000) * 1000;
+ event_add(&eva->ev, &tv);
+ }
+}
+
+struct event_asr *
+event_asr_run(struct asr_query *async, void (*cb)(struct asr_result *, void *),
+ void *arg)
+{
+ struct event_asr *eva;
+ struct timeval tv;
+
+ eva = calloc(1, sizeof *eva);
+ if (eva == NULL)
+ return (NULL);
+ eva->async = async;
+ eva->cb = cb;
+ eva->arg = arg;
+ tv.tv_sec = 0;
+ tv.tv_usec = 0;
+ evtimer_set(&eva->ev, event_asr_dispatch, eva);
+ evtimer_add(&eva->ev, &tv);
+ return (eva);
+}
+
+void
+event_asr_abort(struct event_asr *eva)
+{
+ asr_abort(eva->async);
+ event_del(&eva->ev);
+ free(eva);
+}
diff --git a/openbsd-compat/explicit_bzero.c b/openbsd-compat/explicit_bzero.c
new file mode 100644
index 00000000..d9f4abf5
--- /dev/null
+++ b/openbsd-compat/explicit_bzero.c
@@ -0,0 +1,15 @@
+/* $OpenBSD: explicit_bzero.c,v 1.4 2015/08/31 02:53:57 guenther Exp $ */
+/*
+ * Public domain.
+ * Written by Matthew Dempsky.
+ */
+
+#include "includes.h"
+
+#include <string.h>
+
+void
+explicit_bzero(void *buf, size_t len)
+{
+ memset(buf, 0, len);
+}
diff --git a/openbsd-compat/fgetln.c b/openbsd-compat/fgetln.c
new file mode 100644
index 00000000..1c51ff78
--- /dev/null
+++ b/openbsd-compat/fgetln.c
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2015 Joerg Jung <jung@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.
+ */
+
+/*
+ * portable fgetln() version, NOT reentrant
+ */
+
+#include "includes.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+
+void *reallocarray(void *, size_t, size_t);
+
+char *
+fgetln(FILE *fp, size_t *len)
+{
+ static char *buf = NULL;
+ static size_t bufsz = 0;
+ size_t r = 0;
+ char *p;
+ int c, e;
+
+ if (buf == NULL) {
+ if ((buf = calloc(1, BUFSIZ)) == NULL)
+ return NULL;
+ bufsz = BUFSIZ;
+ }
+
+ while ((c = getc(fp)) != EOF) {
+ buf[r++] = c;
+ if (r == bufsz) {
+ if (!(p = reallocarray(buf, 2, bufsz))) {
+ e = errno;
+ free(buf);
+ errno = e;
+ buf = NULL, bufsz = 0;
+ return NULL;
+ }
+ buf = p, bufsz = 2 * bufsz;
+ }
+ if (c == '\n')
+ break;
+ }
+ return (*len = r) ? buf : NULL;
+}
+
diff --git a/openbsd-compat/fmt_scaled.c b/openbsd-compat/fmt_scaled.c
new file mode 100644
index 00000000..edd682a4
--- /dev/null
+++ b/openbsd-compat/fmt_scaled.c
@@ -0,0 +1,274 @@
+/* $OpenBSD: fmt_scaled.c,v 1.9 2007/03/20 03:42:52 tedu Exp $ */
+
+/*
+ * Copyright (c) 2001, 2002, 2003 Ian F. Darwin. 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 author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * 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.
+ */
+
+/* OPENBSD ORIGINAL: lib/libutil/fmt_scaled.c */
+
+/*
+ * fmt_scaled: Format numbers scaled for human comprehension
+ * scan_scaled: Scan numbers in this format.
+ *
+ * "Human-readable" output uses 4 digits max, and puts a unit suffix at
+ * the end. Makes output compact and easy-to-read esp. on huge disks.
+ * Formatting code was originally in OpenBSD "df", converted to library routine.
+ * Scanning code written for OpenBSD libutil.
+ */
+
+#include "includes.h"
+
+#ifndef HAVE_FMT_SCALED
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <ctype.h>
+#include <limits.h>
+
+typedef enum {
+ NONE = 0, KILO = 1, MEGA = 2, GIGA = 3, TERA = 4, PETA = 5, EXA = 6
+} unit_type;
+
+/* These three arrays MUST be in sync! XXX make a struct */
+static unit_type units[] = { NONE, KILO, MEGA, GIGA, TERA, PETA, EXA };
+static char scale_chars[] = "BKMGTPE";
+static long long scale_factors[] = {
+ 1LL,
+ 1024LL,
+ 1024LL*1024,
+ 1024LL*1024*1024,
+ 1024LL*1024*1024*1024,
+ 1024LL*1024*1024*1024*1024,
+ 1024LL*1024*1024*1024*1024*1024,
+};
+#define SCALE_LENGTH (sizeof(units)/sizeof(units[0]))
+
+#define MAX_DIGITS (SCALE_LENGTH * 3) /* XXX strlen(sprintf("%lld", -1)? */
+
+/** Convert the given input string "scaled" into numeric in "result".
+ * Return 0 on success, -1 and errno set on error.
+ */
+int
+scan_scaled(char *scaled, long long *result)
+{
+ char *p = scaled;
+ int sign = 0;
+ unsigned int i, ndigits = 0, fract_digits = 0;
+ long long scale_fact = 1, whole = 0, fpart = 0;
+
+ /* Skip leading whitespace */
+ while (isascii(*p) && isspace(*p))
+ ++p;
+
+ /* Then at most one leading + or - */
+ while (*p == '-' || *p == '+') {
+ if (*p == '-') {
+ if (sign) {
+ errno = EINVAL;
+ return -1;
+ }
+ sign = -1;
+ ++p;
+ } else if (*p == '+') {
+ if (sign) {
+ errno = EINVAL;
+ return -1;
+ }
+ sign = +1;
+ ++p;
+ }
+ }
+
+ /* Main loop: Scan digits, find decimal point, if present.
+ * We don't allow exponentials, so no scientific notation
+ * (but note that E for Exa might look like e to some!).
+ * Advance 'p' to end, to get scale factor.
+ */
+ for (; isascii(*p) && (isdigit(*p) || *p=='.'); ++p) {
+ if (*p == '.') {
+ if (fract_digits > 0) { /* oops, more than one '.' */
+ errno = EINVAL;
+ return -1;
+ }
+ fract_digits = 1;
+ continue;
+ }
+
+ i = (*p) - '0'; /* whew! finally a digit we can use */
+ if (fract_digits > 0) {
+ if (fract_digits >= MAX_DIGITS-1)
+ /* ignore extra fractional digits */
+ continue;
+ fract_digits++; /* for later scaling */
+ fpart *= 10;
+ fpart += i;
+ } else { /* normal digit */
+ if (++ndigits >= MAX_DIGITS) {
+ errno = ERANGE;
+ return -1;
+ }
+ whole *= 10;
+ whole += i;
+ }
+ }
+
+ if (sign) {
+ whole *= sign;
+ fpart *= sign;
+ }
+
+ /* If no scale factor given, we're done. fraction is discarded. */
+ if (!*p) {
+ *result = whole;
+ return 0;
+ }
+
+ /* Validate scale factor, and scale whole and fraction by it. */
+ for (i = 0; i < SCALE_LENGTH; i++) {
+
+ /** Are we there yet? */
+ if (*p == scale_chars[i] ||
+ *p == tolower(scale_chars[i])) {
+
+ /* If it ends with alphanumerics after the scale char, bad. */
+ if (isalnum(*(p+1))) {
+ errno = EINVAL;
+ return -1;
+ }
+ scale_fact = scale_factors[i];
+
+ /* scale whole part */
+ whole *= scale_fact;
+
+ /* truncate fpart so it does't overflow.
+ * then scale fractional part.
+ */
+ while (fpart >= LLONG_MAX / scale_fact) {
+ fpart /= 10;
+ fract_digits--;
+ }
+ fpart *= scale_fact;
+ if (fract_digits > 0) {
+ for (i = 0; i < fract_digits -1; i++)
+ fpart /= 10;
+ }
+ whole += fpart;
+ *result = whole;
+ return 0;
+ }
+ }
+ errno = ERANGE;
+ return -1;
+}
+
+/* Format the given "number" into human-readable form in "result".
+ * Result must point to an allocated buffer of length FMT_SCALED_STRSIZE.
+ * Return 0 on success, -1 and errno set if error.
+ */
+int
+fmt_scaled(long long number, char *result)
+{
+ long long abval, fract = 0;
+ unsigned int i;
+ unit_type unit = NONE;
+
+ abval = (number < 0LL) ? -number : number; /* no long long_abs yet */
+
+ /* Not every negative long long has a positive representation.
+ * Also check for numbers that are just too darned big to format
+ */
+ if (abval < 0 || abval / 1024 >= scale_factors[SCALE_LENGTH-1]) {
+ errno = ERANGE;
+ return -1;
+ }
+
+ /* scale whole part; get unscaled fraction */
+ for (i = 0; i < SCALE_LENGTH; i++) {
+ if (abval/1024 < scale_factors[i]) {
+ unit = units[i];
+ fract = (i == 0) ? 0 : abval % scale_factors[i];
+ number /= scale_factors[i];
+ if (i > 0)
+ fract /= scale_factors[i - 1];
+ break;
+ }
+ }
+
+ fract = (10 * fract + 512) / 1024;
+ /* if the result would be >= 10, round main number */
+ if (fract == 10) {
+ if (number >= 0)
+ number++;
+ else
+ number--;
+ fract = 0;
+ }
+
+ if (number == 0)
+ strlcpy(result, "0B", FMT_SCALED_STRSIZE);
+ else if (unit == NONE || number >= 100 || number <= -100) {
+ if (fract >= 5) {
+ if (number >= 0)
+ number++;
+ else
+ number--;
+ }
+ (void)snprintf(result, FMT_SCALED_STRSIZE, "%lld%c",
+ number, scale_chars[unit]);
+ } else
+ (void)snprintf(result, FMT_SCALED_STRSIZE, "%lld.%1lld%c",
+ number, fract, scale_chars[unit]);
+
+ return 0;
+}
+
+#ifdef MAIN
+/*
+ * This is the original version of the program in the man page.
+ * Copy-and-paste whatever you need from it.
+ */
+int
+main(int argc, char **argv)
+{
+ char *cinput = "1.5K", buf[FMT_SCALED_STRSIZE];
+ long long ninput = 10483892, result;
+
+ if (scan_scaled(cinput, &result) == 0)
+ printf("\"%s\" -> %lld\n", cinput, result);
+ else
+ perror(cinput);
+
+ if (fmt_scaled(ninput, buf) == 0)
+ printf("%lld -> \"%s\"\n", ninput, buf);
+ else
+ fprintf(stderr, "%lld invalid (%s)\n", ninput, strerror(errno));
+
+ return 0;
+}
+#endif
+
+#endif /* HAVE_FMT_SCALED */
diff --git a/openbsd-compat/fparseln.c b/openbsd-compat/fparseln.c
new file mode 100644
index 00000000..dfa9093c
--- /dev/null
+++ b/openbsd-compat/fparseln.c
@@ -0,0 +1,179 @@
+/* $OpenBSD: fparseln.c,v 1.6 2005/08/02 21:46:23 espie Exp $ */
+/* $NetBSD: fparseln.c,v 1.7 1999/07/02 15:49:12 simonb Exp $ */
+
+/*
+ * Copyright (c) 1997 Christos Zoulas. 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. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Christos Zoulas.
+ * 4. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * 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.
+ */
+
+/* OPENBSD ORIGINAL: lib/libutil/fparseln.c */
+
+#include "includes.h"
+
+#ifdef HAVE_SYS_CDEFS
+#include <sys/cdefs.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+static int isescaped(const char *, const char *, int);
+
+/* isescaped():
+ * Return true if the character in *p that belongs to a string
+ * that starts in *sp, is escaped by the escape character esc.
+ */
+static int
+isescaped(const char *sp, const char *p, int esc)
+{
+ const char *cp;
+ size_t ne;
+
+ /* No escape character */
+ if (esc == '\0')
+ return 1;
+
+ /* Count the number of escape characters that precede ours */
+ for (ne = 0, cp = p; --cp >= sp && *cp == esc; ne++)
+ continue;
+
+ /* Return true if odd number of escape characters */
+ return (ne & 1) != 0;
+}
+
+
+/* fparseln():
+ * Read a line from a file parsing continuations ending in \
+ * and eliminating trailing newlines, or comments starting with
+ * the comment char.
+ */
+char *
+fparseln(FILE *fp, size_t *size, size_t *lineno, const char str[3],
+ int flags)
+{
+ static const char dstr[3] = { '\\', '\\', '#' };
+ char *buf = NULL, *ptr, *cp, esc, con, nl, com;
+ size_t s, len = 0;
+ int cnt = 1;
+
+ if (str == NULL)
+ str = dstr;
+
+ esc = str[0];
+ con = str[1];
+ com = str[2];
+
+ /*
+ * XXX: it would be cool to be able to specify the newline character,
+ * but unfortunately, fgetln does not let us
+ */
+ nl = '\n';
+
+ while (cnt) {
+ cnt = 0;
+
+ if (lineno)
+ (*lineno)++;
+
+ if ((ptr = fgetln(fp, &s)) == NULL)
+ break;
+
+ if (s && com) { /* Check and eliminate comments */
+ for (cp = ptr; cp < ptr + s; cp++)
+ if (*cp == com && !isescaped(ptr, cp, esc)) {
+ s = cp - ptr;
+ cnt = s == 0 && buf == NULL;
+ break;
+ }
+ }
+
+ if (s && nl) { /* Check and eliminate newlines */
+ cp = &ptr[s - 1];
+
+ if (*cp == nl)
+ s--; /* forget newline */
+ }
+
+ if (s && con) { /* Check and eliminate continuations */
+ cp = &ptr[s - 1];
+
+ if (*cp == con && !isescaped(ptr, cp, esc)) {
+ s--; /* forget escape */
+ cnt = 1;
+ }
+ }
+
+ if (s == 0 && buf != NULL)
+ continue;
+
+ if ((cp = realloc(buf, len + s + 1)) == NULL) {
+ free(buf);
+ return NULL;
+ }
+ buf = cp;
+
+ (void) memcpy(buf + len, ptr, s);
+ len += s;
+ buf[len] = '\0';
+ }
+
+ if ((flags & FPARSELN_UNESCALL) != 0 && esc && buf != NULL &&
+ strchr(buf, esc) != NULL) {
+ ptr = cp = buf;
+ while (cp[0] != '\0') {
+ int skipesc;
+
+ while (cp[0] != '\0' && cp[0] != esc)
+ *ptr++ = *cp++;
+ if (cp[0] == '\0' || cp[1] == '\0')
+ break;
+
+ skipesc = 0;
+ if (cp[1] == com)
+ skipesc += (flags & FPARSELN_UNESCCOMM);
+ if (cp[1] == con)
+ skipesc += (flags & FPARSELN_UNESCCONT);
+ if (cp[1] == esc)
+ skipesc += (flags & FPARSELN_UNESCESC);
+ if (cp[1] != com && cp[1] != con && cp[1] != esc)
+ skipesc = (flags & FPARSELN_UNESCREST);
+
+ if (skipesc)
+ cp++;
+ else
+ *ptr++ = *cp++;
+ *ptr++ = *cp++;
+ }
+ *ptr = '\0';
+ len = strlen(buf);
+ }
+
+ if (size)
+ *size = len;
+ return buf;
+}
diff --git a/openbsd-compat/freezero.c b/openbsd-compat/freezero.c
new file mode 100644
index 00000000..da20d132
--- /dev/null
+++ b/openbsd-compat/freezero.c
@@ -0,0 +1,34 @@
+/* $OpenBSD: strtonum.c,v 1.6 2004/08/03 19:38:01 millert Exp $ */
+
+/*
+ * Copyright (c) 2004 Ted Unangst and Todd Miller
+ * 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.
+ */
+
+/* OPENBSD ORIGINAL: lib/libc/stdlib/malloc.c */
+
+#include "includes.h"
+
+#include <stdlib.h>
+#include <strings.h>
+
+void
+freezero(void *ptr, size_t sz)
+{
+ if (ptr == NULL)
+ return;
+ explicit_bzero(ptr, sz);
+ free(ptr);
+}
diff --git a/openbsd-compat/getopt.c b/openbsd-compat/getopt.c
new file mode 100644
index 00000000..5450e43d
--- /dev/null
+++ b/openbsd-compat/getopt.c
@@ -0,0 +1,123 @@
+/*
+ * Copyright (c) 1987, 1993, 1994
+ * 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.
+ */
+
+/* OPENBSD ORIGINAL: lib/libc/stdlib/getopt.c */
+
+#include "includes.h"
+#if !defined(HAVE_GETOPT) || !defined(HAVE_GETOPT_OPTRESET)
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char *rcsid = "$OpenBSD: getopt.c,v 1.5 2003/06/02 20:18:37 millert Exp $";
+#endif /* LIBC_SCCS and not lint */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+int BSDopterr = 1, /* if error message should be printed */
+ BSDoptind = 1, /* index into parent argv vector */
+ BSDoptopt, /* character checked for validity */
+ BSDoptreset; /* reset getopt */
+char *BSDoptarg; /* argument associated with option */
+
+#define BADCH (int)'?'
+#define BADARG (int)':'
+#define EMSG ""
+
+/*
+ * getopt --
+ * Parse argc/argv argument vector.
+ */
+int
+BSDgetopt(nargc, nargv, ostr)
+ int nargc;
+ char * const *nargv;
+ const char *ostr;
+{
+ extern char *__progname;
+ static char *place = EMSG; /* option letter processing */
+ char *oli; /* option letter list index */
+
+ if (ostr == NULL)
+ return (-1);
+
+ if (BSDoptreset || !*place) { /* update scanning pointer */
+ BSDoptreset = 0;
+ if (BSDoptind >= nargc || *(place = nargv[BSDoptind]) != '-') {
+ place = EMSG;
+ return (-1);
+ }
+ if (place[1] && *++place == '-') { /* found "--" */
+ ++BSDoptind;
+ place = EMSG;
+ return (-1);
+ }
+ } /* option letter okay? */
+ if ((BSDoptopt = (int)*place++) == (int)':' ||
+ !(oli = strchr(ostr, BSDoptopt))) {
+ /*
+ * if the user didn't specify '-' as an option,
+ * assume it means -1.
+ */
+ if (BSDoptopt == (int)'-')
+ return (-1);
+ if (!*place)
+ ++BSDoptind;
+ if (BSDopterr && *ostr != ':')
+ (void)fprintf(stderr,
+ "%s: illegal option -- %c\n", __progname, BSDoptopt);
+ return (BADCH);
+ }
+ if (*++oli != ':') { /* don't need argument */
+ BSDoptarg = NULL;
+ if (!*place)
+ ++BSDoptind;
+ }
+ else { /* need an argument */
+ if (*place) /* no white space */
+ BSDoptarg = place;
+ else if (nargc <= ++BSDoptind) { /* no arg */
+ place = EMSG;
+ if (*ostr == ':')
+ return (BADARG);
+ if (BSDopterr)
+ (void)fprintf(stderr,
+ "%s: option requires an argument -- %c\n",
+ __progname, BSDoptopt);
+ return (BADCH);
+ }
+ else /* white space */
+ BSDoptarg = nargv[BSDoptind];
+ place = EMSG;
+ ++BSDoptind;
+ }
+ return (BSDoptopt); /* dump back option letter */
+}
+
+#endif /* !defined(HAVE_GETOPT) || !defined(HAVE_OPTRESET) */
diff --git a/openbsd-compat/getpeereid.c b/openbsd-compat/getpeereid.c
new file mode 100644
index 00000000..c8ce808f
--- /dev/null
+++ b/openbsd-compat/getpeereid.c
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2002,2004 Damien Miller <djm@mindrot.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 <unistd.h>
+
+#if defined(SO_PEERCRED)
+int
+getpeereid(int s, uid_t *euid, gid_t *gid)
+{
+ struct ucred cred;
+ socklen_t len = sizeof(cred);
+
+ if (getsockopt(s, SOL_SOCKET, SO_PEERCRED, &cred, &len) < 0)
+ return (-1);
+ *euid = cred.uid;
+ *gid = cred.gid;
+
+ return (0);
+}
+#elif defined(HAVE_GETPEERUCRED)
+
+#ifdef HAVE_UCRED_H
+# include <ucred.h>
+#endif
+
+int
+getpeereid(int s, uid_t *euid, gid_t *gid)
+{
+ ucred_t *ucred = NULL;
+
+ if (getpeerucred(s, &ucred) == -1)
+ return (-1);
+ if ((*euid = ucred_geteuid(ucred)) == -1)
+ return (-1);
+ if ((*gid = ucred_getrgid(ucred)) == -1)
+ return (-1);
+
+ ucred_free(ucred);
+
+ return (0);
+}
+#else
+int
+getpeereid(int s, uid_t *euid, gid_t *gid)
+{
+ *euid = geteuid();
+ *gid = getgid();
+
+ return (0);
+}
+#endif /* defined(SO_PEERCRED) */
diff --git a/openbsd-compat/imsg-buffer.c b/openbsd-compat/imsg-buffer.c
new file mode 100644
index 00000000..e3762092
--- /dev/null
+++ b/openbsd-compat/imsg-buffer.c
@@ -0,0 +1,310 @@
+/* $OpenBSD: imsg-buffer.c,v 1.3 2013/11/13 20:40:24 benno Exp $ */
+
+/*
+ * Copyright (c) 2003, 2004 Henning Brauer <henning@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/param.h>
+#include <sys/queue.h>
+#include <sys/socket.h>
+#include <sys/uio.h>
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#ifndef HAVE_EXPLICIT_BZERO
+#include <strings.h>
+#endif
+#include <unistd.h>
+
+#include "imsg.h"
+
+int ibuf_realloc(struct ibuf *, size_t);
+void ibuf_enqueue(struct msgbuf *, struct ibuf *);
+void ibuf_dequeue(struct msgbuf *, struct ibuf *);
+
+struct ibuf *
+ibuf_open(size_t len)
+{
+ struct ibuf *buf;
+
+ if ((buf = calloc(1, sizeof(struct ibuf))) == NULL)
+ return (NULL);
+ if ((buf->buf = malloc(len)) == NULL) {
+ free(buf);
+ return (NULL);
+ }
+ buf->size = buf->max = len;
+ buf->fd = -1;
+
+ return (buf);
+}
+
+struct ibuf *
+ibuf_dynamic(size_t len, size_t max)
+{
+ struct ibuf *buf;
+
+ if (max < len)
+ return (NULL);
+
+ if ((buf = ibuf_open(len)) == NULL)
+ return (NULL);
+
+ if (max > 0)
+ buf->max = max;
+
+ return (buf);
+}
+
+int
+ibuf_realloc(struct ibuf *buf, size_t len)
+{
+ u_char *b;
+
+ /* on static buffers max is eq size and so the following fails */
+ if (buf->wpos + len > buf->max) {
+ errno = ENOMEM;
+ return (-1);
+ }
+
+ b = realloc(buf->buf, buf->wpos + len);
+ if (b == NULL)
+ return (-1);
+ buf->buf = b;
+ buf->size = buf->wpos + len;
+
+ return (0);
+}
+
+int
+ibuf_add(struct ibuf *buf, const void *data, size_t len)
+{
+ if (buf->wpos + len > buf->size)
+ if (ibuf_realloc(buf, len) == -1)
+ return (-1);
+
+ memcpy(buf->buf + buf->wpos, data, len);
+ buf->wpos += len;
+ return (0);
+}
+
+void *
+ibuf_reserve(struct ibuf *buf, size_t len)
+{
+ void *b;
+
+ if (buf->wpos + len > buf->size)
+ if (ibuf_realloc(buf, len) == -1)
+ return (NULL);
+
+ b = buf->buf + buf->wpos;
+ buf->wpos += len;
+ return (b);
+}
+
+void *
+ibuf_seek(struct ibuf *buf, size_t pos, size_t len)
+{
+ /* only allowed to seek in already written parts */
+ if (pos + len > buf->wpos)
+ return (NULL);
+
+ return (buf->buf + pos);
+}
+
+size_t
+ibuf_size(struct ibuf *buf)
+{
+ return (buf->wpos);
+}
+
+size_t
+ibuf_left(struct ibuf *buf)
+{
+ return (buf->max - buf->wpos);
+}
+
+void
+ibuf_close(struct msgbuf *msgbuf, struct ibuf *buf)
+{
+ ibuf_enqueue(msgbuf, buf);
+}
+
+int
+ibuf_write(struct msgbuf *msgbuf)
+{
+ struct iovec iov[IOV_MAX];
+ struct ibuf *buf;
+ unsigned int i = 0;
+ ssize_t n;
+
+ bzero(&iov, sizeof(iov));
+ TAILQ_FOREACH(buf, &msgbuf->bufs, entry) {
+ if (i >= IOV_MAX)
+ break;
+ iov[i].iov_base = buf->buf + buf->rpos;
+ iov[i].iov_len = buf->wpos - buf->rpos;
+ i++;
+ }
+
+again:
+ if ((n = writev(msgbuf->fd, iov, i)) == -1) {
+ if (errno == EINTR)
+ goto again;
+ if (errno == ENOBUFS)
+ errno = EAGAIN;
+ return (-1);
+ }
+
+ if (n == 0) { /* connection closed */
+ errno = 0;
+ return (0);
+ }
+
+ msgbuf_drain(msgbuf, n);
+
+ return (1);
+}
+
+void
+ibuf_free(struct ibuf *buf)
+{
+ free(buf->buf);
+ free(buf);
+}
+
+void
+msgbuf_init(struct msgbuf *msgbuf)
+{
+ msgbuf->queued = 0;
+ msgbuf->fd = -1;
+ TAILQ_INIT(&msgbuf->bufs);
+}
+
+void
+msgbuf_drain(struct msgbuf *msgbuf, size_t n)
+{
+ struct ibuf *buf, *next;
+
+ for (buf = TAILQ_FIRST(&msgbuf->bufs); buf != NULL && n > 0;
+ buf = next) {
+ next = TAILQ_NEXT(buf, entry);
+ if (buf->rpos + n >= buf->wpos) {
+ n -= buf->wpos - buf->rpos;
+ ibuf_dequeue(msgbuf, buf);
+ } else {
+ buf->rpos += n;
+ n = 0;
+ }
+ }
+}
+
+void
+msgbuf_clear(struct msgbuf *msgbuf)
+{
+ struct ibuf *buf;
+
+ while ((buf = TAILQ_FIRST(&msgbuf->bufs)) != NULL)
+ ibuf_dequeue(msgbuf, buf);
+}
+
+int
+msgbuf_write(struct msgbuf *msgbuf)
+{
+ struct iovec iov[IOV_MAX];
+ struct ibuf *buf;
+ unsigned int i = 0;
+ ssize_t n;
+ struct msghdr msg;
+ struct cmsghdr *cmsg;
+ union {
+ struct cmsghdr hdr;
+ char buf[CMSG_SPACE(sizeof(int))];
+ } cmsgbuf;
+
+ bzero(&iov, sizeof(iov));
+ bzero(&msg, sizeof(msg));
+ TAILQ_FOREACH(buf, &msgbuf->bufs, entry) {
+ if (i >= IOV_MAX)
+ break;
+ iov[i].iov_base = buf->buf + buf->rpos;
+ iov[i].iov_len = buf->wpos - buf->rpos;
+ i++;
+ if (buf->fd != -1)
+ break;
+ }
+
+ msg.msg_iov = iov;
+ msg.msg_iovlen = i;
+
+ if (buf != NULL && buf->fd != -1) {
+ msg.msg_control = (caddr_t)&cmsgbuf.buf;
+ msg.msg_controllen = sizeof(cmsgbuf.buf);
+ cmsg = CMSG_FIRSTHDR(&msg);
+ cmsg->cmsg_len = CMSG_LEN(sizeof(int));
+ cmsg->cmsg_level = SOL_SOCKET;
+ cmsg->cmsg_type = SCM_RIGHTS;
+ *(int *)CMSG_DATA(cmsg) = buf->fd;
+ }
+
+again:
+ if ((n = sendmsg(msgbuf->fd, &msg, 0)) == -1) {
+ if (errno == EINTR)
+ goto again;
+ if (errno == ENOBUFS)
+ errno = EAGAIN;
+ return (-1);
+ }
+
+ if (n == 0) { /* connection closed */
+ errno = 0;
+ return (0);
+ }
+
+ /*
+ * assumption: fd got sent if sendmsg sent anything
+ * this works because fds are passed one at a time
+ */
+ if (buf != NULL && buf->fd != -1) {
+ close(buf->fd);
+ buf->fd = -1;
+ }
+
+ msgbuf_drain(msgbuf, n);
+
+ return (1);
+}
+
+void
+ibuf_enqueue(struct msgbuf *msgbuf, struct ibuf *buf)
+{
+ TAILQ_INSERT_TAIL(&msgbuf->bufs, buf, entry);
+ msgbuf->queued++;
+}
+
+void
+ibuf_dequeue(struct msgbuf *msgbuf, struct ibuf *buf)
+{
+ TAILQ_REMOVE(&msgbuf->bufs, buf, entry);
+
+ if (buf->fd != -1)
+ close(buf->fd);
+
+ msgbuf->queued--;
+ ibuf_free(buf);
+}
diff --git a/openbsd-compat/imsg.c b/openbsd-compat/imsg.c
new file mode 100644
index 00000000..a5900a05
--- /dev/null
+++ b/openbsd-compat/imsg.c
@@ -0,0 +1,330 @@
+/* $OpenBSD: imsg.c,v 1.5 2013/12/26 17:32:33 eric Exp $ */
+
+/*
+ * Copyright (c) 2003, 2004 Henning Brauer <henning@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/param.h>
+#include <sys/queue.h>
+#include <sys/socket.h>
+#include <sys/uio.h>
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#ifndef HAVE_EXPLICIT_BZERO
+#include <strings.h>
+#endif
+#include <unistd.h>
+
+#include "imsg.h"
+
+int imsg_fd_overhead = 0;
+
+int imsg_get_fd(struct imsgbuf *);
+
+int
+available_fds(unsigned int n)
+{
+ unsigned int i;
+ int ret, fds[256];
+
+ if (n > (sizeof(fds)/sizeof(fds[0])))
+ return (1);
+
+ ret = 0;
+ for (i = 0; i < n; i++) {
+ fds[i] = -1;
+ if ((fds[i] = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
+ ret = 1;
+ break;
+ }
+ }
+
+ for (i = 0; i < n && fds[i] >= 0; i++)
+ close(fds[i]);
+
+ return (ret);
+}
+
+void
+imsg_init(struct imsgbuf *ibuf, int fd)
+{
+ msgbuf_init(&ibuf->w);
+ bzero(&ibuf->r, sizeof(ibuf->r));
+ ibuf->fd = fd;
+ ibuf->w.fd = fd;
+ ibuf->pid = getpid();
+ TAILQ_INIT(&ibuf->fds);
+}
+
+ssize_t
+imsg_read(struct imsgbuf *ibuf)
+{
+ struct msghdr msg;
+ struct cmsghdr *cmsg;
+ union {
+ struct cmsghdr hdr;
+ char buf[CMSG_SPACE(sizeof(int) * 1)];
+ } cmsgbuf;
+ struct iovec iov;
+ ssize_t n = -1;
+ int fd;
+ struct imsg_fd *ifd;
+
+ bzero(&msg, sizeof(msg));
+
+ iov.iov_base = ibuf->r.buf + ibuf->r.wpos;
+ iov.iov_len = sizeof(ibuf->r.buf) - ibuf->r.wpos;
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+ msg.msg_control = &cmsgbuf.buf;
+ msg.msg_controllen = sizeof(cmsgbuf.buf);
+
+ if ((ifd = calloc(1, sizeof(struct imsg_fd))) == NULL)
+ return (-1);
+
+again:
+ if (available_fds(imsg_fd_overhead +
+ (CMSG_SPACE(sizeof(int))-CMSG_SPACE(0))/sizeof(int))) {
+ errno = EAGAIN;
+ free(ifd);
+ return (-1);
+ }
+
+ if ((n = recvmsg(ibuf->fd, &msg, 0)) == -1) {
+ if (errno == EMSGSIZE)
+ goto fail;
+ if (errno != EINTR && errno != EAGAIN)
+ goto fail;
+ goto again;
+ }
+
+ ibuf->r.wpos += n;
+
+ for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL;
+ cmsg = CMSG_NXTHDR(&msg, cmsg)) {
+ if (cmsg->cmsg_level == SOL_SOCKET &&
+ cmsg->cmsg_type == SCM_RIGHTS) {
+ int i;
+ int j;
+
+ /*
+ * We only accept one file descriptor. Due to C
+ * padding rules, our control buffer might contain
+ * more than one fd, and we must close them.
+ */
+ j = ((char *)cmsg + cmsg->cmsg_len -
+ (char *)CMSG_DATA(cmsg)) / sizeof(int);
+ for (i = 0; i < j; i++) {
+ fd = ((int *)CMSG_DATA(cmsg))[i];
+ if (ifd != NULL) {
+ ifd->fd = fd;
+ TAILQ_INSERT_TAIL(&ibuf->fds, ifd,
+ entry);
+ ifd = NULL;
+ } else
+ close(fd);
+ }
+ }
+ /* we do not handle other ctl data level */
+ }
+
+fail:
+ if (ifd)
+ free(ifd);
+ return (n);
+}
+
+ssize_t
+imsg_get(struct imsgbuf *ibuf, struct imsg *imsg)
+{
+ size_t av, left, datalen;
+
+ av = ibuf->r.wpos;
+
+ if (IMSG_HEADER_SIZE > av)
+ return (0);
+
+ memcpy(&imsg->hdr, ibuf->r.buf, sizeof(imsg->hdr));
+ if (imsg->hdr.len < IMSG_HEADER_SIZE ||
+ imsg->hdr.len > MAX_IMSGSIZE) {
+ errno = ERANGE;
+ return (-1);
+ }
+ if (imsg->hdr.len > av)
+ return (0);
+ datalen = imsg->hdr.len - IMSG_HEADER_SIZE;
+ ibuf->r.rptr = ibuf->r.buf + IMSG_HEADER_SIZE;
+ if ((imsg->data = malloc(datalen)) == NULL)
+ return (-1);
+
+ if (imsg->hdr.flags & IMSGF_HASFD)
+ imsg->fd = imsg_get_fd(ibuf);
+ else
+ imsg->fd = -1;
+
+ memcpy(imsg->data, ibuf->r.rptr, datalen);
+
+ if (imsg->hdr.len < av) {
+ left = av - imsg->hdr.len;
+ memmove(&ibuf->r.buf, ibuf->r.buf + imsg->hdr.len, left);
+ ibuf->r.wpos = left;
+ } else
+ ibuf->r.wpos = 0;
+
+ return (datalen + IMSG_HEADER_SIZE);
+}
+
+int
+imsg_compose(struct imsgbuf *ibuf, uint32_t type, uint32_t peerid,
+ pid_t pid, int fd, const void *data, uint16_t datalen)
+{
+ struct ibuf *wbuf;
+
+ if ((wbuf = imsg_create(ibuf, type, peerid, pid, datalen)) == NULL)
+ return (-1);
+
+ if (imsg_add(wbuf, data, datalen) == -1)
+ return (-1);
+
+ wbuf->fd = fd;
+
+ imsg_close(ibuf, wbuf);
+
+ return (1);
+}
+
+int
+imsg_composev(struct imsgbuf *ibuf, uint32_t type, uint32_t peerid,
+ pid_t pid, int fd, const struct iovec *iov, int iovcnt)
+{
+ struct ibuf *wbuf;
+ int i, datalen = 0;
+
+ for (i = 0; i < iovcnt; i++)
+ datalen += iov[i].iov_len;
+
+ if ((wbuf = imsg_create(ibuf, type, peerid, pid, datalen)) == NULL)
+ return (-1);
+
+ for (i = 0; i < iovcnt; i++)
+ if (imsg_add(wbuf, iov[i].iov_base, iov[i].iov_len) == -1)
+ return (-1);
+
+ wbuf->fd = fd;
+
+ imsg_close(ibuf, wbuf);
+
+ return (1);
+}
+
+/* ARGSUSED */
+struct ibuf *
+imsg_create(struct imsgbuf *ibuf, uint32_t type, uint32_t peerid,
+ pid_t pid, uint16_t datalen)
+{
+ struct ibuf *wbuf;
+ struct imsg_hdr hdr;
+
+ datalen += IMSG_HEADER_SIZE;
+ if (datalen > MAX_IMSGSIZE) {
+ errno = ERANGE;
+ return (NULL);
+ }
+
+ hdr.type = type;
+ hdr.flags = 0;
+ hdr.peerid = peerid;
+ if ((hdr.pid = pid) == 0)
+ hdr.pid = ibuf->pid;
+ if ((wbuf = ibuf_dynamic(datalen, MAX_IMSGSIZE)) == NULL) {
+ return (NULL);
+ }
+ if (imsg_add(wbuf, &hdr, sizeof(hdr)) == -1)
+ return (NULL);
+
+ return (wbuf);
+}
+
+int
+imsg_add(struct ibuf *msg, const void *data, uint16_t datalen)
+{
+ if (datalen)
+ if (ibuf_add(msg, data, datalen) == -1) {
+ ibuf_free(msg);
+ return (-1);
+ }
+ return (datalen);
+}
+
+void
+imsg_close(struct imsgbuf *ibuf, struct ibuf *msg)
+{
+ struct imsg_hdr *hdr;
+
+ hdr = (struct imsg_hdr *)msg->buf;
+
+ hdr->flags &= ~IMSGF_HASFD;
+ if (msg->fd != -1)
+ hdr->flags |= IMSGF_HASFD;
+
+ hdr->len = (uint16_t)msg->wpos;
+
+ ibuf_close(&ibuf->w, msg);
+}
+
+void
+imsg_free(struct imsg *imsg)
+{
+ free(imsg->data);
+}
+
+int
+imsg_get_fd(struct imsgbuf *ibuf)
+{
+ int fd;
+ struct imsg_fd *ifd;
+
+ if ((ifd = TAILQ_FIRST(&ibuf->fds)) == NULL)
+ return (-1);
+
+ fd = ifd->fd;
+ TAILQ_REMOVE(&ibuf->fds, ifd, entry);
+ free(ifd);
+
+ return (fd);
+}
+
+int
+imsg_flush(struct imsgbuf *ibuf)
+{
+ while (ibuf->w.queued)
+ if (msgbuf_write(&ibuf->w) <= 0)
+ return (-1);
+ return (0);
+}
+
+void
+imsg_clear(struct imsgbuf *ibuf)
+{
+ int fd;
+
+ msgbuf_clear(&ibuf->w);
+ while ((fd = imsg_get_fd(ibuf)) != -1)
+ close(fd);
+}
diff --git a/openbsd-compat/imsg.h b/openbsd-compat/imsg.h
new file mode 100644
index 00000000..3757c8b9
--- /dev/null
+++ b/openbsd-compat/imsg.h
@@ -0,0 +1,115 @@
+/* $OpenBSD: imsg.h,v 1.3 2013/12/26 17:32:33 eric Exp $ */
+
+/*
+ * Copyright (c) 2006, 2007 Pierre-Yves Ritschard <pyr@openbsd.org>
+ * Copyright (c) 2006, 2007, 2008 Reyk Floeter <reyk@openbsd.org>
+ * Copyright (c) 2003, 2004 Henning Brauer <henning@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.
+ */
+
+#ifndef _IMSG_H_
+#define _IMSG_H_
+
+#define IBUF_READ_SIZE 65535
+#define IMSG_HEADER_SIZE sizeof(struct imsg_hdr)
+#define MAX_IMSGSIZE 16384
+
+#include "defines.h"
+
+struct ibuf {
+ TAILQ_ENTRY(ibuf) entry;
+ u_char *buf;
+ size_t size;
+ size_t max;
+ size_t wpos;
+ size_t rpos;
+ int fd;
+};
+
+struct msgbuf {
+ TAILQ_HEAD(, ibuf) bufs;
+ uint32_t queued;
+ int fd;
+};
+
+struct ibuf_read {
+ u_char buf[IBUF_READ_SIZE];
+ u_char *rptr;
+ size_t wpos;
+};
+
+struct imsg_fd {
+ TAILQ_ENTRY(imsg_fd) entry;
+ int fd;
+};
+
+struct imsgbuf {
+ TAILQ_HEAD(, imsg_fd) fds;
+ struct ibuf_read r;
+ struct msgbuf w;
+ int fd;
+ pid_t pid;
+};
+
+#define IMSGF_HASFD 1
+
+struct imsg_hdr {
+ uint32_t type;
+ uint16_t len;
+ uint16_t flags;
+ uint32_t peerid;
+ uint32_t pid;
+};
+
+struct imsg {
+ struct imsg_hdr hdr;
+ int fd;
+ void *data;
+};
+
+
+/* buffer.c */
+struct ibuf *ibuf_open(size_t);
+struct ibuf *ibuf_dynamic(size_t, size_t);
+int ibuf_add(struct ibuf *, const void *, size_t);
+void *ibuf_reserve(struct ibuf *, size_t);
+void *ibuf_seek(struct ibuf *, size_t, size_t);
+size_t ibuf_size(struct ibuf *);
+size_t ibuf_left(struct ibuf *);
+void ibuf_close(struct msgbuf *, struct ibuf *);
+int ibuf_write(struct msgbuf *);
+void ibuf_free(struct ibuf *);
+void msgbuf_init(struct msgbuf *);
+void msgbuf_clear(struct msgbuf *);
+int msgbuf_write(struct msgbuf *);
+void msgbuf_drain(struct msgbuf *, size_t);
+
+/* imsg.c */
+int available_fds(unsigned int);
+void imsg_init(struct imsgbuf *, int);
+ssize_t imsg_read(struct imsgbuf *);
+ssize_t imsg_get(struct imsgbuf *, struct imsg *);
+int imsg_compose(struct imsgbuf *, uint32_t, uint32_t, pid_t,
+ int, const void *, uint16_t);
+int imsg_composev(struct imsgbuf *, uint32_t, uint32_t, pid_t,
+ int, const struct iovec *, int);
+struct ibuf *imsg_create(struct imsgbuf *, uint32_t, uint32_t, pid_t,
+ uint16_t);
+int imsg_add(struct ibuf *, const void *, uint16_t);
+void imsg_close(struct imsgbuf *, struct ibuf *);
+void imsg_free(struct imsg *);
+int imsg_flush(struct imsgbuf *);
+void imsg_clear(struct imsgbuf *);
+
+#endif
diff --git a/openbsd-compat/includes.h b/openbsd-compat/includes.h
new file mode 100644
index 00000000..cd044a20
--- /dev/null
+++ b/openbsd-compat/includes.h
@@ -0,0 +1,75 @@
+/* $OpenBSD: includes.h,v 1.54 2006/07/22 20:48:23 stevesk Exp $ */
+
+/*
+ * Author: Tatu Ylonen <ylo@cs.hut.fi>
+ * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
+ * All rights reserved
+ * This file includes most of the needed system headers.
+ *
+ * As far as I am concerned, the code I have written for this software
+ * can be used freely for any purpose. Any derived versions of this
+ * software must be clearly marked as such, and if the derived work is
+ * incompatible with the protocol description in the RFC file, it must be
+ * called by a name other than "ssh" or "Secure Shell".
+ */
+
+#ifndef INCLUDES_H
+#define INCLUDES_H
+
+#include "config.h"
+
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE /* activate extra prototypes for glibc */
+#endif
+
+#include <sys/types.h>
+#include <sys/socket.h> /* For CMSG_* */
+
+#ifdef HAVE_LIMITS_H
+# include <limits.h> /* For PATH_MAX */
+#endif
+#ifdef HAVE_BSTRING_H
+# include <bstring.h>
+#endif
+
+#ifdef HAVE_ENDIAN_H
+# include <endian.h>
+#endif
+#ifdef HAVE_MAILLOCK_H
+# include <maillock.h> /* For _PATH_MAILDIR */
+#endif
+#ifdef HAVE_PATHS_H
+# include <paths.h>
+#endif
+
+#ifdef HAVE_RPC_TYPES_H
+# include <rpc/types.h> /* For INADDR_LOOPBACK */
+#endif
+#ifdef USE_PAM
+#if defined(HAVE_SECURITY_PAM_APPL_H)
+# include <security/pam_appl.h>
+#elif defined (HAVE_PAM_PAM_APPL_H)
+# include <pam/pam_appl.h>
+#endif
+#endif
+#include <errno.h>
+
+/* chl */
+#ifdef HAVE_NETDB_H
+# include <netdb.h>
+#endif
+/* end of chl*/
+
+#if !defined(NETDB_INTERNAL)
+# define NETDB_INTERNAL (-1)
+#endif
+
+#include <openssl/opensslv.h> /* For OPENSSL_VERSION_NUMBER */
+
+#include "defines.h"
+
+#include "openbsd-compat.h"
+
+#include "entropy.h"
+
+#endif /* INCLUDES_H */
diff --git a/openbsd-compat/inet_net_pton.c b/openbsd-compat/inet_net_pton.c
new file mode 100644
index 00000000..b65cb76f
--- /dev/null
+++ b/openbsd-compat/inet_net_pton.c
@@ -0,0 +1,236 @@
+/* $OpenBSD: inet_net_pton.c,v 1.8 2013/11/25 18:23:51 deraadt Exp $ */
+
+/*
+ * Copyright (c) 2012 by Gilles Chehade <gilles@openbsd.org>
+ * Copyright (c) 1996 by 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.
+ */
+
+#include "includes.h"
+#ifndef HAVE_INET_NET_PTON
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <assert.h>
+#include <ctype.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+static int inet_net_pton_ipv4(const char *, u_char *, size_t);
+static int inet_net_pton_ipv6(const char *, u_char *, size_t);
+
+/*
+ * static int
+ * inet_net_pton(af, src, dst, size)
+ * convert network number from presentation to network format.
+ * accepts hex octets, hex strings, decimal octets, and /CIDR.
+ * "size" is in bytes and describes "dst".
+ * return:
+ * number of bits, either imputed classfully or specified with /CIDR,
+ * or -1 if some failure occurred (check errno). ENOENT means it was
+ * not a valid network specification.
+ * author:
+ * Paul Vixie (ISC), June 1996
+ */
+int
+inet_net_pton(int af, const char *src, void *dst, size_t size)
+{
+ switch (af) {
+ case AF_INET:
+ return (inet_net_pton_ipv4(src, dst, size));
+ case AF_INET6:
+ return (inet_net_pton_ipv6(src, dst, size));
+ default:
+ errno = EAFNOSUPPORT;
+ return (-1);
+ }
+}
+
+/*
+ * static int
+ * inet_net_pton_ipv4(src, dst, size)
+ * convert IPv4 network number from presentation to network format.
+ * accepts hex octets, hex strings, decimal octets, and /CIDR.
+ * "size" is in bytes and describes "dst".
+ * return:
+ * number of bits, either imputed classfully or specified with /CIDR,
+ * or -1 if some failure occurred (check errno). ENOENT means it was
+ * not an IPv4 network specification.
+ * note:
+ * network byte order assumed. this means 192.5.5.240/28 has
+ * 0x11110000 in its fourth octet.
+ * author:
+ * Paul Vixie (ISC), June 1996
+ */
+static int
+inet_net_pton_ipv4(const char *src, u_char *dst, size_t size)
+{
+ static const char
+ xdigits[] = "0123456789abcdef",
+ digits[] = "0123456789";
+ int n, ch, tmp, dirty, bits;
+ const u_char *odst = dst;
+
+ ch = (unsigned char)*src++;
+ if (ch == '0' && (src[0] == 'x' || src[0] == 'X')
+ && isascii((unsigned char)src[1]) && isxdigit((unsigned char)src[1])) {
+ /* Hexadecimal: Eat nybble string. */
+ if (size <= 0)
+ goto emsgsize;
+ *dst = 0, dirty = 0;
+ src++; /* skip x or X. */
+ while ((ch = (unsigned char)*src++) != '\0' &&
+ isascii(ch) && isxdigit(ch)) {
+ if (isupper(ch))
+ ch = tolower(ch);
+ n = strchr(xdigits, ch) - xdigits;
+ assert(n >= 0 && n <= 15);
+ *dst |= n;
+ if (!dirty++)
+ *dst <<= 4;
+ else if (size-- > 0)
+ *++dst = 0, dirty = 0;
+ else
+ goto emsgsize;
+ }
+ if (dirty)
+ size--;
+ } else if (isascii(ch) && isdigit(ch)) {
+ /* Decimal: eat dotted digit string. */
+ for (;;) {
+ tmp = 0;
+ do {
+ n = strchr(digits, ch) - digits;
+ assert(n >= 0 && n <= 9);
+ tmp *= 10;
+ tmp += n;
+ if (tmp > 255)
+ goto enoent;
+ } while ((ch = (unsigned char)*src++) != '\0' &&
+ isascii(ch) && isdigit(ch));
+ if (size-- <= 0)
+ goto emsgsize;
+ *dst++ = (u_char) tmp;
+ if (ch == '\0' || ch == '/')
+ break;
+ if (ch != '.')
+ goto enoent;
+ ch = (unsigned char)*src++;
+ if (!isascii(ch) || !isdigit(ch))
+ goto enoent;
+ }
+ } else
+ goto enoent;
+
+ bits = -1;
+ if (ch == '/' && isascii((unsigned char)src[0]) &&
+ isdigit((unsigned char)src[0]) && dst > odst) {
+ /* CIDR width specifier. Nothing can follow it. */
+ ch = (unsigned char)*src++; /* Skip over the /. */
+ bits = 0;
+ do {
+ n = strchr(digits, ch) - digits;
+ assert(n >= 0 && n <= 9);
+ bits *= 10;
+ bits += n;
+ if (bits > 32)
+ goto emsgsize;
+ } while ((ch = (unsigned char)*src++) != '\0' &&
+ isascii(ch) && isdigit(ch));
+ if (ch != '\0')
+ goto enoent;
+ }
+
+ /* Firey death and destruction unless we prefetched EOS. */
+ if (ch != '\0')
+ goto enoent;
+
+ /* If nothing was written to the destination, we found no address. */
+ if (dst == odst)
+ goto enoent;
+ /* If no CIDR spec was given, infer width from net class. */
+ if (bits == -1) {
+ if (*odst >= 240) /* Class E */
+ bits = 32;
+ else if (*odst >= 224) /* Class D */
+ bits = 4;
+ else if (*odst >= 192) /* Class C */
+ bits = 24;
+ else if (*odst >= 128) /* Class B */
+ bits = 16;
+ else /* Class A */
+ bits = 8;
+ /* If imputed mask is narrower than specified octets, widen. */
+ if (bits < ((dst - odst) * 8))
+ bits = (dst - odst) * 8;
+ }
+ /* Extend network to cover the actual mask. */
+ while (bits > ((dst - odst) * 8)) {
+ if (size-- <= 0)
+ goto emsgsize;
+ *dst++ = '\0';
+ }
+ return (bits);
+
+ enoent:
+ errno = ENOENT;
+ return (-1);
+
+ emsgsize:
+ errno = EMSGSIZE;
+ return (-1);
+}
+
+
+static int
+inet_net_pton_ipv6(const char *src, u_char *dst, size_t size)
+{
+ int ret;
+ int bits;
+ char buf[sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:255:255:255:255/128")];
+ char *sep;
+ const char *errstr;
+
+ if (strlcpy(buf, src, sizeof buf) >= sizeof buf) {
+ errno = EMSGSIZE;
+ return (-1);
+ }
+
+ sep = strchr(buf, '/');
+ if (sep != NULL)
+ *sep++ = '\0';
+
+ ret = inet_pton(AF_INET6, buf, dst);
+ if (ret != 1)
+ return (-1);
+
+ if (sep == NULL)
+ return 128;
+
+ bits = strtonum(sep, 0, 128, &errstr);
+ if (errstr) {
+ errno = EINVAL;
+ return (-1);
+ }
+
+ return bits;
+}
+
+#endif
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/libressl.c b/openbsd-compat/libressl.c
new file mode 100644
index 00000000..f4f2b52e
--- /dev/null
+++ b/openbsd-compat/libressl.c
@@ -0,0 +1,131 @@
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to. The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code. The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * 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 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. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * "This product includes cryptographic software written by
+ * Eric Young (eay@cryptsoft.com)"
+ * The word 'cryptographic' can be left out if the rouines from the library
+ * being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ * the apps directory (application code) you must include an acknowledgement:
+ * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``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 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.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed. i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.]
+ */
+
+#include <sys/types.h>
+
+#include <unistd.h>
+#include <stdio.h>
+
+#include <openssl/err.h>
+#include <openssl/bio.h>
+#include <openssl/objects.h>
+#include <openssl/evp.h>
+#include <openssl/x509.h>
+#include <openssl/pem.h>
+#include <openssl/ssl.h>
+
+int
+SSL_CTX_use_certificate_chain(SSL_CTX *ctx, char *buf, off_t len)
+{
+ int ret;
+ BIO*in;
+ X509*x;
+ X509*ca;
+ unsigned long err;
+
+ ret = 0;
+ x = ca = NULL;
+
+ if ((in = BIO_new_mem_buf(buf, len)) == NULL) {
+ SSLerr(SSL_F_SSL_CTX_USE_CERTIFICATE_CHAIN_FILE, ERR_R_BUF_LIB);
+ goto end;
+ }
+
+ if ((x = PEM_read_bio_X509(in, NULL,
+ ctx->default_passwd_callback,
+ ctx->default_passwd_callback_userdata)) == NULL) {
+ SSLerr(SSL_F_SSL_CTX_USE_CERTIFICATE_CHAIN_FILE, ERR_R_PEM_LIB);
+ goto end;
+ }
+
+ if (!SSL_CTX_use_certificate(ctx, x) || ERR_peek_error() != 0)
+ goto end;
+
+ /* If we could set up our certificate, now proceed to
+ * the CA certificates.
+ */
+
+ if (ctx->extra_certs != NULL) {
+ sk_X509_pop_free(ctx->extra_certs, X509_free);
+ ctx->extra_certs = NULL;
+ }
+
+ while ((ca = PEM_read_bio_X509(in, NULL,
+ ctx->default_passwd_callback,
+ ctx->default_passwd_callback_userdata)) != NULL) {
+
+ if (!SSL_CTX_add_extra_chain_cert(ctx, ca))
+ goto end;
+ }
+
+ err = ERR_peek_last_error();
+ if (ERR_GET_LIB(err) == ERR_LIB_PEM &&
+ ERR_GET_REASON(err) == PEM_R_NO_START_LINE)
+ ERR_clear_error();
+ else
+ goto end;
+
+ ret = 1;
+end:
+ if (ca != NULL)
+ X509_free(ca);
+ if (x != NULL)
+ X509_free(x);
+ if (in != NULL)
+ BIO_free(in);
+ return (ret);
+}
diff --git a/openbsd-compat/nanosleep.c b/openbsd-compat/nanosleep.c
new file mode 100644
index 00000000..1256c0b5
--- /dev/null
+++ b/openbsd-compat/nanosleep.c
@@ -0,0 +1,63 @@
+
+/*
+ * Copyright (c) 1999-2004 Damien Miller <djm@mindrot.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>
+#ifdef HAVE_SYS_SELECT_H
+# include <sys/select.h>
+#endif
+#ifdef HAVE_SYS_TIME_H
+# include <sys/time.h>
+#endif
+
+#include <err.h>
+#include <string.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <time.h>
+#include <unistd.h>
+
+int
+nanosleep(const struct timespec *req, struct timespec *rem)
+{
+ int rc, saverrno;
+ extern int errno;
+ struct timeval tstart, tstop, tremain, time2wait;
+
+ TIMESPEC_TO_TIMEVAL(&time2wait, req);
+ (void) gettimeofday(&tstart, NULL);
+ rc = select(0, NULL, NULL, NULL, &time2wait);
+ if (rc == -1) {
+ saverrno = errno;
+ (void) gettimeofday (&tstop, NULL);
+ errno = saverrno;
+ tremain.tv_sec = time2wait.tv_sec -
+ (tstop.tv_sec - tstart.tv_sec);
+ tremain.tv_usec = time2wait.tv_usec -
+ (tstop.tv_usec - tstart.tv_usec);
+ tremain.tv_sec += tremain.tv_usec / 1000000L;
+ tremain.tv_usec %= 1000000L;
+ } else {
+ tremain.tv_sec = 0;
+ tremain.tv_usec = 0;
+ }
+ if (rem != NULL)
+ TIMEVAL_TO_TIMESPEC(&tremain, rem);
+
+ return(rc);
+}
diff --git a/openbsd-compat/openbsd-compat.h b/openbsd-compat/openbsd-compat.h
new file mode 100644
index 00000000..dcb643f1
--- /dev/null
+++ b/openbsd-compat/openbsd-compat.h
@@ -0,0 +1,342 @@
+/* $Id: openbsd-compat.h,v 1.51 2010/10/07 10:25:29 djm Exp $ */
+
+/*
+ * Copyright (c) 1999-2003 Damien Miller. All rights reserved.
+ * Copyright (c) 2003 Ben Lindstrom. All rights reserved.
+ * Copyright (c) 2002 Tim Rice. 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.
+ */
+
+#ifndef _OPENBSD_COMPAT_H
+#define _OPENBSD_COMPAT_H
+
+#include "includes.h"
+
+#include <sys/types.h>
+
+#include <sys/socket.h>
+#include <netinet/in.h>
+
+/* OpenBSD function replacements */
+#include "base64.h"
+
+#include <sys/queue.h>
+#include <sys/tree.h>
+#include "bsd-vis.h"
+
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
+
+#ifndef HAVE_BASENAME
+char *basename(const char *path);
+#endif
+
+#ifndef HAVE_CLOSEFROM
+void closefrom(int);
+#endif
+
+#if !defined(HAVE_REALPATH) || defined(BROKEN_REALPATH)
+char *realpath(const char *path, char *resolved);
+#endif
+
+#if !HAVE_DECL_STRLCPY
+size_t strlcpy(char *dst, const char *src, size_t size);
+#endif
+
+#if !HAVE_DECL_STRLCAT
+size_t strlcat(char *dst, const char *src, size_t size);
+#endif
+
+#ifndef HAVE_STRMODE
+void strmode(int mode, char *p);
+#endif
+
+#ifndef HAVE_DAEMON
+int daemon(int nochdir, int noclose);
+#endif
+
+#ifndef HAVE_DIRNAME
+char *dirname(const char *path);
+#endif
+
+#ifndef HAVE_FMT_SCALED
+#define FMT_SCALED_STRSIZE 7
+int fmt_scaled(long long number, char *result);
+#endif
+
+#ifndef HAVE_SCAN_SCALED
+int scan_scaled(char *, long long *);
+#endif
+
+#ifndef HAVE_INET_NTOP
+const char *inet_ntop(int af, const void *src, char *dst, socklen_t size);
+#endif
+
+#ifndef HAVE_STRSEP
+char *strsep(char **stringp, const char *delim);
+#endif
+
+#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);
+#endif
+
+/* Home grown routines */
+#include "bsd-misc.h"
+/* #include "bsd-setres_id.h" */
+/* #include "bsd-statvfs.h" */
+#include "bsd-waitpid.h"
+/* #include "bsd-poll.h" */
+
+#ifndef HAVE_GETPEEREID
+int getpeereid(int , uid_t *, gid_t *);
+#endif
+
+#if !defined(HAVE_ARC4RANDOM) || defined(LIBRESSL_VERSION_NUMBER)
+unsigned int arc4random(void);
+#endif
+
+#if 0
+#if defined(LIBRESSL_VERSION_NUMBER)
+# define arc4random_stir()
+#elif defined(HAVE_ARC4RANDOM_STIR)
+void arc4random_stir(void);
+#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);
+#endif
+
+#if !defined(HAVE_ARC4RANDOM_UNIFORM) || defined(LIBRESSL_VERSION_NUMBER)
+uint32_t arc4random_uniform(uint32_t);
+#endif
+
+#if !defined(SSL_OP_NO_CLIENT_RENEGOTIATION) && !defined(LIBRESSL_VERSION_NUMBER)
+#define SSL_OP_NO_CLIENT_RENEGOTIATION 0
+#endif
+
+#ifndef HAVE_ASPRINTF
+int asprintf(char **, const char *, ...);
+#endif
+
+/* #include <sys/types.h> XXX needed? For size_t */
+
+#ifndef HAVE_SNPRINTF
+int snprintf(char *, size_t, const char *, ...);
+#endif
+
+#ifndef HAVE_STRTOLL
+long long strtoll(const char *, char **, int);
+#endif
+
+#ifndef HAVE_STRTOUL
+unsigned long strtoul(const char *, char **, int);
+#endif
+
+#ifndef HAVE_STRTOULL
+unsigned long long strtoull(const char *, char **, int);
+#endif
+
+#ifndef HAVE_STRTONUM
+long long strtonum(const char *nptr, long long minval, long long maxval, const char **errstr);
+#endif
+
+#if !defined(HAVE_VASPRINTF) || !defined(HAVE_VSNPRINTF)
+# include <stdarg.h>
+#endif
+
+#ifndef HAVE_VASPRINTF
+int vasprintf(char **, const char *, va_list);
+#endif
+
+#ifndef HAVE_VSNPRINTF
+int vsnprintf(char *, size_t, const char *, va_list);
+#endif
+
+#if !defined(HAVE_EXPLICIT_BZERO) || defined(LIBRESSL_VERSION_NUMBER)
+void explicit_bzero(void *p, size_t n);
+#endif
+
+/* OpenSMTPD-portable specific entries */
+
+#ifndef HAVE_FGETLN
+#include <stdio.h>
+#include <string.h>
+char * fgetln(FILE *stream, size_t *len);
+#endif
+
+#ifndef HAVE_FPARSELN
+#include <stdio.h>
+#include <string.h>
+char * fparseln(FILE *fp, size_t *size, size_t *lineno, const char str[3], int flags);
+#endif
+
+#ifndef HAVE_FREEZERO
+void freezero(void *, size_t);
+#endif
+
+#ifndef HAVE_PIDFILE
+int pidfile(const char *basename);
+#endif
+
+#ifndef HAVE_PW_DUP
+struct passwd *pw_dup(const struct passwd *);
+#endif
+
+#if !defined(HAVE_REALLOCARRAY) || defined(LIBRESSL_VERSION_NUMBER)
+void *reallocarray(void *, size_t, size_t);
+#endif
+
+#if !defined(HAVE_RECALLOCARRAY) || defined(LIBRESSL_VERSION_NUMBER)
+void *recallocarray(void *, size_t, size_t, size_t);
+#endif
+
+#ifndef HAVE_ERRC
+__attribute__ ((noreturn))
+void errc(int, int, const char *, ...);
+#endif
+
+#ifndef HAVE_INET_NET_PTON
+int inet_net_pton(int, const char *, void *, size_t);
+#endif
+
+#ifndef HAVE_PLEDGE
+#define pledge(promises, paths) 0
+#endif
+
+#ifndef HAVE_MALLOC_CONCEAL
+#define malloc_conceal malloc
+#endif
+
+#ifndef HAVE_CALLOC_CONCEAL
+#define calloc_conceal calloc
+#endif
+
+#ifndef HAVE_RES_HNOK
+int res_hnok(const char *);
+#endif
+
+#if !HAVE_DECL_AF_LOCAL
+#define AF_LOCAL AF_UNIX
+#endif
+
+#if !HAVE_DECL_PF_LOCAL
+#define PF_LOCAL PF_UNIX
+#endif
+
+#if !HAVE_DECL_WAIT_MYPGRP
+#define WAIT_MYPGRP 0
+#endif
+
+#if !HAVE_DECL_IPPORT_HILASTAUTO
+#define IPPORT_HILASTAUTO 65535
+#endif
+
+#ifndef HAVE_FLOCK
+int flock(int, int);
+#endif
+
+#ifndef HAVE_SETRESGID
+int setresgid(uid_t, uid_t, uid_t);
+#endif
+
+#ifndef HAVE_SETRESUID
+int setresuid(uid_t, uid_t, uid_t);
+#endif
+
+#ifndef HAVE_GETLINE
+ssize_t getline(char **, size_t *, FILE *);
+#endif
+
+#ifndef HAVE_CRYPT_CHECKPASS
+int crypt_checkpass(const char *, const char *);
+#endif
+
+#ifndef HAVE_STRNDUP
+char * strndup(const char *, size_t);
+#endif
+
+#ifndef HAVE_STRNLEN
+char * strnlen(const char *, size_t);
+#endif
+
+#ifndef HAVE_STRUCT_TIMEVAL
+struct timeval {
+ long tv_sec;
+ long tv_usec;
+}
+#endif
+
+#ifdef NEED_NANOSLEEP
+#ifndef HAVE_STRUCT_TIMESPEC
+struct timespec {
+ time_t tv_sec;
+ long tv_nsec;
+};
+#endif
+int nanosleep(const struct timespec *, struct timespec *);
+#endif
+
+#ifdef NEED_SETEGID
+int setegid(uid_t);
+#endif
+
+#ifdef NEED_SETEUID
+int seteuid(uid_t);
+#endif
+
+#ifdef NEED_SETSID
+#define setsid() setpgrp(0, getpid())
+#endif
+
+#ifdef NEED_SIGNAL
+typedef void (*mysig_t)(int);
+mysig_t mysignal(int sig, mysig_t act);
+#define signal(a,b) mysignal(a,b)
+#endif
+
+#ifdef NEED_STRERROR
+const char *strerror(int);
+#endif
+
+#ifdef NEED_USLEEP
+int usleep(unsigned int useconds);
+#endif
+
+int pipe2(int pipefd[2], int flags);
+
+char *get_progname(char *);
+
+
+#endif /* _OPENBSD_COMPAT_H */
diff --git a/openbsd-compat/paths_h/paths.h b/openbsd-compat/paths_h/paths.h
new file mode 100644
index 00000000..6b66a9c1
--- /dev/null
+++ b/openbsd-compat/paths_h/paths.h
@@ -0,0 +1,8 @@
+#ifndef PATHS_H
+#define PATHS_H
+
+#ifndef _PATH_DEFPATH
+#define _PATH_DEFPATH "/bin:/usr/bin"
+#endif
+
+#endif
diff --git a/openbsd-compat/pidfile.c b/openbsd-compat/pidfile.c
new file mode 100644
index 00000000..d6f83880
--- /dev/null
+++ b/openbsd-compat/pidfile.c
@@ -0,0 +1,112 @@
+/* $OpenBSD: pidfile.c,v 1.8 2008/06/26 05:42:05 ray Exp $ */
+/* $NetBSD: pidfile.c,v 1.4 2001/02/19 22:43:42 cgd Exp $ */
+
+/*-
+ * Copyright (c) 1999 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Jason R. Thorpe.
+ *
+ * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
+ */
+
+/* OPENBSD ORIGINAL: lib/libutil/pidfile.c */
+
+#include "includes.h"
+#ifndef HAVE_PIDFILE
+
+#include <sys/param.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+static char *pidfile_path;
+static pid_t pidfile_pid;
+
+static void pidfile_cleanup(void);
+
+extern char *__progname;
+
+int
+pidfile(const char *basename)
+{
+ int save_errno;
+ pid_t pid;
+ FILE *f;
+
+ if (basename == NULL)
+ basename = __progname;
+
+ if (pidfile_path != NULL) {
+ free(pidfile_path);
+ pidfile_path = NULL;
+ }
+
+ (void) asprintf(&pidfile_path, "%s/%s.pid", SMTPD_PIDDIR, basename);
+ if (pidfile_path == NULL)
+ return (-1);
+
+ if ((f = fopen(pidfile_path, "w")) == NULL) {
+ save_errno = errno;
+ free(pidfile_path);
+ pidfile_path = NULL;
+ errno = save_errno;
+ return (-1);
+ }
+
+ pid = getpid();
+ if (fprintf(f, "%ld\n", (long)pid) <= 0) {
+ fclose(f);
+ save_errno = errno;
+ (void) unlink(pidfile_path);
+ free(pidfile_path);
+ pidfile_path = NULL;
+ errno = save_errno;
+ return (-1);
+ }
+
+ fclose(f);
+ pidfile_pid = pid;
+ if (atexit(pidfile_cleanup) < 0) {
+ save_errno = errno;
+ (void) unlink(pidfile_path);
+ free(pidfile_path);
+ pidfile_path = NULL;
+ pidfile_pid = 0;
+ errno = save_errno;
+ return (-1);
+ }
+
+ return (0);
+}
+
+static void
+pidfile_cleanup(void)
+{
+
+ if (pidfile_path != NULL && pidfile_pid == getpid())
+ (void) unlink(pidfile_path);
+}
+
+#endif
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/progname.c b/openbsd-compat/progname.c
new file mode 100644
index 00000000..b8b9ae30
--- /dev/null
+++ b/openbsd-compat/progname.c
@@ -0,0 +1,62 @@
+
+/*
+ * Copyright (c) 1999-2004 Damien Miller <djm@mindrot.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>
+#ifdef HAVE_SYS_SELECT_H
+# include <sys/select.h>
+#endif
+#ifdef HAVE_SYS_TIME_H
+# include <sys/time.h>
+#endif
+
+#include <err.h>
+#include <string.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <time.h>
+#include <unistd.h>
+
+/*
+ * NB. duplicate __progname in case it is an alias for argv[0]
+ * Otherwise it may get clobbered by setproctitle()
+ */
+char *get_progname(char *argv0)
+{
+ char *retp;
+#ifdef HAVE___PROGNAME
+ extern char *__progname;
+
+ if ((retp = strdup(__progname)) == NULL)
+ err(1, NULL);
+#else
+ char *p;
+
+ if (argv0 == NULL)
+ return ("unknown"); /* XXX */
+ p = strrchr(argv0, '/');
+ if (p == NULL)
+ p = argv0;
+ else
+ p++;
+
+ if ((retp = strdup(p)) == NULL)
+ err(1, NULL);
+#endif
+ return retp;
+}
diff --git a/openbsd-compat/reallocarray.c b/openbsd-compat/reallocarray.c
new file mode 100644
index 00000000..9beec719
--- /dev/null
+++ b/openbsd-compat/reallocarray.c
@@ -0,0 +1,42 @@
+/* $OpenBSD: reallocarray.c,v 1.1 2014/05/08 21:43:49 deraadt Exp $ */
+/*
+ * Copyright (c) 2008 Otto Moerbeek <otto@drijf.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.
+ */
+
+/* OPENBSD ORIGINAL: lib/libc/stdlib/reallocarray.c */
+
+#include "includes.h"
+
+#include <sys/types.h>
+#include <errno.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+/*
+ * This is sqrt(SIZE_MAX+1), as s1*s2 <= SIZE_MAX
+ * if both s1 < MUL_NO_OVERFLOW and s2 < MUL_NO_OVERFLOW
+ */
+#define MUL_NO_OVERFLOW (1UL << (sizeof(size_t) * 4))
+
+void *
+reallocarray(void *optr, size_t nmemb, size_t size)
+{
+ if ((nmemb >= MUL_NO_OVERFLOW || size >= MUL_NO_OVERFLOW) &&
+ nmemb > 0 && SIZE_MAX / nmemb < size) {
+ errno = ENOMEM;
+ return NULL;
+ }
+ return realloc(optr, size * nmemb);
+}
diff --git a/openbsd-compat/recallocarray.c b/openbsd-compat/recallocarray.c
new file mode 100644
index 00000000..fc0b5a8a
--- /dev/null
+++ b/openbsd-compat/recallocarray.c
@@ -0,0 +1,84 @@
+/* $OpenBSD: recallocarray.c,v 1.1 2017/03/06 18:44:21 otto Exp $ */
+/*
+ * Copyright (c) 2008, 2017 Otto Moerbeek <otto@drijf.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.
+ */
+
+/* OPENBSD ORIGINAL: lib/libc/stdlib/recallocarray.c */
+
+#include "includes.h"
+
+#include <errno.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <unistd.h>
+
+/*
+ * This is sqrt(SIZE_MAX+1), as s1*s2 <= SIZE_MAX
+ * if both s1 < MUL_NO_OVERFLOW and s2 < MUL_NO_OVERFLOW
+ */
+#define MUL_NO_OVERFLOW ((size_t)1 << (sizeof(size_t) * 4))
+
+void *
+recallocarray(void *ptr, size_t oldnmemb, size_t newnmemb, size_t size)
+{
+ size_t oldsize, newsize;
+ void *newptr;
+
+ if (ptr == NULL)
+ return calloc(newnmemb, size);
+
+ if ((newnmemb >= MUL_NO_OVERFLOW || size >= MUL_NO_OVERFLOW) &&
+ newnmemb > 0 && SIZE_MAX / newnmemb < size) {
+ errno = ENOMEM;
+ return NULL;
+ }
+ newsize = newnmemb * size;
+
+ if ((oldnmemb >= MUL_NO_OVERFLOW || size >= MUL_NO_OVERFLOW) &&
+ oldnmemb > 0 && SIZE_MAX / oldnmemb < size) {
+ errno = EINVAL;
+ return NULL;
+ }
+ oldsize = oldnmemb * size;
+
+ /*
+ * Don't bother too much if we're shrinking just a bit,
+ * we do not shrink for series of small steps, oh well.
+ */
+ if (newsize <= oldsize) {
+ size_t d = oldsize - newsize;
+
+ if (d < oldsize / 2 && d < (size_t)getpagesize()) {
+ memset((char *)ptr + newsize, 0, d);
+ return ptr;
+ }
+ }
+
+ newptr = malloc(newsize);
+ if (newptr == NULL)
+ return NULL;
+
+ if (newsize > oldsize) {
+ memcpy(newptr, ptr, oldsize);
+ memset((char *)newptr + oldsize, 0, newsize - oldsize);
+ } else
+ memcpy(newptr, ptr, newsize);
+
+ explicit_bzero(ptr, oldsize);
+ free(ptr);
+
+ return newptr;
+}
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/setegid.c b/openbsd-compat/setegid.c
new file mode 100644
index 00000000..9e056351
--- /dev/null
+++ b/openbsd-compat/setegid.c
@@ -0,0 +1,39 @@
+
+/*
+ * Copyright (c) 1999-2004 Damien Miller <djm@mindrot.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>
+#ifdef HAVE_SYS_SELECT_H
+# include <sys/select.h>
+#endif
+#ifdef HAVE_SYS_TIME_H
+# include <sys/time.h>
+#endif
+
+#include <err.h>
+#include <string.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <time.h>
+#include <unistd.h>
+
+int
+setegid(uid_t egid)
+{
+ return(setresgid(-1, egid, -1));
+}
diff --git a/openbsd-compat/seteuid.c b/openbsd-compat/seteuid.c
new file mode 100644
index 00000000..6f581125
--- /dev/null
+++ b/openbsd-compat/seteuid.c
@@ -0,0 +1,39 @@
+
+/*
+ * Copyright (c) 1999-2004 Damien Miller <djm@mindrot.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>
+#ifdef HAVE_SYS_SELECT_H
+# include <sys/select.h>
+#endif
+#ifdef HAVE_SYS_TIME_H
+# include <sys/time.h>
+#endif
+
+#include <err.h>
+#include <string.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <time.h>
+#include <unistd.h>
+
+int
+seteuid(uid_t euid)
+{
+ return (setreuid(-1, euid));
+}
diff --git a/openbsd-compat/setproctitle.c b/openbsd-compat/setproctitle.c
new file mode 100644
index 00000000..71e1595e
--- /dev/null
+++ b/openbsd-compat/setproctitle.c
@@ -0,0 +1,164 @@
+/* Based on conf.c from UCB sendmail 8.8.8 */
+
+/*
+ * Copyright 2003 Damien Miller
+ * Copyright (c) 1983, 1995-1997 Eric P. Allman
+ * Copyright (c) 1988, 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.
+ */
+
+#include "includes.h"
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#ifdef HAVE_SYS_PSTAT_H
+#include <sys/pstat.h>
+#endif
+#include <string.h>
+
+#if defined(HAVE_VIS_H) && !defined(BROKEN_STRNVIS)
+#include <vis.h>
+#else
+#include "bsd-vis.h"
+#endif
+
+#define SPT_NONE 0 /* don't use it at all */
+#define SPT_PSTAT 1 /* use pstat(PSTAT_SETCMD, ...) */
+#define SPT_REUSEARGV 2 /* cover argv with title information */
+
+#ifndef SPT_TYPE
+# define SPT_TYPE SPT_NONE
+#endif
+
+#ifndef SPT_PADCHAR
+# define SPT_PADCHAR '\0'
+#endif
+
+#if SPT_TYPE == SPT_REUSEARGV
+static char *argv_start = NULL;
+static size_t argv_env_len = 0;
+#endif
+
+void
+compat_init_setproctitle(int argc, char *argv[])
+{
+#if defined(SPT_TYPE) && SPT_TYPE == SPT_REUSEARGV
+ extern char **environ;
+ char *lastargv = NULL;
+ char **envp = environ;
+ int i;
+
+ /*
+ * NB: This assumes that argv has already been copied out of the
+ * way. This is true for sshd, but may not be true for other
+ * programs. Beware.
+ */
+
+ if (argc == 0 || argv[0] == NULL)
+ return;
+
+ /* Fail if we can't allocate room for the new environment */
+ for (i = 0; envp[i] != NULL; i++)
+ ;
+ if ((environ = calloc(i + 1, sizeof(*environ))) == NULL) {
+ environ = envp; /* put it back */
+ return;
+ }
+
+ /*
+ * Find the last argv string or environment variable within
+ * our process memory area.
+ */
+ for (i = 0; i < argc; i++) {
+ if (lastargv == NULL || lastargv + 1 == argv[i])
+ lastargv = argv[i] + strlen(argv[i]);
+ }
+ for (i = 0; envp[i] != NULL; i++) {
+ if (lastargv + 1 == envp[i])
+ lastargv = envp[i] + strlen(envp[i]);
+ }
+
+ argv[1] = NULL;
+ argv_start = argv[0];
+ argv_env_len = lastargv - argv[0] - 1;
+
+ /*
+ * Copy environment
+ * XXX - will truncate env on strdup fail
+ */
+ for (i = 0; envp[i] != NULL; i++)
+ environ[i] = strdup(envp[i]);
+ environ[i] = NULL;
+#endif /* SPT_REUSEARGV */
+}
+
+void
+setproctitle(const char *fmt, ...)
+{
+#if SPT_TYPE != SPT_NONE
+ va_list ap;
+ char buf[1024], ptitle[1024];
+ size_t len;
+ int r;
+ extern char *__progname;
+#if SPT_TYPE == SPT_PSTAT
+ union pstun pst;
+#endif
+
+#if SPT_TYPE == SPT_REUSEARGV
+ if (argv_env_len <= 0)
+ return;
+#endif
+
+ strlcpy(buf, __progname, sizeof(buf));
+
+ r = -1;
+ va_start(ap, fmt);
+ if (fmt != NULL) {
+ len = strlcat(buf, ": ", sizeof(buf));
+ if (len < sizeof(buf))
+ r = vsnprintf(buf + len, sizeof(buf) - len , fmt, ap);
+ }
+ va_end(ap);
+ if (r == -1 || (size_t)r >= sizeof(buf) - len)
+ return;
+ strnvis(ptitle, buf, sizeof(ptitle),
+ VIS_CSTYLE|VIS_NL|VIS_TAB|VIS_OCTAL);
+
+#if SPT_TYPE == SPT_PSTAT
+ pst.pst_command = ptitle;
+ pstat(PSTAT_SETCMD, pst, strlen(ptitle), 0, 0);
+#elif SPT_TYPE == SPT_REUSEARGV
+ len = strlcpy(argv_start, ptitle, argv_env_len);
+ for(; len < argv_env_len; len++)
+ argv_start[len] = SPT_PADCHAR;
+#endif
+
+#endif /* SPT_NONE */
+}
diff --git a/openbsd-compat/setresgid.c b/openbsd-compat/setresgid.c
new file mode 100644
index 00000000..e798cdad
--- /dev/null
+++ b/openbsd-compat/setresgid.c
@@ -0,0 +1,41 @@
+/* Subset of uidswap.c from portable OpenSSH */
+
+/* $OpenBSD: uidswap.c,v 1.35 2006/08/03 03:34:42 deraadt Exp $ */
+/*
+ * Author: Tatu Ylonen <ylo@cs.hut.fi>
+ * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
+ * All rights reserved
+ * Code for uid-swapping.
+ *
+ * As far as I am concerned, the code I have written for this software
+ * can be used freely for any purpose. Any derived versions of this
+ * software must be clearly marked as such, and if the derived work is
+ * incompatible with the protocol description in the RFC file, it must be
+ * called by a name other than "ssh" or "Secure Shell".
+ */
+
+#include "includes.h"
+
+#include <stdarg.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "log.h"
+
+int setresgid(uid_t rgid, uid_t egid, uid_t sgid)
+{
+
+#if defined(HAVE_SETRESGID) && !defined(BROKEN_SETRESGID)
+ if (setresgid(rgid, egid, sgid) < 0)
+ fatal("setresgid %u: %.100s", (u_int)rgid, strerror(errno));
+#elif defined(HAVE_SETREGID) && !defined(BROKEN_SETREGID)
+ if (setregid(rgid, egid) < 0)
+ fatal("setregid %u: %.100s", (u_int)rgid, strerror(errno));
+#else
+ if (setegid(egid) < 0)
+ fatal("setegid %u: %.100s", (u_int)egid, strerror(errno));
+ if (setgid(rgid) < 0)
+ fatal("setgid %u: %.100s", (u_int)rgid, strerror(errno));
+#endif
+ return (0);
+}
diff --git a/openbsd-compat/setresuid.c b/openbsd-compat/setresuid.c
new file mode 100644
index 00000000..36df39e9
--- /dev/null
+++ b/openbsd-compat/setresuid.c
@@ -0,0 +1,43 @@
+/* Subset of uidswap.c from portable OpenSSH */
+
+/* $OpenBSD: uidswap.c,v 1.35 2006/08/03 03:34:42 deraadt Exp $ */
+/*
+ * Author: Tatu Ylonen <ylo@cs.hut.fi>
+ * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
+ * All rights reserved
+ * Code for uid-swapping.
+ *
+ * As far as I am concerned, the code I have written for this software
+ * can be used freely for any purpose. Any derived versions of this
+ * software must be clearly marked as such, and if the derived work is
+ * incompatible with the protocol description in the RFC file, it must be
+ * called by a name other than "ssh" or "Secure Shell".
+ */
+
+#include "includes.h"
+
+#include <stdarg.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "log.h"
+
+int setresuid(uid_t ruid, uid_t euid, uid_t suid)
+{
+
+#if defined(HAVE_SETRESUID) && !defined(BROKEN_SETRESUID)
+ if (setresuid(ruid, euid, suid) < 0)
+ fatal("setresuid %u: %.100s", (u_int)ruid, strerror(errno));
+#elif defined(HAVE_SETREUID) && !defined(BROKEN_SETREUID)
+ if (setreuid(ruid, euid) < 0)
+ fatal("setreuid %u: %.100s", (u_int)ruid, strerror(errno));
+#else
+# ifndef SETEUID_BREAKS_SETUID
+ if (seteuid(euid) < 0)
+ fatal("seteuid %u: %.100s", (u_int)euid, strerror(errno));
+# endif
+ if (setuid(ruid) < 0)
+ fatal("setuid %u: %.100s", (u_int)ruid, strerror(errno));
+#endif
+ return (0);
+}
diff --git a/openbsd-compat/signal.c b/openbsd-compat/signal.c
new file mode 100644
index 00000000..cdae0cd2
--- /dev/null
+++ b/openbsd-compat/signal.c
@@ -0,0 +1,60 @@
+
+/*
+ * Copyright (c) 1999-2004 Damien Miller <djm@mindrot.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>
+#ifdef HAVE_SYS_SELECT_H
+# include <sys/select.h>
+#endif
+#ifdef HAVE_SYS_TIME_H
+# include <sys/time.h>
+#endif
+
+#include <err.h>
+#include <string.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <time.h>
+#include <unistd.h>
+
+mysig_t
+mysignal(int sig, mysig_t act)
+{
+#ifdef HAVE_SIGACTION
+ struct sigaction sa, osa;
+
+ if (sigaction(sig, NULL, &osa) == -1)
+ return (mysig_t) -1;
+ if (osa.sa_handler != act) {
+ memset(&sa, 0, sizeof(sa));
+ sigemptyset(&sa.sa_mask);
+ sa.sa_flags = 0;
+#ifdef SA_INTERRUPT
+ if (sig == SIGALRM)
+ sa.sa_flags |= SA_INTERRUPT;
+#endif
+ sa.sa_handler = act;
+ if (sigaction(sig, &sa, NULL) == -1)
+ return (mysig_t) -1;
+ }
+ return (osa.sa_handler);
+#else
+ #undef signal
+ return (signal(sig, act));
+#endif
+}
diff --git a/openbsd-compat/strerror.c b/openbsd-compat/strerror.c
new file mode 100644
index 00000000..0ddad2d4
--- /dev/null
+++ b/openbsd-compat/strerror.c
@@ -0,0 +1,44 @@
+
+/*
+ * Copyright (c) 1999-2004 Damien Miller <djm@mindrot.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>
+#ifdef HAVE_SYS_SELECT_H
+# include <sys/select.h>
+#endif
+#ifdef HAVE_SYS_TIME_H
+# include <sys/time.h>
+#endif
+
+#include <err.h>
+#include <string.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <time.h>
+#include <unistd.h>
+
+const char *strerror(int e)
+{
+ extern int sys_nerr;
+ extern char *sys_errlist[];
+
+ if ((e >= 0) && (e < sys_nerr))
+ return (sys_errlist[e]);
+
+ return ("unlisted error");
+}
diff --git a/openbsd-compat/strlcat.c b/openbsd-compat/strlcat.c
new file mode 100644
index 00000000..979eb876
--- /dev/null
+++ b/openbsd-compat/strlcat.c
@@ -0,0 +1,59 @@
+/* $OpenBSD: strlcat.c,v 1.13 2005/08/08 08:05:37 espie Exp $ */
+
+/*
+ * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
+ *
+ * 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.
+ */
+
+/* OPENBSD ORIGINAL: lib/libc/string/strlcat.c */
+
+#include "includes.h"
+
+#include <sys/types.h>
+#include <string.h>
+
+/*
+ * Appends src to string dst of size siz (unlike strncat, siz is the
+ * full size of dst, not space left). At most siz-1 characters
+ * will be copied. Always NUL terminates (unless siz <= strlen(dst)).
+ * Returns strlen(src) + MIN(siz, strlen(initial dst)).
+ * If retval >= siz, truncation occurred.
+ */
+size_t
+strlcat(char *dst, const char *src, size_t siz)
+{
+ char *d = dst;
+ const char *s = src;
+ size_t n = siz;
+ size_t dlen;
+
+ /* Find the end of dst and adjust bytes left but don't go past end */
+ while (n-- != 0 && *d != '\0')
+ d++;
+ dlen = d - dst;
+ n = siz - dlen;
+
+ if (n == 0)
+ return(dlen + strlen(s));
+ while (*s != '\0') {
+ if (n != 1) {
+ *d++ = *s;
+ n--;
+ }
+ s++;
+ }
+ *d = '\0';
+
+ return(dlen + (s - src)); /* count does not include NUL */
+}
diff --git a/openbsd-compat/strlcpy.c b/openbsd-compat/strlcpy.c
new file mode 100644
index 00000000..2562ebbf
--- /dev/null
+++ b/openbsd-compat/strlcpy.c
@@ -0,0 +1,55 @@
+/* $OpenBSD: strlcpy.c,v 1.11 2006/05/05 15:27:38 millert Exp $ */
+
+/*
+ * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
+ *
+ * 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.
+ */
+
+/* OPENBSD ORIGINAL: lib/libc/string/strlcpy.c */
+
+#include "includes.h"
+
+#include <sys/types.h>
+#include <string.h>
+
+/*
+ * Copy src to string dst of size siz. At most siz-1 characters
+ * will be copied. Always NUL terminates (unless siz == 0).
+ * Returns strlen(src); if retval >= siz, truncation occurred.
+ */
+size_t
+strlcpy(char *dst, const char *src, size_t siz)
+{
+ char *d = dst;
+ const char *s = src;
+ size_t n = siz;
+
+ /* Copy as many bytes as will fit */
+ if (n != 0) {
+ while (--n != 0) {
+ if ((*d++ = *s++) == '\0')
+ break;
+ }
+ }
+
+ /* Not enough room in dst, add NUL and traverse rest of src */
+ if (n == 0) {
+ if (siz != 0)
+ *d = '\0'; /* NUL-terminate dst */
+ while (*s++)
+ ;
+ }
+
+ return(s - src - 1); /* count does not include NUL */
+}
diff --git a/openbsd-compat/strmode.c b/openbsd-compat/strmode.c
new file mode 100644
index 00000000..2fc2bb0e
--- /dev/null
+++ b/openbsd-compat/strmode.c
@@ -0,0 +1,146 @@
+/* $OpenBSD: strmode.c,v 1.7 2005/08/08 08:05:37 espie 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.
+ */
+
+/* OPENBSD ORIGINAL: lib/libc/string/strmode.c */
+
+#include "includes.h"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <string.h>
+
+/* XXX mode should be mode_t */
+
+void
+strmode(int mode, char *p)
+{
+ /* print type */
+ switch (mode & S_IFMT) {
+ case S_IFDIR: /* directory */
+ *p++ = 'd';
+ break;
+ case S_IFCHR: /* character special */
+ *p++ = 'c';
+ break;
+ case S_IFBLK: /* block special */
+ *p++ = 'b';
+ break;
+ case S_IFREG: /* regular */
+ *p++ = '-';
+ break;
+ case S_IFLNK: /* symbolic link */
+ *p++ = 'l';
+ break;
+#ifdef S_IFSOCK
+ case S_IFSOCK: /* socket */
+ *p++ = 's';
+ break;
+#endif
+#ifdef S_IFIFO
+ case S_IFIFO: /* fifo */
+ *p++ = 'p';
+ break;
+#endif
+ default: /* unknown */
+ *p++ = '?';
+ break;
+ }
+ /* usr */
+ if (mode & S_IRUSR)
+ *p++ = 'r';
+ else
+ *p++ = '-';
+ if (mode & S_IWUSR)
+ *p++ = 'w';
+ else
+ *p++ = '-';
+ switch (mode & (S_IXUSR | S_ISUID)) {
+ case 0:
+ *p++ = '-';
+ break;
+ case S_IXUSR:
+ *p++ = 'x';
+ break;
+ case S_ISUID:
+ *p++ = 'S';
+ break;
+ case S_IXUSR | S_ISUID:
+ *p++ = 's';
+ break;
+ }
+ /* group */
+ if (mode & S_IRGRP)
+ *p++ = 'r';
+ else
+ *p++ = '-';
+ if (mode & S_IWGRP)
+ *p++ = 'w';
+ else
+ *p++ = '-';
+ switch (mode & (S_IXGRP | S_ISGID)) {
+ case 0:
+ *p++ = '-';
+ break;
+ case S_IXGRP:
+ *p++ = 'x';
+ break;
+ case S_ISGID:
+ *p++ = 'S';
+ break;
+ case S_IXGRP | S_ISGID:
+ *p++ = 's';
+ break;
+ }
+ /* other */
+ if (mode & S_IROTH)
+ *p++ = 'r';
+ else
+ *p++ = '-';
+ if (mode & S_IWOTH)
+ *p++ = 'w';
+ else
+ *p++ = '-';
+ switch (mode & (S_IXOTH | S_ISVTX)) {
+ case 0:
+ *p++ = '-';
+ break;
+ case S_IXOTH:
+ *p++ = 'x';
+ break;
+ case S_ISVTX:
+ *p++ = 'T';
+ break;
+ case S_IXOTH | S_ISVTX:
+ *p++ = 't';
+ break;
+ }
+ *p++ = ' '; /* will be a '+' if ACL's implemented */
+ *p = '\0';
+}
diff --git a/openbsd-compat/strndup.c b/openbsd-compat/strndup.c
new file mode 100644
index 00000000..f43ba659
--- /dev/null
+++ b/openbsd-compat/strndup.c
@@ -0,0 +1,39 @@
+/* $OpenBSD: strndup.c,v 1.2 2015/08/31 02:53:57 guenther Exp $ */
+
+/*
+ * Copyright (c) 2010 Todd C. Miller <Todd.Miller@courtesan.com>
+ *
+ * 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 <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+
+char *
+strndup(const char *str, size_t maxlen)
+{
+ char *copy;
+ size_t len;
+
+ len = strnlen(str, maxlen);
+ copy = malloc(len + 1);
+ if (copy != NULL) {
+ (void)memcpy(copy, str, len);
+ copy[len] = '\0';
+ }
+
+ return copy;
+}
diff --git a/openbsd-compat/strnlen.c b/openbsd-compat/strnlen.c
new file mode 100644
index 00000000..a2017e19
--- /dev/null
+++ b/openbsd-compat/strnlen.c
@@ -0,0 +1,32 @@
+/* $OpenBSD: strnlen.c,v 1.8 2016/10/16 17:37:39 dtucker Exp $ */
+
+/*
+ * Copyright (c) 2010 Todd C. Miller <Todd.Miller@courtesan.com>
+ *
+ * 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 <string.h>
+
+size_t
+strnlen(const char *str, size_t maxlen)
+{
+ const char *cp;
+
+ for (cp = str; maxlen != 0 && *cp != '\0'; cp++, maxlen--)
+ ;
+
+ return (size_t)(cp - str);
+}
diff --git a/openbsd-compat/strsep.c b/openbsd-compat/strsep.c
new file mode 100644
index 00000000..b36eb8fd
--- /dev/null
+++ b/openbsd-compat/strsep.c
@@ -0,0 +1,79 @@
+/* $OpenBSD: strsep.c,v 1.6 2005/08/08 08:05:37 espie Exp $ */
+
+/*-
+ * Copyright (c) 1990, 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.
+ */
+
+/* OPENBSD ORIGINAL: lib/libc/string/strsep.c */
+
+#include "includes.h"
+
+#if !defined(HAVE_STRSEP)
+
+#include <string.h>
+#include <stdio.h>
+
+/*
+ * Get next token from string *stringp, where tokens are possibly-empty
+ * strings separated by characters from delim.
+ *
+ * Writes NULs into the string at *stringp to end tokens.
+ * delim need not remain constant from call to call.
+ * On return, *stringp points past the last NUL written (if there might
+ * be further tokens), or is NULL (if there are definitely no more tokens).
+ *
+ * If *stringp is NULL, strsep returns NULL.
+ */
+char *
+strsep(char **stringp, const char *delim)
+{
+ char *s;
+ const char *spanp;
+ int c, sc;
+ char *tok;
+
+ if ((s = *stringp) == NULL)
+ return (NULL);
+ for (tok = s;;) {
+ c = *s++;
+ spanp = delim;
+ do {
+ if ((sc = *spanp++) == c) {
+ if (c == 0)
+ s = NULL;
+ else
+ s[-1] = 0;
+ *stringp = s;
+ return (tok);
+ }
+ } while (sc != 0);
+ }
+ /* NOTREACHED */
+}
+
+#endif /* !defined(HAVE_STRSEP) */
diff --git a/openbsd-compat/strtonum.c b/openbsd-compat/strtonum.c
new file mode 100644
index 00000000..87f2f24b
--- /dev/null
+++ b/openbsd-compat/strtonum.c
@@ -0,0 +1,72 @@
+/* $OpenBSD: strtonum.c,v 1.6 2004/08/03 19:38:01 millert Exp $ */
+
+/*
+ * Copyright (c) 2004 Ted Unangst and Todd Miller
+ * 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.
+ */
+
+/* OPENBSD ORIGINAL: lib/libc/stdlib/strtonum.c */
+
+#include "includes.h"
+
+#ifndef HAVE_STRTONUM
+#include <stdlib.h>
+#include <limits.h>
+#include <errno.h>
+
+#define INVALID 1
+#define TOOSMALL 2
+#define TOOLARGE 3
+
+long long
+strtonum(const char *numstr, long long minval, long long maxval,
+ const char **errstrp)
+{
+ long long ll = 0;
+ char *ep;
+ int error = 0;
+ struct errval {
+ const char *errstr;
+ int err;
+ } ev[4] = {
+ { NULL, 0 },
+ { "invalid", EINVAL },
+ { "too small", ERANGE },
+ { "too large", ERANGE },
+ };
+
+ ev[0].err = errno;
+ errno = 0;
+ if (minval > maxval)
+ error = INVALID;
+ else {
+ ll = strtoll(numstr, &ep, 10);
+ if (numstr == ep || *ep != '\0')
+ error = INVALID;
+ else if ((ll == LLONG_MIN && errno == ERANGE) || ll < minval)
+ error = TOOSMALL;
+ else if ((ll == LLONG_MAX && errno == ERANGE) || ll > maxval)
+ error = TOOLARGE;
+ }
+ if (errstrp != NULL)
+ *errstrp = ev[error].errstr;
+ errno = ev[error].err;
+ if (error)
+ ll = 0;
+
+ return (ll);
+}
+
+#endif /* HAVE_STRTONUM */
diff --git a/openbsd-compat/sys/queue.h b/openbsd-compat/sys/queue.h
new file mode 100644
index 00000000..28aaaa37
--- /dev/null
+++ b/openbsd-compat/sys/queue.h
@@ -0,0 +1,653 @@
+/* $OpenBSD: queue.h,v 1.36 2012/04/11 13:29:14 naddy Exp $ */
+/* $NetBSD: queue.h,v 1.11 1996/05/16 05:17:14 mycroft Exp $ */
+
+/*
+ * Copyright (c) 1991, 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.
+ *
+ * @(#)queue.h 8.5 (Berkeley) 8/20/94
+ */
+
+/* OPENBSD ORIGINAL: sys/sys/queue.h */
+
+#ifndef _FAKE_QUEUE_H_
+#define _FAKE_QUEUE_H_
+
+/*
+ * Require for OS/X and other platforms that have old/broken/incomplete
+ * <sys/queue.h>.
+ */
+#undef SLIST_HEAD
+#undef SLIST_HEAD_INITIALIZER
+#undef SLIST_ENTRY
+#undef SLIST_FOREACH_PREVPTR
+#undef SLIST_FIRST
+#undef SLIST_END
+#undef SLIST_EMPTY
+#undef SLIST_NEXT
+#undef SLIST_FOREACH
+#undef SLIST_INIT
+#undef SLIST_INSERT_AFTER
+#undef SLIST_INSERT_HEAD
+#undef SLIST_REMOVE_HEAD
+#undef SLIST_REMOVE
+#undef SLIST_REMOVE_NEXT
+#undef LIST_HEAD
+#undef LIST_HEAD_INITIALIZER
+#undef LIST_ENTRY
+#undef LIST_FIRST
+#undef LIST_END
+#undef LIST_EMPTY
+#undef LIST_NEXT
+#undef LIST_FOREACH
+#undef LIST_INIT
+#undef LIST_INSERT_AFTER
+#undef LIST_INSERT_BEFORE
+#undef LIST_INSERT_HEAD
+#undef LIST_REMOVE
+#undef LIST_REPLACE
+#undef SIMPLEQ_HEAD
+#undef SIMPLEQ_HEAD_INITIALIZER
+#undef SIMPLEQ_ENTRY
+#undef SIMPLEQ_FIRST
+#undef SIMPLEQ_END
+#undef SIMPLEQ_EMPTY
+#undef SIMPLEQ_NEXT
+#undef SIMPLEQ_FOREACH
+#undef SIMPLEQ_INIT
+#undef SIMPLEQ_INSERT_HEAD
+#undef SIMPLEQ_INSERT_TAIL
+#undef SIMPLEQ_INSERT_AFTER
+#undef SIMPLEQ_REMOVE_HEAD
+#undef TAILQ_HEAD
+#undef TAILQ_HEAD_INITIALIZER
+#undef TAILQ_ENTRY
+#undef TAILQ_FIRST
+#undef TAILQ_END
+#undef TAILQ_NEXT
+#undef TAILQ_LAST
+#undef TAILQ_PREV
+#undef TAILQ_EMPTY
+#undef TAILQ_FOREACH
+#undef TAILQ_FOREACH_REVERSE
+#undef TAILQ_INIT
+#undef TAILQ_INSERT_HEAD
+#undef TAILQ_INSERT_TAIL
+#undef TAILQ_INSERT_AFTER
+#undef TAILQ_INSERT_BEFORE
+#undef TAILQ_REMOVE
+#undef TAILQ_REPLACE
+#undef CIRCLEQ_HEAD
+#undef CIRCLEQ_HEAD_INITIALIZER
+#undef CIRCLEQ_ENTRY
+#undef CIRCLEQ_FIRST
+#undef CIRCLEQ_LAST
+#undef CIRCLEQ_END
+#undef CIRCLEQ_NEXT
+#undef CIRCLEQ_PREV
+#undef CIRCLEQ_EMPTY
+#undef CIRCLEQ_FOREACH
+#undef CIRCLEQ_FOREACH_REVERSE
+#undef CIRCLEQ_INIT
+#undef CIRCLEQ_INSERT_AFTER
+#undef CIRCLEQ_INSERT_BEFORE
+#undef CIRCLEQ_INSERT_HEAD
+#undef CIRCLEQ_INSERT_TAIL
+#undef CIRCLEQ_REMOVE
+#undef CIRCLEQ_REPLACE
+
+/*
+ * This file defines five types of data structures: singly-linked lists,
+ * lists, simple queues, tail queues, and circular queues.
+ *
+ *
+ * A singly-linked list is headed by a single forward pointer. The elements
+ * are singly linked for minimum space and pointer manipulation overhead at
+ * the expense of O(n) removal for arbitrary elements. New elements can be
+ * added to the list after an existing element or at the head of the list.
+ * Elements being removed from the head of the list should use the explicit
+ * macro for this purpose for optimum efficiency. A singly-linked list may
+ * only be traversed in the forward direction. Singly-linked lists are ideal
+ * for applications with large datasets and few or no removals or for
+ * implementing a LIFO queue.
+ *
+ * A list is headed by a single forward pointer (or an array of forward
+ * pointers for a hash table header). The elements are doubly linked
+ * so that an arbitrary element can be removed without a need to
+ * traverse the list. New elements can be added to the list before
+ * or after an existing element or at the head of the list. A list
+ * may only be traversed in the forward direction.
+ *
+ * A simple queue is headed by a pair of pointers, one the head of the
+ * list and the other to the tail of the list. The elements are singly
+ * linked to save space, so elements can only be removed from the
+ * head of the list. New elements can be added to the list before or after
+ * an existing element, at the head of the list, or at the end of the
+ * list. A simple queue may only be traversed in the forward direction.
+ *
+ * A tail queue is headed by a pair of pointers, one to the head of the
+ * list and the other to the tail of the list. The elements are doubly
+ * linked so that an arbitrary element can be removed without a need to
+ * traverse the list. New elements can be added to the list before or
+ * after an existing element, at the head of the list, or at the end of
+ * the list. A tail queue may be traversed in either direction.
+ *
+ * A circle queue is headed by a pair of pointers, one to the head of the
+ * list and the other to the tail of the list. The elements are doubly
+ * linked so that an arbitrary element can be removed without a need to
+ * traverse the list. New elements can be added to the list before or after
+ * an existing element, at the head of the list, or at the end of the list.
+ * A circle queue may be traversed in either direction, but has a more
+ * complex end of list detection.
+ *
+ * For details on the use of these macros, see the queue(3) manual page.
+ */
+
+#if defined(QUEUE_MACRO_DEBUG) || (defined(_KERNEL) && defined(DIAGNOSTIC))
+#define _Q_INVALIDATE(a) (a) = ((void *)-1)
+#else
+#define _Q_INVALIDATE(a)
+#endif
+
+/*
+ * Singly-linked List definitions.
+ */
+#define SLIST_HEAD(name, type) \
+struct name { \
+ struct type *slh_first; /* first element */ \
+}
+
+#define SLIST_HEAD_INITIALIZER(head) \
+ { NULL }
+
+#define SLIST_ENTRY(type) \
+struct { \
+ struct type *sle_next; /* next element */ \
+}
+
+/*
+ * Singly-linked List access methods.
+ */
+#define SLIST_FIRST(head) ((head)->slh_first)
+#define SLIST_END(head) NULL
+#define SLIST_EMPTY(head) (SLIST_FIRST(head) == SLIST_END(head))
+#define SLIST_NEXT(elm, field) ((elm)->field.sle_next)
+
+#define SLIST_FOREACH(var, head, field) \
+ for((var) = SLIST_FIRST(head); \
+ (var) != SLIST_END(head); \
+ (var) = SLIST_NEXT(var, field))
+
+#define SLIST_FOREACH_SAFE(var, head, field, tvar) \
+ for ((var) = SLIST_FIRST(head); \
+ (var) && ((tvar) = SLIST_NEXT(var, field), 1); \
+ (var) = (tvar))
+
+/*
+ * Singly-linked List functions.
+ */
+#define SLIST_INIT(head) { \
+ SLIST_FIRST(head) = SLIST_END(head); \
+}
+
+#define SLIST_INSERT_AFTER(slistelm, elm, field) do { \
+ (elm)->field.sle_next = (slistelm)->field.sle_next; \
+ (slistelm)->field.sle_next = (elm); \
+} while (0)
+
+#define SLIST_INSERT_HEAD(head, elm, field) do { \
+ (elm)->field.sle_next = (head)->slh_first; \
+ (head)->slh_first = (elm); \
+} while (0)
+
+#define SLIST_REMOVE_AFTER(elm, field) do { \
+ (elm)->field.sle_next = (elm)->field.sle_next->field.sle_next; \
+} while (0)
+
+#define SLIST_REMOVE_HEAD(head, field) do { \
+ (head)->slh_first = (head)->slh_first->field.sle_next; \
+} while (0)
+
+#define SLIST_REMOVE(head, elm, type, field) do { \
+ if ((head)->slh_first == (elm)) { \
+ SLIST_REMOVE_HEAD((head), field); \
+ } else { \
+ struct type *curelm = (head)->slh_first; \
+ \
+ while (curelm->field.sle_next != (elm)) \
+ curelm = curelm->field.sle_next; \
+ curelm->field.sle_next = \
+ curelm->field.sle_next->field.sle_next; \
+ _Q_INVALIDATE((elm)->field.sle_next); \
+ } \
+} while (0)
+
+/*
+ * List definitions.
+ */
+#define LIST_HEAD(name, type) \
+struct name { \
+ struct type *lh_first; /* first element */ \
+}
+
+#define LIST_HEAD_INITIALIZER(head) \
+ { NULL }
+
+#define LIST_ENTRY(type) \
+struct { \
+ struct type *le_next; /* next element */ \
+ struct type **le_prev; /* address of previous next element */ \
+}
+
+/*
+ * List access methods
+ */
+#define LIST_FIRST(head) ((head)->lh_first)
+#define LIST_END(head) NULL
+#define LIST_EMPTY(head) (LIST_FIRST(head) == LIST_END(head))
+#define LIST_NEXT(elm, field) ((elm)->field.le_next)
+
+#define LIST_FOREACH(var, head, field) \
+ for((var) = LIST_FIRST(head); \
+ (var)!= LIST_END(head); \
+ (var) = LIST_NEXT(var, field))
+
+#define LIST_FOREACH_SAFE(var, head, field, tvar) \
+ for ((var) = LIST_FIRST(head); \
+ (var) && ((tvar) = LIST_NEXT(var, field), 1); \
+ (var) = (tvar))
+
+/*
+ * List functions.
+ */
+#define LIST_INIT(head) do { \
+ LIST_FIRST(head) = LIST_END(head); \
+} while (0)
+
+#define LIST_INSERT_AFTER(listelm, elm, field) do { \
+ if (((elm)->field.le_next = (listelm)->field.le_next) != NULL) \
+ (listelm)->field.le_next->field.le_prev = \
+ &(elm)->field.le_next; \
+ (listelm)->field.le_next = (elm); \
+ (elm)->field.le_prev = &(listelm)->field.le_next; \
+} while (0)
+
+#define LIST_INSERT_BEFORE(listelm, elm, field) do { \
+ (elm)->field.le_prev = (listelm)->field.le_prev; \
+ (elm)->field.le_next = (listelm); \
+ *(listelm)->field.le_prev = (elm); \
+ (listelm)->field.le_prev = &(elm)->field.le_next; \
+} while (0)
+
+#define LIST_INSERT_HEAD(head, elm, field) do { \
+ if (((elm)->field.le_next = (head)->lh_first) != NULL) \
+ (head)->lh_first->field.le_prev = &(elm)->field.le_next;\
+ (head)->lh_first = (elm); \
+ (elm)->field.le_prev = &(head)->lh_first; \
+} while (0)
+
+#define LIST_REMOVE(elm, field) do { \
+ if ((elm)->field.le_next != NULL) \
+ (elm)->field.le_next->field.le_prev = \
+ (elm)->field.le_prev; \
+ *(elm)->field.le_prev = (elm)->field.le_next; \
+ _Q_INVALIDATE((elm)->field.le_prev); \
+ _Q_INVALIDATE((elm)->field.le_next); \
+} while (0)
+
+#define LIST_REPLACE(elm, elm2, field) do { \
+ if (((elm2)->field.le_next = (elm)->field.le_next) != NULL) \
+ (elm2)->field.le_next->field.le_prev = \
+ &(elm2)->field.le_next; \
+ (elm2)->field.le_prev = (elm)->field.le_prev; \
+ *(elm2)->field.le_prev = (elm2); \
+ _Q_INVALIDATE((elm)->field.le_prev); \
+ _Q_INVALIDATE((elm)->field.le_next); \
+} while (0)
+
+/*
+ * Simple queue definitions.
+ */
+#define SIMPLEQ_HEAD(name, type) \
+struct name { \
+ struct type *sqh_first; /* first element */ \
+ struct type **sqh_last; /* addr of last next element */ \
+}
+
+#define SIMPLEQ_HEAD_INITIALIZER(head) \
+ { NULL, &(head).sqh_first }
+
+#define SIMPLEQ_ENTRY(type) \
+struct { \
+ struct type *sqe_next; /* next element */ \
+}
+
+/*
+ * Simple queue access methods.
+ */
+#define SIMPLEQ_FIRST(head) ((head)->sqh_first)
+#define SIMPLEQ_END(head) NULL
+#define SIMPLEQ_EMPTY(head) (SIMPLEQ_FIRST(head) == SIMPLEQ_END(head))
+#define SIMPLEQ_NEXT(elm, field) ((elm)->field.sqe_next)
+
+#define SIMPLEQ_FOREACH(var, head, field) \
+ for((var) = SIMPLEQ_FIRST(head); \
+ (var) != SIMPLEQ_END(head); \
+ (var) = SIMPLEQ_NEXT(var, field))
+
+#define SIMPLEQ_FOREACH_SAFE(var, head, field, tvar) \
+ for ((var) = SIMPLEQ_FIRST(head); \
+ (var) && ((tvar) = SIMPLEQ_NEXT(var, field), 1); \
+ (var) = (tvar))
+
+/*
+ * Simple queue functions.
+ */
+#define SIMPLEQ_INIT(head) do { \
+ (head)->sqh_first = NULL; \
+ (head)->sqh_last = &(head)->sqh_first; \
+} while (0)
+
+#define SIMPLEQ_INSERT_HEAD(head, elm, field) do { \
+ if (((elm)->field.sqe_next = (head)->sqh_first) == NULL) \
+ (head)->sqh_last = &(elm)->field.sqe_next; \
+ (head)->sqh_first = (elm); \
+} while (0)
+
+#define SIMPLEQ_INSERT_TAIL(head, elm, field) do { \
+ (elm)->field.sqe_next = NULL; \
+ *(head)->sqh_last = (elm); \
+ (head)->sqh_last = &(elm)->field.sqe_next; \
+} while (0)
+
+#define SIMPLEQ_INSERT_AFTER(head, listelm, elm, field) do { \
+ if (((elm)->field.sqe_next = (listelm)->field.sqe_next) == NULL)\
+ (head)->sqh_last = &(elm)->field.sqe_next; \
+ (listelm)->field.sqe_next = (elm); \
+} while (0)
+
+#define SIMPLEQ_REMOVE_HEAD(head, field) do { \
+ if (((head)->sqh_first = (head)->sqh_first->field.sqe_next) == NULL) \
+ (head)->sqh_last = &(head)->sqh_first; \
+} while (0)
+
+#define SIMPLEQ_REMOVE_AFTER(head, elm, field) do { \
+ if (((elm)->field.sqe_next = (elm)->field.sqe_next->field.sqe_next) \
+ == NULL) \
+ (head)->sqh_last = &(elm)->field.sqe_next; \
+} while (0)
+
+/*
+ * Tail queue definitions.
+ */
+#define TAILQ_HEAD(name, type) \
+struct name { \
+ struct type *tqh_first; /* first element */ \
+ struct type **tqh_last; /* addr of last next element */ \
+}
+
+#define TAILQ_HEAD_INITIALIZER(head) \
+ { NULL, &(head).tqh_first }
+
+#define TAILQ_ENTRY(type) \
+struct { \
+ struct type *tqe_next; /* next element */ \
+ struct type **tqe_prev; /* address of previous next element */ \
+}
+
+/*
+ * tail queue access methods
+ */
+#define TAILQ_FIRST(head) ((head)->tqh_first)
+#define TAILQ_END(head) NULL
+#define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next)
+#define TAILQ_LAST(head, headname) \
+ (*(((struct headname *)((head)->tqh_last))->tqh_last))
+/* XXX */
+#define TAILQ_PREV(elm, headname, field) \
+ (*(((struct headname *)((elm)->field.tqe_prev))->tqh_last))
+#define TAILQ_EMPTY(head) \
+ (TAILQ_FIRST(head) == TAILQ_END(head))
+
+#define TAILQ_FOREACH(var, head, field) \
+ for((var) = TAILQ_FIRST(head); \
+ (var) != TAILQ_END(head); \
+ (var) = TAILQ_NEXT(var, field))
+
+#define TAILQ_FOREACH_SAFE(var, head, field, tvar) \
+ for ((var) = TAILQ_FIRST(head); \
+ (var) != TAILQ_END(head) && \
+ ((tvar) = TAILQ_NEXT(var, field), 1); \
+ (var) = (tvar))
+
+
+#define TAILQ_FOREACH_REVERSE(var, head, headname, field) \
+ for((var) = TAILQ_LAST(head, headname); \
+ (var) != TAILQ_END(head); \
+ (var) = TAILQ_PREV(var, headname, field))
+
+#define TAILQ_FOREACH_REVERSE_SAFE(var, head, headname, field, tvar) \
+ for ((var) = TAILQ_LAST(head, headname); \
+ (var) != TAILQ_END(head) && \
+ ((tvar) = TAILQ_PREV(var, headname, field), 1); \
+ (var) = (tvar))
+
+/*
+ * Tail queue functions.
+ */
+#define TAILQ_INIT(head) do { \
+ (head)->tqh_first = NULL; \
+ (head)->tqh_last = &(head)->tqh_first; \
+} while (0)
+
+#define TAILQ_INSERT_HEAD(head, elm, field) do { \
+ if (((elm)->field.tqe_next = (head)->tqh_first) != NULL) \
+ (head)->tqh_first->field.tqe_prev = \
+ &(elm)->field.tqe_next; \
+ else \
+ (head)->tqh_last = &(elm)->field.tqe_next; \
+ (head)->tqh_first = (elm); \
+ (elm)->field.tqe_prev = &(head)->tqh_first; \
+} while (0)
+
+#define TAILQ_INSERT_TAIL(head, elm, field) do { \
+ (elm)->field.tqe_next = NULL; \
+ (elm)->field.tqe_prev = (head)->tqh_last; \
+ *(head)->tqh_last = (elm); \
+ (head)->tqh_last = &(elm)->field.tqe_next; \
+} while (0)
+
+#define TAILQ_INSERT_AFTER(head, listelm, elm, field) do { \
+ if (((elm)->field.tqe_next = (listelm)->field.tqe_next) != NULL)\
+ (elm)->field.tqe_next->field.tqe_prev = \
+ &(elm)->field.tqe_next; \
+ else \
+ (head)->tqh_last = &(elm)->field.tqe_next; \
+ (listelm)->field.tqe_next = (elm); \
+ (elm)->field.tqe_prev = &(listelm)->field.tqe_next; \
+} while (0)
+
+#define TAILQ_INSERT_BEFORE(listelm, elm, field) do { \
+ (elm)->field.tqe_prev = (listelm)->field.tqe_prev; \
+ (elm)->field.tqe_next = (listelm); \
+ *(listelm)->field.tqe_prev = (elm); \
+ (listelm)->field.tqe_prev = &(elm)->field.tqe_next; \
+} while (0)
+
+#define TAILQ_REMOVE(head, elm, field) do { \
+ if (((elm)->field.tqe_next) != NULL) \
+ (elm)->field.tqe_next->field.tqe_prev = \
+ (elm)->field.tqe_prev; \
+ else \
+ (head)->tqh_last = (elm)->field.tqe_prev; \
+ *(elm)->field.tqe_prev = (elm)->field.tqe_next; \
+ _Q_INVALIDATE((elm)->field.tqe_prev); \
+ _Q_INVALIDATE((elm)->field.tqe_next); \
+} while (0)
+
+#define TAILQ_REPLACE(head, elm, elm2, field) do { \
+ if (((elm2)->field.tqe_next = (elm)->field.tqe_next) != NULL) \
+ (elm2)->field.tqe_next->field.tqe_prev = \
+ &(elm2)->field.tqe_next; \
+ else \
+ (head)->tqh_last = &(elm2)->field.tqe_next; \
+ (elm2)->field.tqe_prev = (elm)->field.tqe_prev; \
+ *(elm2)->field.tqe_prev = (elm2); \
+ _Q_INVALIDATE((elm)->field.tqe_prev); \
+ _Q_INVALIDATE((elm)->field.tqe_next); \
+} while (0)
+
+/*
+ * Circular queue definitions.
+ */
+#define CIRCLEQ_HEAD(name, type) \
+struct name { \
+ struct type *cqh_first; /* first element */ \
+ struct type *cqh_last; /* last element */ \
+}
+
+#define CIRCLEQ_HEAD_INITIALIZER(head) \
+ { CIRCLEQ_END(&head), CIRCLEQ_END(&head) }
+
+#define CIRCLEQ_ENTRY(type) \
+struct { \
+ struct type *cqe_next; /* next element */ \
+ struct type *cqe_prev; /* previous element */ \
+}
+
+/*
+ * Circular queue access methods
+ */
+#define CIRCLEQ_FIRST(head) ((head)->cqh_first)
+#define CIRCLEQ_LAST(head) ((head)->cqh_last)
+#define CIRCLEQ_END(head) ((void *)(head))
+#define CIRCLEQ_NEXT(elm, field) ((elm)->field.cqe_next)
+#define CIRCLEQ_PREV(elm, field) ((elm)->field.cqe_prev)
+#define CIRCLEQ_EMPTY(head) \
+ (CIRCLEQ_FIRST(head) == CIRCLEQ_END(head))
+
+#define CIRCLEQ_FOREACH(var, head, field) \
+ for((var) = CIRCLEQ_FIRST(head); \
+ (var) != CIRCLEQ_END(head); \
+ (var) = CIRCLEQ_NEXT(var, field))
+
+#define CIRCLEQ_FOREACH_SAFE(var, head, field, tvar) \
+ for ((var) = CIRCLEQ_FIRST(head); \
+ (var) != CIRCLEQ_END(head) && \
+ ((tvar) = CIRCLEQ_NEXT(var, field), 1); \
+ (var) = (tvar))
+
+#define CIRCLEQ_FOREACH_REVERSE(var, head, field) \
+ for((var) = CIRCLEQ_LAST(head); \
+ (var) != CIRCLEQ_END(head); \
+ (var) = CIRCLEQ_PREV(var, field))
+
+#define CIRCLEQ_FOREACH_REVERSE_SAFE(var, head, headname, field, tvar) \
+ for ((var) = CIRCLEQ_LAST(head, headname); \
+ (var) != CIRCLEQ_END(head) && \
+ ((tvar) = CIRCLEQ_PREV(var, headname, field), 1); \
+ (var) = (tvar))
+
+/*
+ * Circular queue functions.
+ */
+#define CIRCLEQ_INIT(head) do { \
+ (head)->cqh_first = CIRCLEQ_END(head); \
+ (head)->cqh_last = CIRCLEQ_END(head); \
+} while (0)
+
+#define CIRCLEQ_INSERT_AFTER(head, listelm, elm, field) do { \
+ (elm)->field.cqe_next = (listelm)->field.cqe_next; \
+ (elm)->field.cqe_prev = (listelm); \
+ if ((listelm)->field.cqe_next == CIRCLEQ_END(head)) \
+ (head)->cqh_last = (elm); \
+ else \
+ (listelm)->field.cqe_next->field.cqe_prev = (elm); \
+ (listelm)->field.cqe_next = (elm); \
+} while (0)
+
+#define CIRCLEQ_INSERT_BEFORE(head, listelm, elm, field) do { \
+ (elm)->field.cqe_next = (listelm); \
+ (elm)->field.cqe_prev = (listelm)->field.cqe_prev; \
+ if ((listelm)->field.cqe_prev == CIRCLEQ_END(head)) \
+ (head)->cqh_first = (elm); \
+ else \
+ (listelm)->field.cqe_prev->field.cqe_next = (elm); \
+ (listelm)->field.cqe_prev = (elm); \
+} while (0)
+
+#define CIRCLEQ_INSERT_HEAD(head, elm, field) do { \
+ (elm)->field.cqe_next = (head)->cqh_first; \
+ (elm)->field.cqe_prev = CIRCLEQ_END(head); \
+ if ((head)->cqh_last == CIRCLEQ_END(head)) \
+ (head)->cqh_last = (elm); \
+ else \
+ (head)->cqh_first->field.cqe_prev = (elm); \
+ (head)->cqh_first = (elm); \
+} while (0)
+
+#define CIRCLEQ_INSERT_TAIL(head, elm, field) do { \
+ (elm)->field.cqe_next = CIRCLEQ_END(head); \
+ (elm)->field.cqe_prev = (head)->cqh_last; \
+ if ((head)->cqh_first == CIRCLEQ_END(head)) \
+ (head)->cqh_first = (elm); \
+ else \
+ (head)->cqh_last->field.cqe_next = (elm); \
+ (head)->cqh_last = (elm); \
+} while (0)
+
+#define CIRCLEQ_REMOVE(head, elm, field) do { \
+ if ((elm)->field.cqe_next == CIRCLEQ_END(head)) \
+ (head)->cqh_last = (elm)->field.cqe_prev; \
+ else \
+ (elm)->field.cqe_next->field.cqe_prev = \
+ (elm)->field.cqe_prev; \
+ if ((elm)->field.cqe_prev == CIRCLEQ_END(head)) \
+ (head)->cqh_first = (elm)->field.cqe_next; \
+ else \
+ (elm)->field.cqe_prev->field.cqe_next = \
+ (elm)->field.cqe_next; \
+ _Q_INVALIDATE((elm)->field.cqe_prev); \
+ _Q_INVALIDATE((elm)->field.cqe_next); \
+} while (0)
+
+#define CIRCLEQ_REPLACE(head, elm, elm2, field) do { \
+ if (((elm2)->field.cqe_next = (elm)->field.cqe_next) == \
+ CIRCLEQ_END(head)) \
+ (head).cqh_last = (elm2); \
+ else \
+ (elm2)->field.cqe_next->field.cqe_prev = (elm2); \
+ if (((elm2)->field.cqe_prev = (elm)->field.cqe_prev) == \
+ CIRCLEQ_END(head)) \
+ (head).cqh_first = (elm2); \
+ else \
+ (elm2)->field.cqe_prev->field.cqe_next = (elm2); \
+ _Q_INVALIDATE((elm)->field.cqe_prev); \
+ _Q_INVALIDATE((elm)->field.cqe_next); \
+} while (0)
+
+#endif /* !_FAKE_QUEUE_H_ */
diff --git a/openbsd-compat/sys/tree.h b/openbsd-compat/sys/tree.h
new file mode 100644
index 00000000..7f7546ec
--- /dev/null
+++ b/openbsd-compat/sys/tree.h
@@ -0,0 +1,755 @@
+/* $OpenBSD: tree.h,v 1.13 2011/07/09 00:19:45 pirofti Exp $ */
+/*
+ * Copyright 2002 Niels Provos <provos@citi.umich.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.
+ *
+ * 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.
+ */
+
+/* OPENBSD ORIGINAL: sys/sys/tree.h */
+
+#include "config.h"
+#ifdef NO_ATTRIBUTE_ON_RETURN_TYPE
+# define __attribute__(x)
+#endif
+
+#ifndef _SYS_TREE_H_
+#define _SYS_TREE_H_
+
+/*
+ * This file defines data structures for different types of trees:
+ * splay trees and red-black trees.
+ *
+ * A splay tree is a self-organizing data structure. Every operation
+ * on the tree causes a splay to happen. The splay moves the requested
+ * node to the root of the tree and partly rebalances it.
+ *
+ * This has the benefit that request locality causes faster lookups as
+ * the requested nodes move to the top of the tree. On the other hand,
+ * every lookup causes memory writes.
+ *
+ * The Balance Theorem bounds the total access time for m operations
+ * and n inserts on an initially empty tree as O((m + n)lg n). The
+ * amortized cost for a sequence of m accesses to a splay tree is O(lg n);
+ *
+ * A red-black tree is a binary search tree with the node color as an
+ * extra attribute. It fulfills a set of conditions:
+ * - every search path from the root to a leaf consists of the
+ * same number of black nodes,
+ * - each red node (except for the root) has a black parent,
+ * - each leaf node is black.
+ *
+ * Every operation on a red-black tree is bounded as O(lg n).
+ * The maximum height of a red-black tree is 2lg (n+1).
+ */
+
+#define SPLAY_HEAD(name, type) \
+struct name { \
+ struct type *sph_root; /* root of the tree */ \
+}
+
+#define SPLAY_INITIALIZER(root) \
+ { NULL }
+
+#define SPLAY_INIT(root) do { \
+ (root)->sph_root = NULL; \
+} while (0)
+
+#define SPLAY_ENTRY(type) \
+struct { \
+ struct type *spe_left; /* left element */ \
+ struct type *spe_right; /* right element */ \
+}
+
+#define SPLAY_LEFT(elm, field) (elm)->field.spe_left
+#define SPLAY_RIGHT(elm, field) (elm)->field.spe_right
+#define SPLAY_ROOT(head) (head)->sph_root
+#define SPLAY_EMPTY(head) (SPLAY_ROOT(head) == NULL)
+
+/* SPLAY_ROTATE_{LEFT,RIGHT} expect that tmp hold SPLAY_{RIGHT,LEFT} */
+#define SPLAY_ROTATE_RIGHT(head, tmp, field) do { \
+ SPLAY_LEFT((head)->sph_root, field) = SPLAY_RIGHT(tmp, field); \
+ SPLAY_RIGHT(tmp, field) = (head)->sph_root; \
+ (head)->sph_root = tmp; \
+} while (0)
+
+#define SPLAY_ROTATE_LEFT(head, tmp, field) do { \
+ SPLAY_RIGHT((head)->sph_root, field) = SPLAY_LEFT(tmp, field); \
+ SPLAY_LEFT(tmp, field) = (head)->sph_root; \
+ (head)->sph_root = tmp; \
+} while (0)
+
+#define SPLAY_LINKLEFT(head, tmp, field) do { \
+ SPLAY_LEFT(tmp, field) = (head)->sph_root; \
+ tmp = (head)->sph_root; \
+ (head)->sph_root = SPLAY_LEFT((head)->sph_root, field); \
+} while (0)
+
+#define SPLAY_LINKRIGHT(head, tmp, field) do { \
+ SPLAY_RIGHT(tmp, field) = (head)->sph_root; \
+ tmp = (head)->sph_root; \
+ (head)->sph_root = SPLAY_RIGHT((head)->sph_root, field); \
+} while (0)
+
+#define SPLAY_ASSEMBLE(head, node, left, right, field) do { \
+ SPLAY_RIGHT(left, field) = SPLAY_LEFT((head)->sph_root, field); \
+ SPLAY_LEFT(right, field) = SPLAY_RIGHT((head)->sph_root, field);\
+ SPLAY_LEFT((head)->sph_root, field) = SPLAY_RIGHT(node, field); \
+ SPLAY_RIGHT((head)->sph_root, field) = SPLAY_LEFT(node, field); \
+} while (0)
+
+/* Generates prototypes and inline functions */
+
+#define SPLAY_PROTOTYPE(name, type, field, cmp) \
+void name##_SPLAY(struct name *, struct type *); \
+void name##_SPLAY_MINMAX(struct name *, int); \
+struct type *name##_SPLAY_INSERT(struct name *, struct type *); \
+struct type *name##_SPLAY_REMOVE(struct name *, struct type *); \
+ \
+/* Finds the node with the same key as elm */ \
+static __inline struct type * \
+name##_SPLAY_FIND(struct name *head, struct type *elm) \
+{ \
+ if (SPLAY_EMPTY(head)) \
+ return(NULL); \
+ name##_SPLAY(head, elm); \
+ if ((cmp)(elm, (head)->sph_root) == 0) \
+ return (head->sph_root); \
+ return (NULL); \
+} \
+ \
+static __inline struct type * \
+name##_SPLAY_NEXT(struct name *head, struct type *elm) \
+{ \
+ name##_SPLAY(head, elm); \
+ if (SPLAY_RIGHT(elm, field) != NULL) { \
+ elm = SPLAY_RIGHT(elm, field); \
+ while (SPLAY_LEFT(elm, field) != NULL) { \
+ elm = SPLAY_LEFT(elm, field); \
+ } \
+ } else \
+ elm = NULL; \
+ return (elm); \
+} \
+ \
+static __inline struct type * \
+name##_SPLAY_MIN_MAX(struct name *head, int val) \
+{ \
+ name##_SPLAY_MINMAX(head, val); \
+ return (SPLAY_ROOT(head)); \
+}
+
+/* Main splay operation.
+ * Moves node close to the key of elm to top
+ */
+#define SPLAY_GENERATE(name, type, field, cmp) \
+struct type * \
+name##_SPLAY_INSERT(struct name *head, struct type *elm) \
+{ \
+ if (SPLAY_EMPTY(head)) { \
+ SPLAY_LEFT(elm, field) = SPLAY_RIGHT(elm, field) = NULL; \
+ } else { \
+ int __comp; \
+ name##_SPLAY(head, elm); \
+ __comp = (cmp)(elm, (head)->sph_root); \
+ if(__comp < 0) { \
+ SPLAY_LEFT(elm, field) = SPLAY_LEFT((head)->sph_root, field);\
+ SPLAY_RIGHT(elm, field) = (head)->sph_root; \
+ SPLAY_LEFT((head)->sph_root, field) = NULL; \
+ } else if (__comp > 0) { \
+ SPLAY_RIGHT(elm, field) = SPLAY_RIGHT((head)->sph_root, field);\
+ SPLAY_LEFT(elm, field) = (head)->sph_root; \
+ SPLAY_RIGHT((head)->sph_root, field) = NULL; \
+ } else \
+ return ((head)->sph_root); \
+ } \
+ (head)->sph_root = (elm); \
+ return (NULL); \
+} \
+ \
+struct type * \
+name##_SPLAY_REMOVE(struct name *head, struct type *elm) \
+{ \
+ struct type *__tmp; \
+ if (SPLAY_EMPTY(head)) \
+ return (NULL); \
+ name##_SPLAY(head, elm); \
+ if ((cmp)(elm, (head)->sph_root) == 0) { \
+ if (SPLAY_LEFT((head)->sph_root, field) == NULL) { \
+ (head)->sph_root = SPLAY_RIGHT((head)->sph_root, field);\
+ } else { \
+ __tmp = SPLAY_RIGHT((head)->sph_root, field); \
+ (head)->sph_root = SPLAY_LEFT((head)->sph_root, field);\
+ name##_SPLAY(head, elm); \
+ SPLAY_RIGHT((head)->sph_root, field) = __tmp; \
+ } \
+ return (elm); \
+ } \
+ return (NULL); \
+} \
+ \
+void \
+name##_SPLAY(struct name *head, struct type *elm) \
+{ \
+ struct type __node, *__left, *__right, *__tmp; \
+ int __comp; \
+\
+ SPLAY_LEFT(&__node, field) = SPLAY_RIGHT(&__node, field) = NULL;\
+ __left = __right = &__node; \
+\
+ while ((__comp = (cmp)(elm, (head)->sph_root))) { \
+ if (__comp < 0) { \
+ __tmp = SPLAY_LEFT((head)->sph_root, field); \
+ if (__tmp == NULL) \
+ break; \
+ if ((cmp)(elm, __tmp) < 0){ \
+ SPLAY_ROTATE_RIGHT(head, __tmp, field); \
+ if (SPLAY_LEFT((head)->sph_root, field) == NULL)\
+ break; \
+ } \
+ SPLAY_LINKLEFT(head, __right, field); \
+ } else if (__comp > 0) { \
+ __tmp = SPLAY_RIGHT((head)->sph_root, field); \
+ if (__tmp == NULL) \
+ break; \
+ if ((cmp)(elm, __tmp) > 0){ \
+ SPLAY_ROTATE_LEFT(head, __tmp, field); \
+ if (SPLAY_RIGHT((head)->sph_root, field) == NULL)\
+ break; \
+ } \
+ SPLAY_LINKRIGHT(head, __left, field); \
+ } \
+ } \
+ SPLAY_ASSEMBLE(head, &__node, __left, __right, field); \
+} \
+ \
+/* Splay with either the minimum or the maximum element \
+ * Used to find minimum or maximum element in tree. \
+ */ \
+void name##_SPLAY_MINMAX(struct name *head, int __comp) \
+{ \
+ struct type __node, *__left, *__right, *__tmp; \
+\
+ SPLAY_LEFT(&__node, field) = SPLAY_RIGHT(&__node, field) = NULL;\
+ __left = __right = &__node; \
+\
+ while (1) { \
+ if (__comp < 0) { \
+ __tmp = SPLAY_LEFT((head)->sph_root, field); \
+ if (__tmp == NULL) \
+ break; \
+ if (__comp < 0){ \
+ SPLAY_ROTATE_RIGHT(head, __tmp, field); \
+ if (SPLAY_LEFT((head)->sph_root, field) == NULL)\
+ break; \
+ } \
+ SPLAY_LINKLEFT(head, __right, field); \
+ } else if (__comp > 0) { \
+ __tmp = SPLAY_RIGHT((head)->sph_root, field); \
+ if (__tmp == NULL) \
+ break; \
+ if (__comp > 0) { \
+ SPLAY_ROTATE_LEFT(head, __tmp, field); \
+ if (SPLAY_RIGHT((head)->sph_root, field) == NULL)\
+ break; \
+ } \
+ SPLAY_LINKRIGHT(head, __left, field); \
+ } \
+ } \
+ SPLAY_ASSEMBLE(head, &__node, __left, __right, field); \
+}
+
+#define SPLAY_NEGINF -1
+#define SPLAY_INF 1
+
+#define SPLAY_INSERT(name, x, y) name##_SPLAY_INSERT(x, y)
+#define SPLAY_REMOVE(name, x, y) name##_SPLAY_REMOVE(x, y)
+#define SPLAY_FIND(name, x, y) name##_SPLAY_FIND(x, y)
+#define SPLAY_NEXT(name, x, y) name##_SPLAY_NEXT(x, y)
+#define SPLAY_MIN(name, x) (SPLAY_EMPTY(x) ? NULL \
+ : name##_SPLAY_MIN_MAX(x, SPLAY_NEGINF))
+#define SPLAY_MAX(name, x) (SPLAY_EMPTY(x) ? NULL \
+ : name##_SPLAY_MIN_MAX(x, SPLAY_INF))
+
+#define SPLAY_FOREACH(x, name, head) \
+ for ((x) = SPLAY_MIN(name, head); \
+ (x) != NULL; \
+ (x) = SPLAY_NEXT(name, head, x))
+
+/* Macros that define a red-black tree */
+#define RB_HEAD(name, type) \
+struct name { \
+ struct type *rbh_root; /* root of the tree */ \
+}
+
+#define RB_INITIALIZER(root) \
+ { NULL }
+
+#define RB_INIT(root) do { \
+ (root)->rbh_root = NULL; \
+} while (0)
+
+#define RB_BLACK 0
+#define RB_RED 1
+#define RB_ENTRY(type) \
+struct { \
+ struct type *rbe_left; /* left element */ \
+ struct type *rbe_right; /* right element */ \
+ struct type *rbe_parent; /* parent element */ \
+ int rbe_color; /* node color */ \
+}
+
+#define RB_LEFT(elm, field) (elm)->field.rbe_left
+#define RB_RIGHT(elm, field) (elm)->field.rbe_right
+#define RB_PARENT(elm, field) (elm)->field.rbe_parent
+#define RB_COLOR(elm, field) (elm)->field.rbe_color
+#define RB_ROOT(head) (head)->rbh_root
+#define RB_EMPTY(head) (RB_ROOT(head) == NULL)
+
+#define RB_SET(elm, parent, field) do { \
+ RB_PARENT(elm, field) = parent; \
+ RB_LEFT(elm, field) = RB_RIGHT(elm, field) = NULL; \
+ RB_COLOR(elm, field) = RB_RED; \
+} while (0)
+
+#define RB_SET_BLACKRED(black, red, field) do { \
+ RB_COLOR(black, field) = RB_BLACK; \
+ RB_COLOR(red, field) = RB_RED; \
+} while (0)
+
+#ifndef RB_AUGMENT
+#define RB_AUGMENT(x) do {} while (0)
+#endif
+
+#define RB_ROTATE_LEFT(head, elm, tmp, field) do { \
+ (tmp) = RB_RIGHT(elm, field); \
+ if ((RB_RIGHT(elm, field) = RB_LEFT(tmp, field))) { \
+ RB_PARENT(RB_LEFT(tmp, field), field) = (elm); \
+ } \
+ RB_AUGMENT(elm); \
+ if ((RB_PARENT(tmp, field) = RB_PARENT(elm, field))) { \
+ if ((elm) == RB_LEFT(RB_PARENT(elm, field), field)) \
+ RB_LEFT(RB_PARENT(elm, field), field) = (tmp); \
+ else \
+ RB_RIGHT(RB_PARENT(elm, field), field) = (tmp); \
+ } else \
+ (head)->rbh_root = (tmp); \
+ RB_LEFT(tmp, field) = (elm); \
+ RB_PARENT(elm, field) = (tmp); \
+ RB_AUGMENT(tmp); \
+ if ((RB_PARENT(tmp, field))) \
+ RB_AUGMENT(RB_PARENT(tmp, field)); \
+} while (0)
+
+#define RB_ROTATE_RIGHT(head, elm, tmp, field) do { \
+ (tmp) = RB_LEFT(elm, field); \
+ if ((RB_LEFT(elm, field) = RB_RIGHT(tmp, field))) { \
+ RB_PARENT(RB_RIGHT(tmp, field), field) = (elm); \
+ } \
+ RB_AUGMENT(elm); \
+ if ((RB_PARENT(tmp, field) = RB_PARENT(elm, field))) { \
+ if ((elm) == RB_LEFT(RB_PARENT(elm, field), field)) \
+ RB_LEFT(RB_PARENT(elm, field), field) = (tmp); \
+ else \
+ RB_RIGHT(RB_PARENT(elm, field), field) = (tmp); \
+ } else \
+ (head)->rbh_root = (tmp); \
+ RB_RIGHT(tmp, field) = (elm); \
+ RB_PARENT(elm, field) = (tmp); \
+ RB_AUGMENT(tmp); \
+ if ((RB_PARENT(tmp, field))) \
+ RB_AUGMENT(RB_PARENT(tmp, field)); \
+} while (0)
+
+/* Generates prototypes and inline functions */
+#define RB_PROTOTYPE(name, type, field, cmp) \
+ RB_PROTOTYPE_INTERNAL(name, type, field, cmp,)
+#define RB_PROTOTYPE_STATIC(name, type, field, cmp) \
+ RB_PROTOTYPE_INTERNAL(name, type, field, cmp, __attribute__((__unused__)) static)
+#define RB_PROTOTYPE_INTERNAL(name, type, field, cmp, attr) \
+attr void name##_RB_INSERT_COLOR(struct name *, struct type *); \
+attr void name##_RB_REMOVE_COLOR(struct name *, struct type *, struct type *);\
+attr struct type *name##_RB_REMOVE(struct name *, struct type *); \
+attr struct type *name##_RB_INSERT(struct name *, struct type *); \
+attr struct type *name##_RB_FIND(struct name *, struct type *); \
+attr struct type *name##_RB_NFIND(struct name *, struct type *); \
+attr struct type *name##_RB_NEXT(struct type *); \
+attr struct type *name##_RB_PREV(struct type *); \
+attr struct type *name##_RB_MINMAX(struct name *, int); \
+ \
+
+/* Main rb operation.
+ * Moves node close to the key of elm to top
+ */
+#define RB_GENERATE(name, type, field, cmp) \
+ RB_GENERATE_INTERNAL(name, type, field, cmp,)
+#define RB_GENERATE_STATIC(name, type, field, cmp) \
+ RB_GENERATE_INTERNAL(name, type, field, cmp, __attribute__((__unused__)) static)
+#define RB_GENERATE_INTERNAL(name, type, field, cmp, attr) \
+attr void \
+name##_RB_INSERT_COLOR(struct name *head, struct type *elm) \
+{ \
+ struct type *parent, *gparent, *tmp; \
+ while ((parent = RB_PARENT(elm, field)) && \
+ RB_COLOR(parent, field) == RB_RED) { \
+ gparent = RB_PARENT(parent, field); \
+ if (parent == RB_LEFT(gparent, field)) { \
+ tmp = RB_RIGHT(gparent, field); \
+ if (tmp && RB_COLOR(tmp, field) == RB_RED) { \
+ RB_COLOR(tmp, field) = RB_BLACK; \
+ RB_SET_BLACKRED(parent, gparent, field);\
+ elm = gparent; \
+ continue; \
+ } \
+ if (RB_RIGHT(parent, field) == elm) { \
+ RB_ROTATE_LEFT(head, parent, tmp, field);\
+ tmp = parent; \
+ parent = elm; \
+ elm = tmp; \
+ } \
+ RB_SET_BLACKRED(parent, gparent, field); \
+ RB_ROTATE_RIGHT(head, gparent, tmp, field); \
+ } else { \
+ tmp = RB_LEFT(gparent, field); \
+ if (tmp && RB_COLOR(tmp, field) == RB_RED) { \
+ RB_COLOR(tmp, field) = RB_BLACK; \
+ RB_SET_BLACKRED(parent, gparent, field);\
+ elm = gparent; \
+ continue; \
+ } \
+ if (RB_LEFT(parent, field) == elm) { \
+ RB_ROTATE_RIGHT(head, parent, tmp, field);\
+ tmp = parent; \
+ parent = elm; \
+ elm = tmp; \
+ } \
+ RB_SET_BLACKRED(parent, gparent, field); \
+ RB_ROTATE_LEFT(head, gparent, tmp, field); \
+ } \
+ } \
+ RB_COLOR(head->rbh_root, field) = RB_BLACK; \
+} \
+ \
+attr void \
+name##_RB_REMOVE_COLOR(struct name *head, struct type *parent, struct type *elm) \
+{ \
+ struct type *tmp; \
+ while ((elm == NULL || RB_COLOR(elm, field) == RB_BLACK) && \
+ elm != RB_ROOT(head)) { \
+ if (RB_LEFT(parent, field) == elm) { \
+ tmp = RB_RIGHT(parent, field); \
+ if (RB_COLOR(tmp, field) == RB_RED) { \
+ RB_SET_BLACKRED(tmp, parent, field); \
+ RB_ROTATE_LEFT(head, parent, tmp, field);\
+ tmp = RB_RIGHT(parent, field); \
+ } \
+ if ((RB_LEFT(tmp, field) == NULL || \
+ RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) &&\
+ (RB_RIGHT(tmp, field) == NULL || \
+ RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK)) {\
+ RB_COLOR(tmp, field) = RB_RED; \
+ elm = parent; \
+ parent = RB_PARENT(elm, field); \
+ } else { \
+ if (RB_RIGHT(tmp, field) == NULL || \
+ RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK) {\
+ struct type *oleft; \
+ if ((oleft = RB_LEFT(tmp, field)))\
+ RB_COLOR(oleft, field) = RB_BLACK;\
+ RB_COLOR(tmp, field) = RB_RED; \
+ RB_ROTATE_RIGHT(head, tmp, oleft, field);\
+ tmp = RB_RIGHT(parent, field); \
+ } \
+ RB_COLOR(tmp, field) = RB_COLOR(parent, field);\
+ RB_COLOR(parent, field) = RB_BLACK; \
+ if (RB_RIGHT(tmp, field)) \
+ RB_COLOR(RB_RIGHT(tmp, field), field) = RB_BLACK;\
+ RB_ROTATE_LEFT(head, parent, tmp, field);\
+ elm = RB_ROOT(head); \
+ break; \
+ } \
+ } else { \
+ tmp = RB_LEFT(parent, field); \
+ if (RB_COLOR(tmp, field) == RB_RED) { \
+ RB_SET_BLACKRED(tmp, parent, field); \
+ RB_ROTATE_RIGHT(head, parent, tmp, field);\
+ tmp = RB_LEFT(parent, field); \
+ } \
+ if ((RB_LEFT(tmp, field) == NULL || \
+ RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) &&\
+ (RB_RIGHT(tmp, field) == NULL || \
+ RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK)) {\
+ RB_COLOR(tmp, field) = RB_RED; \
+ elm = parent; \
+ parent = RB_PARENT(elm, field); \
+ } else { \
+ if (RB_LEFT(tmp, field) == NULL || \
+ RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) {\
+ struct type *oright; \
+ if ((oright = RB_RIGHT(tmp, field)))\
+ RB_COLOR(oright, field) = RB_BLACK;\
+ RB_COLOR(tmp, field) = RB_RED; \
+ RB_ROTATE_LEFT(head, tmp, oright, field);\
+ tmp = RB_LEFT(parent, field); \
+ } \
+ RB_COLOR(tmp, field) = RB_COLOR(parent, field);\
+ RB_COLOR(parent, field) = RB_BLACK; \
+ if (RB_LEFT(tmp, field)) \
+ RB_COLOR(RB_LEFT(tmp, field), field) = RB_BLACK;\
+ RB_ROTATE_RIGHT(head, parent, tmp, field);\
+ elm = RB_ROOT(head); \
+ break; \
+ } \
+ } \
+ } \
+ if (elm) \
+ RB_COLOR(elm, field) = RB_BLACK; \
+} \
+ \
+attr struct type * \
+name##_RB_REMOVE(struct name *head, struct type *elm) \
+{ \
+ struct type *child, *parent, *old = elm; \
+ int color; \
+ if (RB_LEFT(elm, field) == NULL) \
+ child = RB_RIGHT(elm, field); \
+ else if (RB_RIGHT(elm, field) == NULL) \
+ child = RB_LEFT(elm, field); \
+ else { \
+ struct type *left; \
+ elm = RB_RIGHT(elm, field); \
+ while ((left = RB_LEFT(elm, field))) \
+ elm = left; \
+ child = RB_RIGHT(elm, field); \
+ parent = RB_PARENT(elm, field); \
+ color = RB_COLOR(elm, field); \
+ if (child) \
+ RB_PARENT(child, field) = parent; \
+ if (parent) { \
+ if (RB_LEFT(parent, field) == elm) \
+ RB_LEFT(parent, field) = child; \
+ else \
+ RB_RIGHT(parent, field) = child; \
+ RB_AUGMENT(parent); \
+ } else \
+ RB_ROOT(head) = child; \
+ if (RB_PARENT(elm, field) == old) \
+ parent = elm; \
+ (elm)->field = (old)->field; \
+ if (RB_PARENT(old, field)) { \
+ if (RB_LEFT(RB_PARENT(old, field), field) == old)\
+ RB_LEFT(RB_PARENT(old, field), field) = elm;\
+ else \
+ RB_RIGHT(RB_PARENT(old, field), field) = elm;\
+ RB_AUGMENT(RB_PARENT(old, field)); \
+ } else \
+ RB_ROOT(head) = elm; \
+ RB_PARENT(RB_LEFT(old, field), field) = elm; \
+ if (RB_RIGHT(old, field)) \
+ RB_PARENT(RB_RIGHT(old, field), field) = elm; \
+ if (parent) { \
+ left = parent; \
+ do { \
+ RB_AUGMENT(left); \
+ } while ((left = RB_PARENT(left, field))); \
+ } \
+ goto color; \
+ } \
+ parent = RB_PARENT(elm, field); \
+ color = RB_COLOR(elm, field); \
+ if (child) \
+ RB_PARENT(child, field) = parent; \
+ if (parent) { \
+ if (RB_LEFT(parent, field) == elm) \
+ RB_LEFT(parent, field) = child; \
+ else \
+ RB_RIGHT(parent, field) = child; \
+ RB_AUGMENT(parent); \
+ } else \
+ RB_ROOT(head) = child; \
+color: \
+ if (color == RB_BLACK) \
+ name##_RB_REMOVE_COLOR(head, parent, child); \
+ return (old); \
+} \
+ \
+/* Inserts a node into the RB tree */ \
+attr struct type * \
+name##_RB_INSERT(struct name *head, struct type *elm) \
+{ \
+ struct type *tmp; \
+ struct type *parent = NULL; \
+ int comp = 0; \
+ tmp = RB_ROOT(head); \
+ while (tmp) { \
+ parent = tmp; \
+ comp = (cmp)(elm, parent); \
+ if (comp < 0) \
+ tmp = RB_LEFT(tmp, field); \
+ else if (comp > 0) \
+ tmp = RB_RIGHT(tmp, field); \
+ else \
+ return (tmp); \
+ } \
+ RB_SET(elm, parent, field); \
+ if (parent != NULL) { \
+ if (comp < 0) \
+ RB_LEFT(parent, field) = elm; \
+ else \
+ RB_RIGHT(parent, field) = elm; \
+ RB_AUGMENT(parent); \
+ } else \
+ RB_ROOT(head) = elm; \
+ name##_RB_INSERT_COLOR(head, elm); \
+ return (NULL); \
+} \
+ \
+/* Finds the node with the same key as elm */ \
+attr struct type * \
+name##_RB_FIND(struct name *head, struct type *elm) \
+{ \
+ struct type *tmp = RB_ROOT(head); \
+ int comp; \
+ while (tmp) { \
+ comp = cmp(elm, tmp); \
+ if (comp < 0) \
+ tmp = RB_LEFT(tmp, field); \
+ else if (comp > 0) \
+ tmp = RB_RIGHT(tmp, field); \
+ else \
+ return (tmp); \
+ } \
+ return (NULL); \
+} \
+ \
+/* Finds the first node greater than or equal to the search key */ \
+attr struct type * \
+name##_RB_NFIND(struct name *head, struct type *elm) \
+{ \
+ struct type *tmp = RB_ROOT(head); \
+ struct type *res = NULL; \
+ int comp; \
+ while (tmp) { \
+ comp = cmp(elm, tmp); \
+ if (comp < 0) { \
+ res = tmp; \
+ tmp = RB_LEFT(tmp, field); \
+ } \
+ else if (comp > 0) \
+ tmp = RB_RIGHT(tmp, field); \
+ else \
+ return (tmp); \
+ } \
+ return (res); \
+} \
+ \
+/* ARGSUSED */ \
+attr struct type * \
+name##_RB_NEXT(struct type *elm) \
+{ \
+ if (RB_RIGHT(elm, field)) { \
+ elm = RB_RIGHT(elm, field); \
+ while (RB_LEFT(elm, field)) \
+ elm = RB_LEFT(elm, field); \
+ } else { \
+ if (RB_PARENT(elm, field) && \
+ (elm == RB_LEFT(RB_PARENT(elm, field), field))) \
+ elm = RB_PARENT(elm, field); \
+ else { \
+ while (RB_PARENT(elm, field) && \
+ (elm == RB_RIGHT(RB_PARENT(elm, field), field)))\
+ elm = RB_PARENT(elm, field); \
+ elm = RB_PARENT(elm, field); \
+ } \
+ } \
+ return (elm); \
+} \
+ \
+/* ARGSUSED */ \
+attr struct type * \
+name##_RB_PREV(struct type *elm) \
+{ \
+ if (RB_LEFT(elm, field)) { \
+ elm = RB_LEFT(elm, field); \
+ while (RB_RIGHT(elm, field)) \
+ elm = RB_RIGHT(elm, field); \
+ } else { \
+ if (RB_PARENT(elm, field) && \
+ (elm == RB_RIGHT(RB_PARENT(elm, field), field))) \
+ elm = RB_PARENT(elm, field); \
+ else { \
+ while (RB_PARENT(elm, field) && \
+ (elm == RB_LEFT(RB_PARENT(elm, field), field)))\
+ elm = RB_PARENT(elm, field); \
+ elm = RB_PARENT(elm, field); \
+ } \
+ } \
+ return (elm); \
+} \
+ \
+attr struct type * \
+name##_RB_MINMAX(struct name *head, int val) \
+{ \
+ struct type *tmp = RB_ROOT(head); \
+ struct type *parent = NULL; \
+ while (tmp) { \
+ parent = tmp; \
+ if (val < 0) \
+ tmp = RB_LEFT(tmp, field); \
+ else \
+ tmp = RB_RIGHT(tmp, field); \
+ } \
+ return (parent); \
+}
+
+#define RB_NEGINF -1
+#define RB_INF 1
+
+#define RB_INSERT(name, x, y) name##_RB_INSERT(x, y)
+#define RB_REMOVE(name, x, y) name##_RB_REMOVE(x, y)
+#define RB_FIND(name, x, y) name##_RB_FIND(x, y)
+#define RB_NFIND(name, x, y) name##_RB_NFIND(x, y)
+#define RB_NEXT(name, x, y) name##_RB_NEXT(y)
+#define RB_PREV(name, x, y) name##_RB_PREV(y)
+#define RB_MIN(name, x) name##_RB_MINMAX(x, RB_NEGINF)
+#define RB_MAX(name, x) name##_RB_MINMAX(x, RB_INF)
+
+#define RB_FOREACH(x, name, head) \
+ for ((x) = RB_MIN(name, head); \
+ (x) != NULL; \
+ (x) = name##_RB_NEXT(x))
+
+#define RB_FOREACH_SAFE(x, name, head, y) \
+ for ((x) = RB_MIN(name, head); \
+ ((x) != NULL) && ((y) = name##_RB_NEXT(x), 1); \
+ (x) = (y))
+
+#define RB_FOREACH_REVERSE(x, name, head) \
+ for ((x) = RB_MAX(name, head); \
+ (x) != NULL; \
+ (x) = name##_RB_PREV(x))
+
+#define RB_FOREACH_REVERSE_SAFE(x, name, head, y) \
+ for ((x) = RB_MAX(name, head); \
+ ((x) != NULL) && ((y) = name##_RB_PREV(x), 1); \
+ (x) = (y))
+
+#endif /* _SYS_TREE_H_ */
diff --git a/openbsd-compat/usleep.c b/openbsd-compat/usleep.c
new file mode 100644
index 00000000..946befc9
--- /dev/null
+++ b/openbsd-compat/usleep.c
@@ -0,0 +1,43 @@
+
+/*
+ * Copyright (c) 1999-2004 Damien Miller <djm@mindrot.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>
+#ifdef HAVE_SYS_SELECT_H
+# include <sys/select.h>
+#endif
+#ifdef HAVE_SYS_TIME_H
+# include <sys/time.h>
+#endif
+
+#include <err.h>
+#include <string.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <time.h>
+#include <unistd.h>
+
+int
+usleep(unsigned int useconds)
+{
+ struct timespec ts;
+
+ ts.tv_sec = useconds / 1000000;
+ ts.tv_nsec = (useconds % 1000000) * 1000;
+ return nanosleep(&ts, NULL);
+}
diff --git a/openbsd-compat/vis.c b/openbsd-compat/vis.c
new file mode 100644
index 00000000..a49b4dc3
--- /dev/null
+++ b/openbsd-compat/vis.c
@@ -0,0 +1,222 @@
+/* $OpenBSD: vis.c,v 1.19 2005/09/01 17:15:49 millert Exp $ */
+/*-
+ * Copyright (c) 1989, 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.
+ */
+
+/* OPENBSD ORIGINAL: lib/libc/gen/vis.c */
+
+#include "includes.h"
+
+#include <ctype.h>
+#include <string.h>
+
+#include "bsd-vis.h"
+
+#define isoctal(c) (((u_char)(c)) >= '0' && ((u_char)(c)) <= '7')
+#define isvisible(c) \
+ (((u_int)(c) <= UCHAR_MAX && isascii((u_char)(c)) && \
+ (((c) != '*' && (c) != '?' && (c) != '[' && (c) != '#') || \
+ (flag & VIS_GLOB) == 0) && isgraph((u_char)(c))) || \
+ ((flag & VIS_SP) == 0 && (c) == ' ') || \
+ ((flag & VIS_TAB) == 0 && (c) == '\t') || \
+ ((flag & VIS_NL) == 0 && (c) == '\n') || \
+ ((flag & VIS_SAFE) && ((c) == '\b' || \
+ (c) == '\007' || (c) == '\r' || \
+ isgraph((u_char)(c)))))
+
+/*
+ * vis - visually encode characters
+ */
+char *
+vis(char *dst, int c, int flag, int nextc)
+{
+ if (isvisible(c)) {
+ *dst++ = c;
+ if (c == '\\' && (flag & VIS_NOSLASH) == 0)
+ *dst++ = '\\';
+ *dst = '\0';
+ return (dst);
+ }
+
+ if (flag & VIS_CSTYLE) {
+ switch(c) {
+ case '\n':
+ *dst++ = '\\';
+ *dst++ = 'n';
+ goto done;
+ case '\r':
+ *dst++ = '\\';
+ *dst++ = 'r';
+ goto done;
+ case '\b':
+ *dst++ = '\\';
+ *dst++ = 'b';
+ goto done;
+ case '\a':
+ *dst++ = '\\';
+ *dst++ = 'a';
+ goto done;
+ case '\v':
+ *dst++ = '\\';
+ *dst++ = 'v';
+ goto done;
+ case '\t':
+ *dst++ = '\\';
+ *dst++ = 't';
+ goto done;
+ case '\f':
+ *dst++ = '\\';
+ *dst++ = 'f';
+ goto done;
+ case ' ':
+ *dst++ = '\\';
+ *dst++ = 's';
+ goto done;
+ case '\0':
+ *dst++ = '\\';
+ *dst++ = '0';
+ if (isoctal(nextc)) {
+ *dst++ = '0';
+ *dst++ = '0';
+ }
+ goto done;
+ }
+ }
+ if (((c & 0177) == ' ') || (flag & VIS_OCTAL) ||
+ ((flag & VIS_GLOB) && (c == '*' || c == '?' || c == '[' || c == '#'))) {
+ *dst++ = '\\';
+ *dst++ = ((u_char)c >> 6 & 07) + '0';
+ *dst++ = ((u_char)c >> 3 & 07) + '0';
+ *dst++ = ((u_char)c & 07) + '0';
+ goto done;
+ }
+ if ((flag & VIS_NOSLASH) == 0)
+ *dst++ = '\\';
+ if (c & 0200) {
+ c &= 0177;
+ *dst++ = 'M';
+ }
+ if (iscntrl((u_char)c)) {
+ *dst++ = '^';
+ if (c == 0177)
+ *dst++ = '?';
+ else
+ *dst++ = c + '@';
+ } else {
+ *dst++ = '-';
+ *dst++ = c;
+ }
+done:
+ *dst = '\0';
+ return (dst);
+}
+
+/*
+ * strvis, strnvis, strvisx - visually encode characters from src into dst
+ *
+ * Dst must be 4 times the size of src to account for possible
+ * expansion. The length of dst, not including the trailing NULL,
+ * is returned.
+ *
+ * Strnvis will write no more than siz-1 bytes (and will NULL terminate).
+ * The number of bytes needed to fully encode the string is returned.
+ *
+ * Strvisx encodes exactly len bytes from src into dst.
+ * This is useful for encoding a block of data.
+ */
+int
+strvis(char *dst, const char *src, int flag)
+{
+ char c;
+ char *start;
+
+ for (start = dst; (c = *src);)
+ dst = vis(dst, c, flag, *++src);
+ *dst = '\0';
+ return (dst - start);
+}
+
+int
+strnvis(char *dst, const char *src, size_t siz, int flag)
+{
+ char *start, *end;
+ char tbuf[5];
+ int c, i;
+
+ i = 0;
+ for (start = dst, end = start + siz - 1; (c = *src) && dst < end; ) {
+ if (isvisible(c)) {
+ i = 1;
+ *dst++ = c;
+ if (c == '\\' && (flag & VIS_NOSLASH) == 0) {
+ /* need space for the extra '\\' */
+ if (dst < end)
+ *dst++ = '\\';
+ else {
+ dst--;
+ i = 2;
+ break;
+ }
+ }
+ src++;
+ } else {
+ i = vis(tbuf, c, flag, *++src) - tbuf;
+ if (dst + i <= end) {
+ memcpy(dst, tbuf, i);
+ dst += i;
+ } else {
+ src--;
+ break;
+ }
+ }
+ }
+ if (siz > 0)
+ *dst = '\0';
+ if (dst + i > end) {
+ /* adjust return value for truncation */
+ while ((c = *src))
+ dst += vis(tbuf, c, flag, *++src) - tbuf;
+ }
+ return (dst - start);
+}
+
+int
+strvisx(char *dst, const char *src, size_t len, int flag)
+{
+ char c;
+ char *start;
+
+ for (start = dst; len > 1; len--) {
+ c = *src;
+ dst = vis(dst, c, flag, *++src);
+ }
+ if (len)
+ dst = vis(dst, *src, flag, '\0');
+ *dst = '\0';
+ return (dst - start);
+}
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 f90d006f..00000000
--- a/smtpd/lka_proc.c
+++ /dev/null
@@ -1,188 +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 <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 95562e84..00000000
--- a/smtpd/lka_report.c
+++ /dev/null
@@ -1,509 +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 <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, 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/smtpscript/iobuf.c b/smtpscript/iobuf.c
index 7dcd45b9..05a9cd59 100644
--- a/smtpscript/iobuf.c
+++ b/smtpscript/iobuf.c
@@ -26,7 +26,7 @@
#include <string.h>
#include <unistd.h>
-#ifdef IO_SSL
+#ifdef IO_TLS
#include <openssl/err.h>
#include <openssl/ssl.h>
#endif
@@ -387,7 +387,7 @@ iobuf_flush(struct iobuf *io, int fd)
return (0);
}
-#ifdef IO_SSL
+#ifdef IO_TLS
int
iobuf_flush_ssl(struct iobuf *io, void *ssl)
@@ -463,4 +463,4 @@ iobuf_read_ssl(struct iobuf *io, void *ssl)
return (n);
}
-#endif /* IO_SSL */
+#endif /* IO_TLS */
diff --git a/smtpscript/smtpscript/Makefile b/smtpscript/smtpscript/Makefile
index 3679481c..0520606f 100644
--- a/smtpscript/smtpscript/Makefile
+++ b/smtpscript/smtpscript/Makefile
@@ -7,6 +7,6 @@ NOMAN= noman
LDADD+= -lutil -lssl -lcrypto
DPADD+= ${LIBEVENT} ${LIBUTIL} ${LIBSSL} ${LIBCRYPTO}
CPPFLAGS+= -I${.CURDIR}/..
-CPPFLAGS+= -DIO_SSL
+CPPFLAGS+= -DIO_TLS
.include <bsd.prog.mk>
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 884f6963..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>
@@ -16,6 +16,8 @@
* 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>
@@ -29,7 +31,12 @@
#include <stdlib.h>
#include <string.h>
#include <limits.h>
+#ifdef HAVE_UTIL_H
#include <util.h>
+#endif
+#ifdef HAVE_LIBUTIL_H
+#include <libutil.h>
+#endif
#include "smtpd.h"
#include "log.h"
@@ -164,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 02239988..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>
@@ -18,6 +18,8 @@
* 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>
@@ -184,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);
@@ -196,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);
@@ -311,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);
}
@@ -343,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)
@@ -451,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,
@@ -469,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) {
@@ -495,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),
@@ -538,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);
@@ -555,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);
@@ -577,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);
@@ -728,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 fdc177e2..b36033b4 100644
--- a/smtpd/ca.c
+++ b/usr.sbin/smtpd/ca.c
@@ -17,11 +17,14 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
+#include "includes.h"
+
#include <sys/types.h>
#include <sys/queue.h>
#include <sys/socket.h>
#include <sys/tree.h>
+#include <grp.h> /* needed for setgroups */
#include <err.h>
#include <imsg.h>
#include <limits.h>
@@ -68,7 +71,6 @@ 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 *);
-
static uint64_t reqid = 0;
static void
@@ -115,8 +117,10 @@ ca(void)
/* Ignore them until we get our config */
mproc_disable(p_pony);
+#if HAVE_PLEDGE
if (pledge("stdio", NULL) == -1)
err(1, "pledge");
+#endif
event_dispatch();
fatalx("exited event loop");
@@ -318,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);
@@ -329,7 +334,6 @@ ca_imsg(struct mproc *p, struct imsg *imsg)
EC_KEY_free(ecdsa);
return;
}
-
errx(1, "ca_imsg: unexpected %s imsg", imsg_to_str(imsg->hdr.type));
}
@@ -503,6 +507,7 @@ rsae_keygen(RSA *rsa, int bits, BIGNUM *e, BN_GENCB *cb)
* ECDSA privsep engine (called from unprivileged processes)
*/
+#if defined(SUPPORT_ECDSA)
const ECDSA_METHOD *ecdsa_default = NULL;
static ECDSA_METHOD *ecdsae_method = NULL;
@@ -526,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,
@@ -542,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
@@ -601,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)
@@ -609,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
@@ -626,7 +643,115 @@ 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
rsa_engine_init(void)
@@ -699,6 +824,7 @@ rsa_engine_init(void)
fatalx("%s", errstr);
}
+#if defined(SUPPORT_ECDSA)
static void
ecdsa_engine_init(void)
{
@@ -752,6 +878,62 @@ 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)
diff --git a/smtpd/cert.c b/usr.sbin/smtpd/cert.c
index 05aff418..79b1df91 100644
--- a/smtpd/cert.c
+++ b/usr.sbin/smtpd/cert.c
@@ -16,6 +16,8 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
+#include "includes.h"
+
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/tree.h>
diff --git a/smtpd/compress_backend.c b/usr.sbin/smtpd/compress_backend.c
index 516dd1ee..1b974662 100644
--- a/smtpd/compress_backend.c
+++ b/usr.sbin/smtpd/compress_backend.c
@@ -17,6 +17,8 @@
* 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>
diff --git a/smtpd/compress_gzip.c b/usr.sbin/smtpd/compress_gzip.c
index e7421cec..dd60aeec 100644
--- a/smtpd/compress_gzip.c
+++ b/usr.sbin/smtpd/compress_gzip.c
@@ -17,6 +17,8 @@
* 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>
diff --git a/smtpd/config.c b/usr.sbin/smtpd/config.c
index cc53272a..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>
@@ -16,6 +16,8 @@
* 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>
@@ -89,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));
@@ -103,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)
@@ -116,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);
@@ -155,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);
@@ -203,7 +205,9 @@ set_localaddrs(struct smtpd *conf, struct table *localnames)
case AF_INET:
sain = (struct sockaddr_in *)&ss;
*sain = *(struct sockaddr_in *)p->ifa_addr;
+#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
sain->sin_len = sizeof(struct sockaddr_in);
+#endif
table_add(t, ss_to_text(&ss), NULL);
table_add(localnames, ss_to_text(&ss), NULL);
(void)snprintf(buf, sizeof buf, "[%s]", ss_to_text(&ss));
@@ -213,7 +217,9 @@ set_localaddrs(struct smtpd *conf, struct table *localnames)
case AF_INET6:
sin6 = (struct sockaddr_in6 *)&ss;
*sin6 = *(struct sockaddr_in6 *)p->ifa_addr;
+#ifdef HAVE_STRUCT_SOCKADDR_IN6_SIN6_LEN
sin6->sin6_len = sizeof(struct sockaddr_in6);
+#endif
table_add(t, ss_to_text(&ss), NULL);
table_add(localnames, ss_to_text(&ss), NULL);
(void)snprintf(buf, sizeof buf, "[%s]", ss_to_text(&ss));
@@ -304,7 +310,8 @@ config_process(enum smtp_proc_type proc)
fatal("fdlimit: getrlimit");
rl.rlim_cur = rl.rlim_max;
if (setrlimit(RLIMIT_NOFILE, &rl) == -1)
- fatal("fdlimit: setrlimit");
+ if (errno != EINVAL)
+ fatal("fdlimit: setrlimit");
}
void
diff --git a/smtpd/control.c b/usr.sbin/smtpd/control.c
index 6f9c9aca..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>
@@ -18,6 +18,8 @@
* 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>
@@ -29,6 +31,7 @@
#include <errno.h>
#include <event.h>
#include <fcntl.h>
+#include <grp.h> /* needed for setgroups */
#include <imsg.h>
#include <pwd.h>
#include <signal.h>
@@ -253,8 +256,10 @@ control(void)
control_listen();
+#if HAVE_PLEDGE
if (pledge("stdio unix recvfd sendfd", NULL) == -1)
err(1, "pledge");
+#endif
event_dispatch();
fatalx("exited event loop");
@@ -292,8 +297,13 @@ control_accept(int listenfd, short event, void *arg)
uid_t euid;
gid_t egid;
+#if defined(HAVE_GETDTABLESIZE) && defined(HAVE_GETDTABLECOUNT)
if (getdtablesize() - getdtablecount() < CONTROL_FD_RESERVE)
goto pause;
+#else
+ if (available_fds(CONTROL_FD_RESERVE))
+ goto pause;
+#endif
len = sizeof(s_un);
if ((connfd = accept(listenfd, (struct sockaddr *)&s_un, &len)) == -1) {
@@ -366,8 +376,13 @@ control_close(struct ctl_conn *c)
stat_backend->decrement("control.session", 1);
+#if defined(HAVE_GETDTABLESIZE) && defined(HAVE_GETDTABLECOUNT)
if (getdtablesize() - getdtablecount() < CONTROL_FD_RESERVE)
return;
+#else
+ if (available_fds(CONTROL_FD_RESERVE))
+ return;
+#endif
if (!event_pending(&control_state.ev, EV_READ, NULL)) {
log_warnx("warn: re-enabling ctl connections");
diff --git a/smtpd/crypto.c b/usr.sbin/smtpd/crypto.c
index b6a7547c..20a422cd 100644
--- a/smtpd/crypto.c
+++ b/usr.sbin/smtpd/crypto.c
@@ -16,6 +16,8 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
+#include "includes.h"
+
#include <sys/types.h>
#include <sys/stat.h>
diff --git a/smtpd/dict.c b/usr.sbin/smtpd/dict.c
index 91156d5a..e660f0a5 100644
--- a/smtpd/dict.c
+++ b/usr.sbin/smtpd/dict.c
@@ -17,6 +17,8 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
+#include "includes.h"
+
#include <sys/types.h>
#include <sys/tree.h>
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 d5b66af9..a3107e89 100644
--- a/smtpd/dns.c
+++ b/usr.sbin/smtpd/dns.c
@@ -18,6 +18,8 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
+#include "includes.h"
+
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/tree.h>
@@ -27,10 +29,15 @@
#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 <event.h>
+#include <netdb.h>
+#include <resolv.h>
#include <imsg.h>
#include <stdio.h>
#include <stdlib.h>
@@ -41,6 +48,11 @@
#include "log.h"
#include "unpack_dns.h"
+/* On OpenBSD, this function is not needed because we don't free addrinfo */
+#if defined(NOOP_ASR_FREEADDRINFO)
+#define asr_freeaddrinfo(x) do { } while(0);
+#endif
+
struct dns_lookup {
struct dns_session *session;
char *host;
@@ -218,7 +230,7 @@ dns_dispatch_host(struct asr_result *ar, void *arg)
free(lookup->host);
free(lookup);
if (ar->ar_addrinfo)
- freeaddrinfo(ar->ar_addrinfo);
+ asr_freeaddrinfo(ar->ar_addrinfo);
if (ar->ar_gai_errno)
s->error = ar->ar_gai_errno;
diff --git a/smtpd/enqueue.c b/usr.sbin/smtpd/enqueue.c
index d8943953..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>
@@ -18,6 +18,8 @@
* 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/socket.h>
@@ -134,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
@@ -172,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;
@@ -282,17 +302,20 @@ enqueue(int argc, char *argv[], FILE *ofp)
/* If the server is not running, enqueue the message offline */
if (!srv_connected()) {
+#if HAVE_PLEDGE
if (pledge("stdio", NULL) == -1)
err(1, "pledge");
-
+#endif
return (enqueue_offline(save_argc, save_argv, fp, ofp));
}
if ((msg.fd = open_connection()) == -1)
errx(EX_UNAVAILABLE, "server too busy");
+#if HAVE_PLEDGE
if (pledge("stdio wpath cpath", NULL) == -1)
err(1, "pledge");
+#endif
fout = fdopen(msg.fd, "a+");
if (fout == NULL)
@@ -307,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;
@@ -315,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 : "",
@@ -326,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 : ""))
@@ -335,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;
}
@@ -385,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;
@@ -404,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;
@@ -412,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;
@@ -552,12 +561,12 @@ build_from(char *fake_from, struct passwd *pw)
apos, pw->pw_gecos,
pw->pw_name,
len - apos - 1, p + 1) == -1)
- err(1, NULL);
+ err(1, "asprintf");
msg.fromname[apos] = toupper((unsigned char)msg.fromname[apos]);
} else {
if (asprintf(&msg.fromname, "%.*s", len,
pw->pw_gecos) == -1)
- err(1, NULL);
+ err(1, "asprintf");
}
}
}
diff --git a/smtpd/envelope.c b/usr.sbin/smtpd/envelope.c
index 01957dc5..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>
@@ -17,6 +17,8 @@
* 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>
@@ -177,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");
@@ -302,7 +305,9 @@ ascii_load_sockaddr(struct sockaddr_storage *ss, char *buf)
return 0;
ssin6.sin6_family = AF_INET6;
memcpy(ss, &ssin6, sizeof(ssin6));
+#ifdef HAVE_STRUCT_SOCKADDR_STORAGE_SS_LEN
ss->ss_len = sizeof(struct sockaddr_in6);
+#endif
}
else if (buf[0] == '[' && buf[strlen(buf)-1] == ']') {
buf[strlen(buf)-1] = '\0';
@@ -310,14 +315,18 @@ ascii_load_sockaddr(struct sockaddr_storage *ss, char *buf)
return 0;
ssin6.sin6_family = AF_INET6;
memcpy(ss, &ssin6, sizeof(ssin6));
+#ifdef HAVE_STRUCT_SOCKADDR_STORAGE_SS_LEN
ss->ss_len = sizeof(struct sockaddr_in6);
+#endif
}
else {
if (inet_pton(AF_INET, buf, &ssin.sin_addr) != 1)
return 0;
ssin.sin_family = AF_INET;
memcpy(ss, &ssin, sizeof(ssin));
+#ifdef HAVE_STRUCT_SOCKADDR_STORAGE_SS_LEN
ss->ss_len = sizeof(struct sockaddr_in);
+#endif
}
return 1;
}
@@ -400,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);
@@ -646,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 a07320ce..64a44c79 100644
--- a/smtpd/esc.c
+++ b/usr.sbin/smtpd/esc.c
@@ -16,12 +16,8 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
-#include <sys/queue.h>
-#include <sys/tree.h>
-#include <sys/socket.h>
+#include "includes.h"
-#include <netinet/in.h>
-#include <netdb.h>
#include <stdio.h>
#include <limits.h>
diff --git a/smtpd/expand.c b/usr.sbin/smtpd/expand.c
index 99b25d51..a4306fc0 100644
--- a/smtpd/expand.c
+++ b/usr.sbin/smtpd/expand.c
@@ -17,6 +17,8 @@
* 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>
@@ -29,6 +31,12 @@
#include <limits.h>
#include <stdlib.h>
#include <string.h>
+#ifdef HAVE_UTIL_H
+#include <util.h>
+#endif
+#ifdef HAVE_LIBUTIL_H
+#include <libutil.h>
+#endif
#include "smtpd.h"
#include "log.h"
diff --git a/usr.sbin/smtpd/filter.c b/usr.sbin/smtpd/filter.c
new file mode 100644
index 00000000..614486b7
--- /dev/null
+++ b/usr.sbin/smtpd/filter.c
@@ -0,0 +1,868 @@
+/* $OpenBSD: filter.c,v 1.25 2017/01/09 09:53:23 reyk Exp $ */
+
+/*
+ * Copyright (c) 2011 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 "includes.h"
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/tree.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+
+#include <netinet/in.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <event.h>
+#include <imsg.h>
+#include <inttypes.h>
+#include <limits.h>
+#include <resolv.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "smtpd.h"
+#include "log.h"
+
+enum {
+ QUERY_READY,
+ QUERY_RUNNING,
+ QUERY_DONE
+};
+
+
+struct filter_proc {
+ TAILQ_ENTRY(filter_proc) entry;
+ struct mproc mproc;
+ int hooks;
+ int flags;
+ int ready;
+};
+
+struct filter {
+ TAILQ_ENTRY(filter) entry;
+ struct filter_proc *proc;
+};
+TAILQ_HEAD(filter_lst, filter);
+
+TAILQ_HEAD(filter_query_lst, filter_query);
+struct filter_session {
+ uint64_t id;
+ int terminate;
+ struct filter_lst *filters;
+ struct filter *fcurr;
+
+ int error;
+ struct io *iev;
+ size_t idatalen;
+ FILE *ofile;
+
+ struct filter_query *eom;
+};
+
+struct filter_query {
+ uint64_t qid;
+ int type;
+ struct filter_session *session;
+
+ int state;
+ struct filter *current;
+
+ /* current data */
+ union {
+ struct {
+ struct sockaddr_storage local;
+ struct sockaddr_storage remote;
+ char hostname[HOST_NAME_MAX+1];
+ } connect;
+ char line[LINE_MAX];
+ struct mailaddr maddr;
+ size_t datalen;
+ } u;
+
+ /* current response */
+ struct {
+ int status;
+ int code;
+ char *response;
+ } smtp;
+};
+
+static void filter_imsg(struct mproc *, struct imsg *);
+static void filter_post_event(uint64_t, int, struct filter *, struct filter *);
+static struct filter_query *filter_query(struct filter_session *, int);
+static void filter_drain_query(struct filter_query *);
+static void filter_run_query(struct filter *, struct filter_query *);
+static void filter_end_query(struct filter_query *);
+static void filter_set_sink(struct filter_session *, int);
+static int filter_tx(struct filter_session *, int);
+static void filter_tx_io(struct io *, int, void *);
+
+static TAILQ_HEAD(, filter_proc) procs;
+struct dict chains;
+
+static const char * filter_session_to_text(struct filter_session *);
+static const char * filter_query_to_text(struct filter_query *);
+static const char * filter_to_text(struct filter *);
+static const char * filter_proc_to_text(struct filter_proc *);
+static const char * query_to_str(int);
+static const char * event_to_str(int);
+static const char * status_to_str(int);
+static const char * filterimsg_to_str(int);
+
+struct tree sessions;
+struct tree queries;
+
+static void
+filter_add_arg(struct filter_conf *filter, char *arg)
+{
+ if (filter->argc == MAX_FILTER_ARGS) {
+ log_warnx("warn: filter \"%s\" is full", filter->name);
+ fatalx("exiting");
+ }
+ filter->argv[filter->argc++] = arg;
+}
+
+static void
+filter_extend_chain(struct filter_lst *chain, const char *name)
+{
+ struct filter *n;
+ struct filter_lst *fchain;
+ struct filter_conf *fconf;
+ int i;
+
+ fconf = dict_xget(&env->sc_filters, name);
+ if (fconf->chain) {
+ log_debug("filter: extending with \"%s\"", name);
+ for (i = 0; i < fconf->argc; i++)
+ filter_extend_chain(chain, fconf->argv[i]);
+ }
+ else {
+ log_debug("filter: adding filter \"%s\"", name);
+ n = xcalloc(1, sizeof(*n), "filter_extend_chain");
+ fchain = dict_get(&chains, name);
+ n->proc = TAILQ_FIRST(fchain)->proc;
+ TAILQ_INSERT_TAIL(chain, n, entry);
+ }
+}
+
+void
+filter_postfork(void)
+{
+ static int prepare = 0;
+ struct filter_conf *filter;
+ void *iter;
+ struct filter_proc *proc;
+ struct filter_lst *fchain;
+ struct filter *f;
+ struct mproc *p;
+ int done, i;
+
+ if (prepare)
+ return;
+ prepare = 1;
+
+ TAILQ_INIT(&procs);
+ dict_init(&chains);
+
+ log_debug("filter: building simple chains...");
+
+ /* create all filter proc and associated chains */
+ iter = NULL;
+ while (dict_iter(&env->sc_filters, &iter, NULL, (void **)&filter)) {
+ if (filter->chain)
+ continue;
+
+ log_debug("filter: building simple chain \"%s\"", filter->name);
+ proc = xcalloc(1, sizeof(*proc), "filter_postfork");
+ p = &proc->mproc;
+ p->handler = filter_imsg;
+ p->proc = PROC_FILTER;
+ p->name = xstrdup(filter->name, "filter_postfork");
+ p->data = proc;
+ if (tracing & TRACE_DEBUG)
+ filter_add_arg(filter, "-v");
+ if (foreground_log)
+ filter_add_arg(filter, "-d");
+ if (mproc_fork(p, filter->path, filter->argv) < 0)
+ fatalx("filter_postfork");
+
+ log_debug("filter: registering proc \"%s\"", filter->name);
+ f = xcalloc(1, sizeof(*f), "filter_postfork");
+ f->proc = proc;
+
+ TAILQ_INSERT_TAIL(&procs, proc, entry);
+ fchain = xcalloc(1, sizeof(*fchain), "filter_postfork");
+ TAILQ_INIT(fchain);
+ TAILQ_INSERT_TAIL(fchain, f, entry);
+ dict_xset(&chains, filter->name, fchain);
+ filter->done = 1;
+ }
+
+ log_debug("filter: building complex chains...");
+
+ /* resolve all chains */
+ done = 0;
+ while (!done) {
+ done = 1;
+ iter = NULL;
+ while (dict_iter(&env->sc_filters, &iter, NULL,
+ (void **)&filter)) {
+ if (filter->done)
+ continue;
+ done = 0;
+ filter->done = 1;
+ for (i = 0; i < filter->argc; i++) {
+ if (!dict_get(&chains, filter->argv[i])) {
+ filter->done = 0;
+ break;
+ }
+ }
+ if (filter->done == 0)
+ continue;
+ fchain = xcalloc(1, sizeof(*fchain), "filter_postfork");
+ TAILQ_INIT(fchain);
+ log_debug("filter: building chain \"%s\"...",
+ filter->name);
+ for (i = 0; i < filter->argc; i++)
+ filter_extend_chain(fchain, filter->argv[i]);
+ log_debug("filter: done building chain \"%s\"",
+ filter->name);
+ dict_xset(&chains, filter->name, fchain);
+ }
+ }
+ log_debug("filter: done building complex chains");
+
+ fchain = xcalloc(1, sizeof(*fchain), "filter_postfork");
+ TAILQ_INIT(fchain);
+ dict_xset(&chains, "<no-filter>", fchain);
+}
+
+void
+filter_configure(void)
+{
+ static int init = 0;
+ struct filter_proc *p;
+
+ if (init)
+ return;
+ init = 1;
+
+ tree_init(&sessions);
+ tree_init(&queries);
+
+ TAILQ_FOREACH(p, &procs, entry) {
+ m_create(&p->mproc, IMSG_FILTER_REGISTER, 0, 0, -1);
+ m_add_u32(&p->mproc, FILTER_API_VERSION);
+ m_add_string(&p->mproc, p->mproc.name);
+ m_close(&p->mproc);
+ mproc_enable(&p->mproc);
+ }
+
+ if (TAILQ_FIRST(&procs) == NULL)
+ smtp_configure();
+}
+
+void
+filter_event(uint64_t id, int event)
+{
+ struct filter_session *s;
+
+ if (event == EVENT_DISCONNECT)
+ /* On disconnect, the session is virtualy dead */
+ s = tree_xpop(&sessions, id);
+ else
+ s = tree_xget(&sessions, id);
+
+ filter_post_event(id, event, TAILQ_FIRST(s->filters), NULL);
+
+ if (event == EVENT_DISCONNECT) {
+ if (s->iev)
+ io_free(s->iev);
+ if (s->ofile)
+ fclose(s->ofile);
+ free(s);
+ }
+}
+
+void
+filter_connect(uint64_t id, const struct sockaddr *local,
+ const struct sockaddr *remote, const char *host, const char *filter)
+{
+ struct filter_session *s;
+ struct filter_query *q;
+
+ s = xcalloc(1, sizeof(*s), "filter_event");
+ s->id = id;
+ if (filter == NULL)
+ filter = "<no-filter>";
+ s->filters = dict_xget(&chains, filter);
+ tree_xset(&sessions, s->id, s);
+
+ filter_event(id, EVENT_CONNECT);
+ q = filter_query(s, QUERY_CONNECT);
+
+ memmove(&q->u.connect.local, local, SA_LEN(local));
+ memmove(&q->u.connect.remote, remote, SA_LEN(remote));
+ strlcpy(q->u.connect.hostname, host, sizeof(q->u.connect.hostname));
+
+ q->smtp.status = FILTER_OK;
+ q->smtp.code = 0;
+ q->smtp.response = NULL;
+
+ filter_drain_query(q);
+}
+
+void
+filter_mailaddr(uint64_t id, int type, const struct mailaddr *maddr)
+{
+ struct filter_session *s;
+ struct filter_query *q;
+
+ s = tree_xget(&sessions, id);
+ q = filter_query(s, type);
+
+ strlcpy(q->u.maddr.user, maddr->user, sizeof(q->u.maddr.user));
+ strlcpy(q->u.maddr.domain, maddr->domain, sizeof(q->u.maddr.domain));
+
+ filter_drain_query(q);
+}
+
+void
+filter_line(uint64_t id, int type, const char *line)
+{
+ struct filter_session *s;
+ struct filter_query *q;
+
+ s = tree_xget(&sessions, id);
+ q = filter_query(s, type);
+
+ if (line)
+ strlcpy(q->u.line, line, sizeof(q->u.line));
+
+ filter_drain_query(q);
+}
+
+void
+filter_eom(uint64_t id, int type, size_t datalen)
+{
+ struct filter_session *s;
+ struct filter_query *q;
+
+ s = tree_xget(&sessions, id);
+ q = filter_query(s, type);
+ q->u.datalen = datalen;
+
+ filter_drain_query(q);
+}
+
+static void
+filter_set_sink(struct filter_session *s, int sink)
+{
+ struct mproc *p;
+
+ while (s->fcurr) {
+ if (s->fcurr->proc->hooks & HOOK_DATALINE) {
+ log_trace(TRACE_FILTERS, "filter: sending fd %d to %s",
+ sink, filter_to_text(s->fcurr));
+ p = &s->fcurr->proc->mproc;
+ m_create(p, IMSG_FILTER_PIPE, 0, 0, sink);
+ m_add_id(p, s->id);
+ m_close(p);
+ return;
+ }
+ s->fcurr = TAILQ_PREV(s->fcurr, filter_lst, entry);
+ }
+
+ log_trace(TRACE_FILTERS, "filter: chain input is %d", sink);
+ smtp_filter_fd(s->id, sink);
+}
+
+void
+filter_build_fd_chain(uint64_t id, int sink)
+{
+ struct filter_session *s;
+ int fd;
+
+ s = tree_xget(&sessions, id);
+ s->fcurr = TAILQ_LAST(s->filters, filter_lst);
+
+ fd = filter_tx(s, sink);
+ filter_set_sink(s, fd);
+}
+
+void
+filter_post_event(uint64_t id, int event, struct filter *f, struct filter *end)
+{
+ for(; f && f != end; f = TAILQ_NEXT(f, entry)) {
+ log_trace(TRACE_FILTERS, "filter: post-event event=%s filter=%s",
+ event_to_str(event), f->proc->mproc.name);
+
+ m_create(&f->proc->mproc, IMSG_FILTER_EVENT, 0, 0, -1);
+ m_add_id(&f->proc->mproc, id);
+ m_add_int(&f->proc->mproc, event);
+ m_close(&f->proc->mproc);
+ }
+}
+
+static struct filter_query *
+filter_query(struct filter_session *s, int type)
+{
+ struct filter_query *q;
+
+ q = xcalloc(1, sizeof(*q), "filter_query");
+ q->qid = generate_uid();
+ q->session = s;
+ q->type = type;
+
+ q->state = QUERY_READY;
+ q->current = TAILQ_FIRST(s->filters);
+
+ log_trace(TRACE_FILTERS, "filter: new query %s", query_to_str(type));
+
+ return (q);
+}
+
+static void
+filter_drain_query(struct filter_query *q)
+{
+ log_trace(TRACE_FILTERS, "filter: filter_drain_query %s",
+ filter_query_to_text(q));
+
+ /*
+ * The query must be passed through all filters that registered
+ * a hook, until one rejects it.
+ */
+ while (q->state != QUERY_DONE) {
+ /* Walk over all filters */
+ while (q->current) {
+ filter_run_query(q->current, q);
+ if (q->state == QUERY_RUNNING) {
+ log_trace(TRACE_FILTERS,
+ "filter: waiting for running query %s",
+ filter_query_to_text(q));
+ return;
+ }
+ }
+ q->state = QUERY_DONE;
+ }
+
+ /* Defer the response if the file is not closed yet. */
+ if (q->type == QUERY_EOM && q->session->ofile && q->smtp.status == FILTER_OK) {
+ log_debug("filter: deferring eom query...");
+ q->session->eom = q;
+ return;
+ }
+
+ filter_end_query(q);
+}
+
+static void
+filter_run_query(struct filter *f, struct filter_query *q)
+{
+ log_trace(TRACE_FILTERS,
+ "filter: running filter %s for query %s",
+ filter_to_text(f), filter_query_to_text(q));
+
+ m_create(&f->proc->mproc, IMSG_FILTER_QUERY, 0, 0, -1);
+ m_add_id(&f->proc->mproc, q->session->id);
+ m_add_id(&f->proc->mproc, q->qid);
+ m_add_int(&f->proc->mproc, q->type);
+
+ switch (q->type) {
+ case QUERY_CONNECT:
+ m_add_sockaddr(&f->proc->mproc,
+ (struct sockaddr *)&q->u.connect.local);
+ m_add_sockaddr(&f->proc->mproc,
+ (struct sockaddr *)&q->u.connect.remote);
+ m_add_string(&f->proc->mproc, q->u.connect.hostname);
+ break;
+ case QUERY_HELO:
+ m_add_string(&f->proc->mproc, q->u.line);
+ break;
+ case QUERY_MAIL:
+ case QUERY_RCPT:
+ m_add_mailaddr(&f->proc->mproc, &q->u.maddr);
+ break;
+ case QUERY_EOM:
+ m_add_u32(&f->proc->mproc, q->u.datalen);
+ break;
+ default:
+ break;
+ }
+ m_close(&f->proc->mproc);
+
+ tree_xset(&queries, q->qid, q);
+ q->state = QUERY_RUNNING;
+}
+
+static void
+filter_end_query(struct filter_query *q)
+{
+ struct filter_session *s = q->session;
+ const char *response = q->smtp.response;
+
+ log_trace(TRACE_FILTERS, "filter: filter_end_query %s",
+ filter_query_to_text(q));
+
+ if (q->type == QUERY_EOM && q->smtp.status == FILTER_OK) {
+ if (s->error || q->u.datalen != s->idatalen) {
+ response = "Internal error";
+ q->smtp.code = 451;
+ q->smtp.status = FILTER_FAIL;
+ if (!s->error)
+ log_warnx("filter: datalen mismatch on session %" PRIx64
+ ": %zu/%zu", s->id, s->idatalen, q->u.datalen);
+ }
+ }
+
+ log_trace(TRACE_FILTERS,
+ "filter: query %016"PRIx64" done: "
+ "status=%s code=%d response=\"%s\"",
+ q->qid,
+ status_to_str(q->smtp.status),
+ q->smtp.code,
+ response);
+
+ smtp_filter_response(s->id, q->type, q->smtp.status, q->smtp.code,
+ response);
+ free(q->smtp.response);
+ free(q);
+}
+
+static void
+filter_imsg(struct mproc *p, struct imsg *imsg)
+{
+ struct filter_proc *proc = p->data;
+ struct filter_session *s;
+ struct filter_query *q;
+ struct msg m;
+ const char *line;
+ uint64_t qid;
+ uint32_t datalen;
+ int type, status, code;
+
+ if (imsg == NULL) {
+ log_warnx("warn: filter \"%s\" closed unexpectedly", p->name);
+ fatalx("exiting");
+ }
+
+ log_trace(TRACE_FILTERS, "filter: imsg %s from procfilter %s",
+ filterimsg_to_str(imsg->hdr.type),
+ filter_proc_to_text(proc));
+
+ switch (imsg->hdr.type) {
+
+ case IMSG_FILTER_REGISTER:
+ if (proc->ready) {
+ log_warnx("warn: filter \"%s\" already registered",
+ proc->mproc.name);
+ exit(1);
+ }
+
+ m_msg(&m, imsg);
+ m_get_int(&m, &proc->hooks);
+ m_get_int(&m, &proc->flags);
+ m_end(&m);
+ proc->ready = 1;
+
+ log_debug("debug: filter \"%s\": hooks 0x%08x flags 0x%04x",
+ proc->mproc.name, proc->hooks, proc->flags);
+
+ TAILQ_FOREACH(proc, &procs, entry)
+ if (!proc->ready)
+ return;
+
+ smtp_configure();
+ break;
+
+ case IMSG_FILTER_RESPONSE:
+ m_msg(&m, imsg);
+ m_get_id(&m, &qid);
+ m_get_int(&m, &type);
+ if (type == QUERY_EOM)
+ m_get_u32(&m, &datalen);
+ m_get_int(&m, &status);
+ m_get_int(&m, &code);
+ if (m_is_eom(&m))
+ line = NULL;
+ else
+ m_get_string(&m, &line);
+ m_end(&m);
+
+ q = tree_xpop(&queries, qid);
+ if (q->type != type) {
+ log_warnx("warn: filter: type mismatch %d != %d",
+ q->type, type);
+ fatalx("exiting");
+ }
+ q->smtp.status = status;
+ if (code)
+ q->smtp.code = code;
+ if (line) {
+ free(q->smtp.response);
+ q->smtp.response = xstrdup(line, "filter_imsg");
+ }
+ q->state = (status == FILTER_OK) ? QUERY_READY : QUERY_DONE;
+ if (type == QUERY_EOM)
+ q->u.datalen = datalen;
+
+ q->current = TAILQ_NEXT(q->current, entry);
+ filter_drain_query(q);
+ break;
+
+ case IMSG_FILTER_PIPE:
+ m_msg(&m, imsg);
+ m_get_id(&m, &qid);
+ m_end(&m);
+
+ s = tree_xget(&sessions, qid);
+ s->fcurr = TAILQ_PREV(s->fcurr, filter_lst, entry);
+ filter_set_sink(s, imsg->fd);
+ break;
+
+ default:
+ log_warnx("warn: bad imsg from filter %s", p->name);
+ exit(1);
+ }
+}
+
+static int
+filter_tx(struct filter_session *s, int sink)
+{
+ int sp[2];
+
+ s->idatalen = 0;
+ s->eom = NULL;
+ s->error = 0;
+
+ if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, sp) == -1) {
+ log_warn("warn: filter: socketpair");
+ return (-1);
+ }
+
+ if ((s->ofile = fdopen(sink, "w")) == NULL) {
+ log_warn("warn: filter: fdopen");
+ close(sp[0]);
+ close(sp[1]);
+ return (-1);
+ }
+
+ io_set_nonblocking(sp[0]);
+ io_set_nonblocking(sp[1]);
+
+ s->iev = io_new();
+ io_set_callback(s->iev, filter_tx_io, s);
+ io_set_fd(s->iev, sp[0]);
+ io_set_read(s->iev);
+
+ return (sp[1]);
+}
+
+static void
+filter_tx_io(struct io *io, int evt, void *arg)
+{
+ struct filter_session *s = arg;
+ size_t len, n;
+ char *data;
+
+ log_trace(TRACE_FILTERS, "filter: filter_tx_io(%p, %s)",
+ s, io_strevent(evt));
+
+ switch (evt) {
+ case IO_DATAIN:
+ data = io_data(s->iev);
+ len = io_datalen(s->iev);
+
+ log_trace(TRACE_FILTERS,
+ "filter: filter_tx_io: datain (%zu) for req %016"PRIx64"",
+ len, s->id);
+
+ n = fwrite(data, 1, len, s->ofile);
+ if (n != len) {
+ log_warnx("warn: filter_tx_io: fwrite %zu/%zu", n, len);
+ s->error = 1;
+ break;
+ }
+ s->idatalen += n;
+ io_drop(s->iev, n);
+ return;
+
+ case IO_DISCONNECTED:
+ log_trace(TRACE_FILTERS,
+ "debug: filter: tx done (%zu) for req %016"PRIx64,
+ s->idatalen, s->id);
+ break;
+
+ default:
+ log_warn("warn: filter_tx_io: bad evt (%d) for req %016"PRIx64,
+ evt, s->id);
+ s->error = 1;
+ break;
+ }
+
+ io_free(s->iev);
+ s->iev = NULL;
+ fclose(s->ofile);
+ s->ofile = NULL;
+
+ /* deferred eom request */
+ if (s->eom) {
+ log_debug("filter: running eom query...");
+ filter_end_query(s->eom);
+ } else {
+ log_debug("filter: eom not received yet");
+ }
+}
+
+static const char *
+filter_query_to_text(struct filter_query *q)
+{
+ static char buf[1024];
+ char tmp[1024];
+
+ tmp[0] = '\0';
+
+ switch (q->type) {
+ case QUERY_CONNECT:
+ strlcat(tmp, "=", sizeof tmp);
+ strlcat(tmp, ss_to_text(&q->u.connect.local),
+ sizeof tmp);
+ strlcat(tmp, " <-> ", sizeof tmp);
+ strlcat(tmp, ss_to_text(&q->u.connect.remote),
+ sizeof tmp);
+ strlcat(tmp, "(", sizeof tmp);
+ strlcat(tmp, q->u.connect.hostname, sizeof tmp);
+ strlcat(tmp, ")", sizeof tmp);
+ break;
+ case QUERY_MAIL:
+ case QUERY_RCPT:
+ snprintf(tmp, sizeof tmp, "=%s@%s",
+ q->u.maddr.user, q->u.maddr.domain);
+ break;
+ case QUERY_HELO:
+ snprintf(tmp, sizeof tmp, "=%s", q->u.line);
+ break;
+ default:
+ break;
+ }
+ snprintf(buf, sizeof buf, "%016"PRIx64"[%s%s,%s]",
+ q->qid, query_to_str(q->type), tmp,
+ filter_session_to_text(q->session));
+
+ return (buf);
+}
+
+static const char *
+filter_session_to_text(struct filter_session *s)
+{
+ static char buf[1024];
+
+ if (s == NULL)
+ return "filter_session@NULL";
+
+ snprintf(buf, sizeof(buf),
+ "filter_session@%p[datalen=%zu,eom=%p,ofile=%p]",
+ s, s->idatalen, s->eom, s->ofile);
+
+ return buf;
+}
+
+static const char *
+filter_to_text(struct filter *f)
+{
+ static char buf[1024];
+
+ snprintf(buf, sizeof buf, "filter:%s", filter_proc_to_text(f->proc));
+
+ return (buf);
+}
+
+static const char *
+filter_proc_to_text(struct filter_proc *proc)
+{
+ static char buf[1024];
+
+ snprintf(buf, sizeof buf, "%s[hooks=0x%08x,flags=0x%04x]",
+ proc->mproc.name, proc->hooks, proc->flags);
+
+ return (buf);
+}
+
+#define CASE(x) case x : return #x
+
+static const char *
+filterimsg_to_str(int imsg)
+{
+ switch (imsg) {
+ CASE(IMSG_FILTER_REGISTER);
+ CASE(IMSG_FILTER_EVENT);
+ CASE(IMSG_FILTER_QUERY);
+ CASE(IMSG_FILTER_PIPE);
+ CASE(IMSG_FILTER_RESPONSE);
+ default:
+ return "IMSG_FILTER_???";
+ }
+}
+
+static const char *
+query_to_str(int query)
+{
+ switch (query) {
+ CASE(QUERY_CONNECT);
+ CASE(QUERY_HELO);
+ CASE(QUERY_MAIL);
+ CASE(QUERY_RCPT);
+ CASE(QUERY_DATA);
+ CASE(QUERY_EOM);
+ CASE(QUERY_DATALINE);
+ default:
+ return "QUERY_???";
+ }
+}
+
+static const char *
+event_to_str(int event)
+{
+ switch (event) {
+ CASE(EVENT_CONNECT);
+ CASE(EVENT_RESET);
+ CASE(EVENT_DISCONNECT);
+ CASE(EVENT_TX_BEGIN);
+ CASE(EVENT_TX_COMMIT);
+ CASE(EVENT_TX_ROLLBACK);
+ default:
+ return "EVENT_???";
+ }
+}
+
+static const char *
+status_to_str(int status)
+{
+ switch (status) {
+ CASE(FILTER_OK);
+ CASE(FILTER_FAIL);
+ CASE(FILTER_CLOSE);
+ default:
+ return "FILTER_???";
+ }
+}
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 d30d55e9..7494c6ce 100644
--- a/smtpd/forward.c
+++ b/usr.sbin/smtpd/forward.c
@@ -16,6 +16,8 @@
* 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>
@@ -28,7 +30,12 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#ifdef HAVE_UTIL_H
#include <util.h>
+#endif
+#ifdef HAVE_LIBUTIL_H
+#include <libutil.h>
+#endif
#include <unistd.h>
#include <limits.h>
diff --git a/smtpd/iobuf.c b/usr.sbin/smtpd/iobuf.c
index f5d8b20a..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>
*
@@ -15,6 +15,8 @@
* 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>
@@ -174,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 d36210fd..e0a8a096 100644
--- a/smtpd/ioev.c
+++ b/usr.sbin/smtpd/ioev.c
@@ -15,6 +15,8 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
+#include "includes.h"
+
#include <sys/types.h>
#include <sys/queue.h>
#include <sys/socket.h>
@@ -749,10 +751,10 @@ io_connect(struct io *io, const struct sockaddr *sa, const struct sockaddr *bsa)
io_set_nonblocking(sock);
io_set_nolinger(sock);
- if (bsa && bind(sock, bsa, bsa->sa_len) == -1)
+ if (bsa && bind(sock, bsa, SA_LEN(bsa)) == -1)
goto fail;
- if (connect(sock, sa, sa->sa_len) == -1)
+ if (connect(sock, sa, SA_LEN(sa)) == -1)
if (errno != EINPROGRESS)
goto fail;
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/usr.sbin/smtpd/libressl.c b/usr.sbin/smtpd/libressl.c
new file mode 100644
index 00000000..57d74389
--- /dev/null
+++ b/usr.sbin/smtpd/libressl.c
@@ -0,0 +1,213 @@
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to. The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code. The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * 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 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. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * "This product includes cryptographic software written by
+ * Eric Young (eay@cryptsoft.com)"
+ * The word 'cryptographic' can be left out if the rouines from the library
+ * being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ * the apps directory (application code) you must include an acknowledgement:
+ * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``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 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.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed. i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.]
+ */
+
+/*
+ * SSL operations needed when running in a privilege separated environment.
+ * Adapted from openssl's ssl_rsa.c by Pierre-Yves Ritschard .
+ */
+
+#include "includes.h"
+
+#include <sys/types.h>
+
+#include <limits.h>
+#include <unistd.h>
+#include <stdio.h>
+
+#include <openssl/err.h>
+#include <openssl/bio.h>
+#include <openssl/objects.h>
+#include <openssl/evp.h>
+#include <openssl/x509.h>
+#include <openssl/pem.h>
+#include <openssl/ssl.h>
+
+#include "log.h"
+#include "ssl.h"
+
+#define SSL_ECDH_CURVE "prime256v1"
+
+/*
+ * Read a bio that contains our certificate in "PEM" format,
+ * possibly followed by a sequence of CA certificates that should be
+ * sent to the peer in the Certificate message.
+ */
+static int
+ssl_ctx_use_certificate_chain_bio(SSL_CTX *ctx, BIO *in)
+{
+ int ret = 0;
+ X509 *x = NULL;
+
+ ERR_clear_error(); /* clear error stack for SSL_CTX_use_certificate() */
+
+ x = PEM_read_bio_X509_AUX(in, NULL, ctx->default_passwd_callback,
+ ctx->default_passwd_callback_userdata);
+ if (x == NULL) {
+ SSLerr(SSL_F_SSL_CTX_USE_CERTIFICATE_CHAIN_FILE, ERR_R_PEM_LIB);
+ goto end;
+ }
+
+ ret = SSL_CTX_use_certificate(ctx, x);
+
+ if (ERR_peek_error() != 0)
+ ret = 0;
+ /* Key/certificate mismatch doesn't imply ret==0 ... */
+ if (ret) {
+ /*
+ * If we could set up our certificate, now proceed to
+ * the CA certificates.
+ */
+ X509 *ca;
+ int r;
+ unsigned long err;
+
+ if (ctx->extra_certs != NULL) {
+ sk_X509_pop_free(ctx->extra_certs, X509_free);
+ ctx->extra_certs = NULL;
+ }
+
+ while ((ca = PEM_read_bio_X509(in, NULL,
+ ctx->default_passwd_callback,
+ ctx->default_passwd_callback_userdata)) != NULL) {
+ r = SSL_CTX_add_extra_chain_cert(ctx, ca);
+ if (!r) {
+ X509_free(ca);
+ ret = 0;
+ goto end;
+ }
+ /*
+ * Note that we must not free r if it was successfully
+ * added to the chain (while we must free the main
+ * certificate, since its reference count is increased
+ * by SSL_CTX_use_certificate).
+ */
+ }
+
+ /* When the while loop ends, it's usually just EOF. */
+ err = ERR_peek_last_error();
+ if (ERR_GET_LIB(err) == ERR_LIB_PEM &&
+ ERR_GET_REASON(err) == PEM_R_NO_START_LINE)
+ ERR_clear_error();
+ else
+ ret = 0; /* some real error */
+ }
+
+end:
+ if (x != NULL)
+ X509_free(x);
+ return (ret);
+}
+
+int
+SSL_CTX_use_certificate_chain_mem(SSL_CTX *ctx, void *buf, int len)
+{
+ BIO *in;
+ int ret = 0;
+
+ in = BIO_new_mem_buf(buf, len);
+ if (in == NULL) {
+ SSLerr(SSL_F_SSL_CTX_USE_CERTIFICATE_CHAIN_FILE, ERR_R_BUF_LIB);
+ goto end;
+ }
+
+ ret = ssl_ctx_use_certificate_chain_bio(ctx, in);
+
+end:
+ BIO_free(in);
+ return (ret);
+}
+
+#ifndef HAVE_SSL_CTX_SET_ECDH_AUTO
+void
+SSL_CTX_set_ecdh_auto(SSL_CTX *ctx, int enable)
+{
+ int nid;
+ EC_KEY *ecdh;
+
+ if (!enable)
+ return;
+
+ if ((nid = OBJ_sn2nid(SSL_ECDH_CURVE)) == 0) {
+ ssl_error("ssl_set_ecdh_auto");
+ fatal("ssl_set_ecdh_auto: unknown curve name "
+ SSL_ECDH_CURVE);
+ }
+
+ if ((ecdh = EC_KEY_new_by_curve_name(nid)) == NULL) {
+ ssl_error("ssl_set_ecdh_auto");
+ fatal("ssl_set_ecdh_auto: unable to create curve "
+ SSL_ECDH_CURVE);
+ }
+
+ SSL_CTX_set_tmp_ecdh(ctx, ecdh);
+ SSL_CTX_set_options(ctx, SSL_OP_SINGLE_ECDH_USE);
+ EC_KEY_free(ecdh);
+}
+#endif
+
+#ifndef HAVE_SSL_CTX_SET_DH_AUTO
+void
+SSL_CTX_set_dh_auto(SSL_CTX *ctx, int enable)
+{
+ if (!enable)
+ return;
+
+ /* stub until OpenSSL catches up with this ... */
+ log_warnx("OpenSSL does not support SSL_CTX_set_dh_auto (yet ?)");
+ return;
+}
+#endif
diff --git a/smtpd/limit.c b/usr.sbin/smtpd/limit.c
index e7d0cb17..25e7a026 100644
--- a/smtpd/limit.c
+++ b/usr.sbin/smtpd/limit.c
@@ -16,6 +16,8 @@
* 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>
diff --git a/smtpd/lka.c b/usr.sbin/smtpd/lka.c
index 0d4b43db..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>
@@ -18,6 +18,8 @@
* 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>
@@ -31,6 +33,8 @@
#include <err.h>
#include <errno.h>
#include <event.h>
+#include <netdb.h>
+#include <grp.h> /* needed for setgroups */
#include <imsg.h>
#include <openssl/err.h>
#include <openssl/ssl.h>
@@ -94,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;
@@ -274,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);
@@ -287,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)
@@ -310,10 +320,12 @@ lka_imsg(struct mproc *p, struct imsg *imsg)
/* fork & exec tables that need it */
table_open_all(env);
+#if HAVE_PLEDGE
/* revoke proc & exec */
if (pledge("stdio rpath inet dns getpw recvfd sendfd",
NULL) == -1)
err(1, "pledge");
+#endif
/* setup proc registering task */
evtimer_set(&ev_proc_ready, proc_timeout, &ev_proc_ready);
@@ -362,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:
@@ -605,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:
@@ -706,9 +715,11 @@ lka(void)
lka_report_init();
lka_filter_init();
+#if HAVE_PLEDGE
/* proc & exec will be revoked before serving requests */
if (pledge("stdio rpath inet dns getpw recvfd sendfd proc exec", NULL) == -1)
err(1, "pledge");
+#endif
event_dispatch();
fatalx("exited event loop");
diff --git a/smtpd/lka_filter.c b/usr.sbin/smtpd/lka_filter.c
index 695ce4c3..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>
@@ -16,6 +16,8 @@
* 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>
@@ -35,7 +37,7 @@
#include "smtpd.h"
#include "log.h"
-#define PROTOCOL_VERSION "0.4"
+#define PROTOCOL_VERSION "0.6"
struct filter;
struct filter_session;
@@ -54,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);
@@ -78,6 +82,7 @@ struct filter_session {
int fcrdns;
char *helo;
+ char *username;
char *mail_from;
enum filter_phase phase;
@@ -95,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 {
@@ -126,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)
{
@@ -215,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
@@ -292,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)
@@ -307,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);
@@ -342,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);
@@ -441,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;
@@ -604,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",
@@ -653,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;
}
@@ -690,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:
@@ -753,13 +954,13 @@ filter_protocol_query(struct filter *filter, uint64_t token, uint64_t reqid, con
n = io_printf(lka_proc_get_io(filter->proc),
"filter|%s|%lld.%06ld|smtp-in|%s|%016"PRIx64"|%016"PRIx64"|%s|%s\n",
PROTOCOL_VERSION,
- tv.tv_sec, tv.tv_usec,
+ (long long int)tv.tv_sec, tv.tv_usec,
phase, reqid, token, fs->rdns, param);
else
n = io_printf(lka_proc_get_io(filter->proc),
"filter|%s|%lld.%06ld|smtp-in|%s|%016"PRIx64"|%016"PRIx64"|%s\n",
PROTOCOL_VERSION,
- tv.tv_sec, tv.tv_usec,
+ (long long int)tv.tv_sec, tv.tv_usec,
phase, reqid, token, param);
if (n == -1)
fatalx("failed to write to processor");
@@ -777,7 +978,7 @@ filter_data_query(struct filter *filter, uint64_t token, uint64_t reqid, const c
"filter|%s|%lld.%06ld|smtp-in|data-line|"
"%016"PRIx64"|%016"PRIx64"|%s\n",
PROTOCOL_VERSION,
- tv.tv_sec, tv.tv_usec,
+ (long long int)tv.tv_sec, tv.tv_usec,
reqid, token, line);
if (n == -1)
fatalx("failed to write to processor");
@@ -914,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;
@@ -1015,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);
}
@@ -1044,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 ed1fd36f..999e01d6 100644
--- a/smtpd/lka_session.c
+++ b/usr.sbin/smtpd/lka_session.c
@@ -17,6 +17,8 @@
* 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>
@@ -541,7 +543,7 @@ lka_submit(struct lka_session *lks, struct rule *rule, struct expandnode *xn)
"run with %s privileges", SMTPD_USER);
if (xn->type == EXPAND_FILENAME)
- format = "/usr/libexec/mail.mboxfile -f %%{mbox.from} %s";
+ format = PATH_LIBEXEC"/mail.mboxfile -f %%{mbox.from} %s";
else if (xn->type == EXPAND_FILTER)
format = "%s";
(void)snprintf(ep->mda_exec, sizeof(ep->mda_exec),
diff --git a/smtpd/log.c b/usr.sbin/smtpd/log.c
index 7ec8ca42..14f681e3 100644
--- a/smtpd/log.c
+++ b/usr.sbin/smtpd/log.c
@@ -16,6 +16,8 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
+#include "includes.h"
+
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
diff --git a/smtpd/log.h b/usr.sbin/smtpd/log.h
index 22bb4164..81d0973c 100644
--- a/smtpd/log.h
+++ b/usr.sbin/smtpd/log.h
@@ -19,8 +19,14 @@
#ifndef LOG_H
#define LOG_H
+#include "openbsd-compat.h"
+
+#include <syslog.h>
+
#include <stdarg.h>
+#ifdef HAVE_SYS_CDEFS_H
#include <sys/cdefs.h>
+#endif
void log_init(int, int);
void log_procinit(const char *);
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 c12561cf..90b89990 100644
--- a/smtpd/mail.lmtp.c
+++ b/usr.sbin/smtpd/mail.lmtp.c
@@ -14,6 +14,8 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
+#include "includes.h"
+
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
@@ -41,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;
@@ -62,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;
@@ -75,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;
}
@@ -85,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);
@@ -97,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;
@@ -171,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;
@@ -195,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] == '/')
@@ -207,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");
@@ -226,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");
@@ -241,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;
@@ -260,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 5e558884..fe6adba6 100644
--- a/smtpd/mail.maildir.c
+++ b/usr.sbin/smtpd/mail.maildir.c
@@ -14,6 +14,8 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
+#include "includes.h"
+
#ifndef nitems
#define nitems(_a) (sizeof((_a)) / sizeof((_a)[0]))
#endif
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 96fa2e02..097a8d96 100644
--- a/smtpd/mail.mboxfile.c
+++ b/usr.sbin/smtpd/mail.mboxfile.c
@@ -14,6 +14,8 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
+#include "includes.h"
+
#include <sys/types.h>
#include <sys/stat.h>
@@ -27,6 +29,7 @@
#include <stdlib.h>
#include <string.h>
#include <sysexits.h>
+#include <time.h>
#include <unistd.h>
static void mboxfile_engine(const char *sender, const char *filename);
@@ -72,7 +75,13 @@ mboxfile_engine(const char *sender, const char *filename)
time(&now);
+#ifndef O_EXLOCK
+#define O_EXLOCK 0
+#endif
fd = open(filename, O_CREAT | O_APPEND | O_WRONLY | O_EXLOCK, 0600);
+#ifndef HAVE_O_EXLOCK
+ /* XXX : do something! */
+#endif
if (fd == -1)
err(EX_TEMPFAIL, NULL);
@@ -94,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 f9fb3236..23958071 100644
--- a/smtpd/mail.mda.c
+++ b/usr.sbin/smtpd/mail.mda.c
@@ -14,6 +14,8 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
+#include "includes.h"
+
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
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 a15470d7..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>
@@ -16,6 +16,8 @@
* 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>
@@ -80,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;
@@ -93,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 dd8bc8d3..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>
@@ -17,6 +17,11 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
+#include "includes.h"
+
+#ifdef HAVE_SYS_FILE_H
+#include <sys/file.h> /* Needed for flock */
+#endif
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/tree.h>
@@ -24,7 +29,13 @@
#include <sys/socket.h>
#include <ctype.h>
+#ifdef HAVE_DB_H
#include <db.h>
+#elif defined(HAVE_DB1_DB_H)
+#include <db1/db.h>
+#elif defined(HAVE_DB_185_H)
+#include <db_185.h>
+#endif
#include <err.h>
#include <errno.h>
#include <event.h>
@@ -36,12 +47,17 @@
#include <syslog.h>
#include <unistd.h>
#include <limits.h>
+#ifdef HAVE_UTIL_H
#include <util.h>
+#endif
+#ifdef HAVE_LIBUTIL_H
+#include <libutil.h>
+#endif
#include "smtpd.h"
#include "log.h"
-#define PATH_ALIASES "/etc/mail/aliases"
+#define PATH_ALIASES SMTPD_CONFDIR "/aliases"
static void usage(void);
static int parse_map(DB *, int *, char *);
@@ -53,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;
@@ -83,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);
@@ -161,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 765acda9..5e8fec19 100644
--- a/smtpd/mda.c
+++ b/usr.sbin/smtpd/mda.c
@@ -19,6 +19,8 @@
* 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>
@@ -28,6 +30,7 @@
#include <err.h>
#include <errno.h>
#include <event.h>
+#include <grp.h> /* needed for setgroups */
#include <imsg.h>
#include <inttypes.h>
#include <pwd.h>
@@ -39,7 +42,11 @@
#include <time.h>
#include <unistd.h>
#include <limits.h>
+#if defined(HAVE_VIS_H) && !defined(BROKEN_STRNVIS)
#include <vis.h>
+#else
+#include "bsd-vis.h"
+#endif
#include "smtpd.h"
#include "log.h"
@@ -521,8 +528,8 @@ mda_getlastline(int fd, char *dst, size_t dstsz)
size_t sz = 0;
ssize_t len;
int out = 0;
-
- if (lseek(fd, 0, SEEK_SET) == -1) {
+
+ if (lseek(fd, 0, SEEK_SET) < 0) {
log_warn("warn: mda: lseek");
close(fd);
return (-1);
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 23754070..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>
@@ -16,6 +16,8 @@
* 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>
@@ -40,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;
@@ -72,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 4558305e..b672e492 100644
--- a/smtpd/mda_variables.c
+++ b/usr.sbin/smtpd/mda_variables.c
@@ -17,6 +17,8 @@
* 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>
diff --git a/smtpd/mproc.c b/usr.sbin/smtpd/mproc.c
index aa19c7d4..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>
@@ -16,6 +16,8 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
+#include "includes.h"
+
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/tree.h>
@@ -60,9 +62,7 @@ mproc_fork(struct mproc *p, const char *path, char *argv[])
if (p->pid == 0) {
/* child process */
dup2(sp[0], STDIN_FILENO);
- if (closefrom(STDERR_FILENO + 1) == -1)
- exit(1);
-
+ closefrom(STDERR_FILENO + 1);
execv(path, argv);
err(1, "execv: %s", path);
}
@@ -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);
}
@@ -485,8 +486,8 @@ m_add_msgid(struct mproc *m, uint32_t v)
void
m_add_sockaddr(struct mproc *m, const struct sockaddr *sa)
{
- m_add_size(m, sa->sa_len);
- m_add(m, sa, sa->sa_len);
+ m_add_size(m, SA_LEN(sa));
+ m_add(m, sa, SA_LEN(sa));
}
void
@@ -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 6c817d00..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>
@@ -19,6 +19,8 @@
* 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>
@@ -31,6 +33,7 @@
#include <imsg.h>
#include <inttypes.h>
#include <netdb.h>
+#include <grp.h> /* needed for setgroups */
#include <limits.h>
#include <pwd.h>
#include <signal.h>
@@ -926,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);
@@ -1764,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;
@@ -2107,13 +2115,13 @@ mta_host(const struct sockaddr *sa)
struct mta_host key, *h;
struct sockaddr_storage ss;
- memmove(&ss, sa, sa->sa_len);
+ memmove(&ss, sa, SA_LEN(sa));
key.sa = (struct sockaddr*)&ss;
h = SPLAY_FIND(mta_host_tree, &hosts, &key);
if (h == NULL) {
h = xcalloc(1, sizeof(*h));
- h->sa = xmemdup(sa, sa->sa_len);
+ h->sa = xmemdup(sa, SA_LEN(sa));
SPLAY_INSERT(mta_host_tree, &hosts, h);
stat_increment("mta.host", 1);
}
@@ -2158,11 +2166,11 @@ mta_host_to_text(struct mta_host *h)
static int
mta_host_cmp(const struct mta_host *a, const struct mta_host *b)
{
- if (a->sa->sa_len < b->sa->sa_len)
+ if (SA_LEN(a->sa) < SA_LEN(b->sa))
return (-1);
- if (a->sa->sa_len > b->sa->sa_len)
+ if (SA_LEN(a->sa) > SA_LEN(b->sa))
return (1);
- return (memcmp(a->sa, b->sa, a->sa->sa_len));
+ return (memcmp(a->sa, b->sa, SA_LEN(a->sa)));
}
SPLAY_GENERATE(mta_host_tree, mta_host, entry, mta_host_cmp);
@@ -2237,7 +2245,7 @@ mta_source(const struct sockaddr *sa)
struct sockaddr_storage ss;
if (sa) {
- memmove(&ss, sa, sa->sa_len);
+ memmove(&ss, sa, SA_LEN(sa));
key.sa = (struct sockaddr*)&ss;
} else
key.sa = NULL;
@@ -2246,7 +2254,7 @@ mta_source(const struct sockaddr *sa)
if (s == NULL) {
s = xcalloc(1, sizeof(*s));
if (sa)
- s->sa = xmemdup(sa, sa->sa_len);
+ s->sa = xmemdup(sa, SA_LEN(sa));
SPLAY_INSERT(mta_source_tree, &sources, s);
stat_increment("mta.source", 1);
}
@@ -2291,11 +2299,11 @@ mta_source_cmp(const struct mta_source *a, const struct mta_source *b)
return ((b->sa == NULL) ? 0 : -1);
if (b->sa == NULL)
return (1);
- if (a->sa->sa_len < b->sa->sa_len)
+ if (SA_LEN(a->sa) < SA_LEN(b->sa))
return (-1);
- if (a->sa->sa_len > b->sa->sa_len)
+ if (SA_LEN(a->sa) > SA_LEN(b->sa))
return (1);
- return (memcmp(a->sa, b->sa, a->sa->sa_len));
+ return (memcmp(a->sa, b->sa, SA_LEN(a->sa)));
}
SPLAY_GENERATE(mta_source_tree, mta_source, entry, mta_source_cmp);
diff --git a/smtpd/mta_session.c b/usr.sbin/smtpd/mta_session.c
index 72969e0a..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>
@@ -19,6 +19,8 @@
* 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>
@@ -105,6 +107,8 @@ struct mta_session {
char *helo;
char *mxname;
+ char *username;
+
int flags;
int attempt;
@@ -128,6 +132,7 @@ struct mta_session {
struct mta_task *task;
struct mta_envelope *currevp;
FILE *datafp;
+ size_t datalen;
size_t failures;
@@ -163,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;
@@ -171,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)
{
@@ -201,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) {
@@ -349,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;
@@ -362,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);
@@ -476,7 +521,7 @@ mta_connect(struct mta_session *s)
if (s->relay->port)
portno = s->relay->port;
- memmove(&ss, s->route->dst->sa, s->route->dst->sa->sa_len);
+ memmove(&ss, s->route->dst->sa, SA_LEN(s->route->dst->sa));
sa = (struct sockaddr *)&ss;
if (sa->sa_family == AF_INET)
@@ -553,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:
@@ -613,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;
@@ -628,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);
@@ -769,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;
@@ -814,6 +872,7 @@ again:
if (s->datafp) {
fclose(s->datafp);
s->datafp = NULL;
+ s->datalen = 0;
}
mta_send(s, "RSET");
break;
@@ -838,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;
@@ -850,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
@@ -910,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;
}
@@ -928,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;
}
@@ -937,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;
@@ -949,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;
@@ -976,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
@@ -1027,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
@@ -1035,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;
@@ -1057,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--;
@@ -1078,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);
@@ -1110,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);
@@ -1127,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;
@@ -1141,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);
@@ -1177,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;
}
@@ -1192,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",
@@ -1248,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
@@ -1255,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;
@@ -1322,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);
@@ -1344,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);
@@ -1560,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;
+ }
}
}
@@ -1670,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 2fff100e..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>
@@ -22,7 +22,10 @@
*/
%{
+#include "includes.h"
+
#include <sys/types.h>
+#include <sys/time.h>
#include <sys/queue.h>
#include <sys/tree.h>
#include <sys/socket.h>
@@ -43,13 +46,16 @@
#include <limits.h>
#include <netdb.h>
#include <pwd.h>
+#include <stdarg.h>
#include <resolv.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <syslog.h>
#include <unistd.h>
+#ifdef HAVE_UTIL_H
#include <util.h>
+#endif
#include <openssl/ssl.h>
@@ -105,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;
@@ -173,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
@@ -209,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'
@@ -310,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];
@@ -434,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);
@@ -443,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;
}
;
@@ -529,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;
@@ -566,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;
}
@@ -662,43 +684,42 @@ 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, "/usr/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, "/usr/libexec/mail.maildir");
+ asprintf(&dispatcher->u.local.command, PATH_LIBEXEC"/mail.maildir");
} dispatcher_local_options
| MAILDIR JUNK {
- asprintf(&dispatcher->u.local.command, "/usr/libexec/mail.maildir -j");
+ asprintf(&dispatcher->u.local.command, PATH_LIBEXEC"/mail.maildir -j");
} dispatcher_local_options
| MAILDIR STRING {
if (strncmp($2, "~/", 2) == 0)
asprintf(&dispatcher->u.local.command,
- "/usr/libexec/mail.maildir \"%%{user.directory}/%s\"", $2+2);
+ PATH_LIBEXEC"/mail.maildir \"%%{user.directory}/%s\"", $2+2);
else
asprintf(&dispatcher->u.local.command,
- "/usr/libexec/mail.maildir \"%s\"", $2);
+ PATH_LIBEXEC"/mail.maildir \"%s\"", $2);
} dispatcher_local_options
| MAILDIR STRING JUNK {
if (strncmp($2, "~/", 2) == 0)
asprintf(&dispatcher->u.local.command,
- "/usr/libexec/mail.maildir -j \"%%{user.directory}/%s\"", $2+2);
+ PATH_LIBEXEC"/mail.maildir -j \"%%{user.directory}/%s\"", $2+2);
else
asprintf(&dispatcher->u.local.command,
- "/usr/libexec/mail.maildir -j \"%s\"", $2);
+ PATH_LIBEXEC"/mail.maildir -j \"%s\"", $2);
} dispatcher_local_options
| LMTP STRING {
asprintf(&dispatcher->u.local.command,
- "/usr/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,
- "/usr/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,
- "/usr/libexec/mail.mda \"%s\"", $2);
+ PATH_LIBEXEC"/mail.mda \"%s\"", $2);
} dispatcher_local_options
| FORWARD_ONLY {
dispatcher->u.local.forward_only = 1;
@@ -818,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");
@@ -856,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");
@@ -1021,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;
@@ -1228,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>");
@@ -1284,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:
@@ -1330,6 +1547,9 @@ filter_action_builtin_nojunk
| JUNK {
filter_config->junk = 1;
}
+| BYPASS {
+ filter_config->bypass = 1;
+}
;
filter_action_builtin_nojunk:
@@ -1400,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;
@@ -1442,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;
@@ -1452,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 |
@@ -1461,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;
@@ -1485,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;
@@ -1509,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;
@@ -1565,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
@@ -1579,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) {
@@ -1601,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;
@@ -1609,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)
@@ -1624,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;
@@ -1661,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;
}
@@ -1836,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;
}
@@ -1865,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);
@@ -1875,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 {
@@ -1957,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;
}
@@ -1985,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);
@@ -2332,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 },
@@ -2915,10 +3208,11 @@ 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
l->ss.ss_len = sizeof(struct sockaddr *);
+#endif
l->local = 1;
conf->sc_sock_listener = l;
config_listener(l, lo);
@@ -3050,7 +3344,9 @@ host_v4(struct listen_opts *lo)
h = xcalloc(1, sizeof(*h));
sain = (struct sockaddr_in *)&h->ss;
+#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
sain->sin_len = sizeof(struct sockaddr_in);
+#endif
sain->sin_family = AF_INET;
sain->sin_addr.s_addr = ina.s_addr;
sain->sin_port = lo->port;
@@ -3078,7 +3374,9 @@ host_v6(struct listen_opts *lo)
h = xcalloc(1, sizeof(*h));
sin6 = (struct sockaddr_in6 *)&h->ss;
+#ifdef HAVE_STRUCT_SOCKADDR_IN6_SIN6_LEN
sin6->sin6_len = sizeof(struct sockaddr_in6);
+#endif
sin6->sin6_family = AF_INET6;
sin6->sin6_port = lo->port;
memcpy(&sin6->sin6_addr, &ina6, sizeof(ina6));
@@ -3121,7 +3419,9 @@ host_dns(struct listen_opts *lo)
h->ss.ss_family = res->ai_family;
if (res->ai_family == AF_INET) {
sain = (struct sockaddr_in *)&h->ss;
+#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
sain->sin_len = sizeof(struct sockaddr_in);
+#endif
sain->sin_addr.s_addr = ((struct sockaddr_in *)
res->ai_addr)->sin_addr.s_addr;
sain->sin_port = lo->port;
@@ -3129,7 +3429,9 @@ host_dns(struct listen_opts *lo)
h->local = 1;
} else {
sin6 = (struct sockaddr_in6 *)&h->ss;
+#ifdef HAVE_STRUCT_SOCKADDR_IN6_SIN6_LEN
sin6->sin6_len = sizeof(struct sockaddr_in6);
+#endif
memcpy(&sin6->sin6_addr, &((struct sockaddr_in6 *)
res->ai_addr)->sin6_addr, sizeof(struct in6_addr));
sin6->sin6_port = lo->port;
@@ -3173,7 +3475,9 @@ interface(struct listen_opts *lo)
case AF_INET:
sain = (struct sockaddr_in *)&h->ss;
*sain = *(struct sockaddr_in *)p->ifa_addr;
+#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
sain->sin_len = sizeof(struct sockaddr_in);
+#endif
sain->sin_port = lo->port;
if (sain->sin_addr.s_addr == htonl(INADDR_LOOPBACK))
h->local = 1;
@@ -3182,7 +3486,9 @@ interface(struct listen_opts *lo)
case AF_INET6:
sin6 = (struct sockaddr_in6 *)&h->ss;
*sin6 = *(struct sockaddr_in6 *)p->ifa_addr;
+#ifdef HAVE_STRUCT_SOCKADDR_IN6_SIN6_LEN
sin6->sin6_len = sizeof(struct sockaddr_in6);
+#endif
sin6->sin6_port = lo->port;
if (IN6_IS_ADDR_LOOPBACK(&sin6->sin6_addr))
h->local = 1;
@@ -3251,6 +3557,7 @@ bad:
int
is_if_in_group(const char *ifname, const char *groupname)
{
+#ifdef HAVE_STRUCT_IFGROUPREQ
unsigned int len;
struct ifgroupreq ifgr;
struct ifg_req *ifg;
@@ -3289,6 +3596,9 @@ is_if_in_group(const char *ifname, const char *groupname)
end:
close(s);
return ret;
+#else
+ return (0);
+#endif
}
static int
diff --git a/smtpd/parser.c b/usr.sbin/smtpd/parser.c
index df90e508..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>
@@ -16,6 +16,8 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
+#include "includes.h"
+
#include <sys/types.h>
#include <sys/queue.h>
#include <sys/socket.h>
@@ -219,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++)
@@ -281,7 +288,9 @@ text_to_sockaddr(struct sockaddr *sa, int family, const char *str)
in = (struct sockaddr_in *)sa;
memset(in, 0, sizeof *in);
+#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
in->sin_len = sizeof(struct sockaddr_in);
+#endif
in->sin_family = PF_INET;
in->sin_addr.s_addr = ina.s_addr;
return (0);
@@ -304,7 +313,9 @@ text_to_sockaddr(struct sockaddr *sa, int family, const char *str)
in6 = (struct sockaddr_in6 *)sa;
memset(in6, 0, sizeof *in6);
+#ifdef HAVE_STRUCT_SOCKADDR_IN6_SIN6_LEN
in6->sin6_len = sizeof(struct sockaddr_in6);
+#endif
in6->sin6_family = PF_INET6;
in6->sin6_addr = in6a;
@@ -313,7 +324,7 @@ text_to_sockaddr(struct sockaddr *sa, int family, const char *str)
if (IN6_IS_ADDR_LINKLOCAL(&in6a) ||
IN6_IS_ADDR_MC_LINKLOCAL(&in6a) ||
- IN6_IS_ADDR_MC_INTFACELOCAL(&in6a))
+ IN6_IS_ADDR_MC_NODELOCAL(&in6a))
if ((in6->sin6_scope_id = if_nametoindex(cp)))
return (0);
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 026d6473..1865b339 100644
--- a/smtpd/pony.c
+++ b/usr.sbin/smtpd/pony.c
@@ -16,6 +16,8 @@
* 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>
@@ -198,8 +200,10 @@ pony(void)
ca_engine_init();
+#if HAVE_PLEDGE
if (pledge("stdio inet unix recvfd sendfd", NULL) == -1)
err(1, "pledge");
+#endif
event_dispatch();
fatalx("exited event loop");
diff --git a/smtpd/proxy.c b/usr.sbin/smtpd/proxy.c
index 7bf976d8..fdaf4f27 100644
--- a/smtpd/proxy.c
+++ b/usr.sbin/smtpd/proxy.c
@@ -14,6 +14,8 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
+#include "includes.h"
+
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/tree.h>
diff --git a/smtpd/queue.c b/usr.sbin/smtpd/queue.c
index 8380c7b5..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>
@@ -18,6 +18,8 @@
* 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>
@@ -26,6 +28,8 @@
#include <err.h>
#include <event.h>
+#include <fcntl.h>
+#include <grp.h> /* needed for setgroups */
#include <imsg.h>
#include <inttypes.h>
#include <pwd.h>
@@ -673,8 +677,10 @@ queue(void)
tv.tv_usec = 10;
evtimer_add(&ev_qload, &tv);
+#if HAVE_PLEDGE
if (pledge("stdio rpath wpath cpath flock recvfd sendfd", NULL) == -1)
err(1, "pledge");
+#endif
event_dispatch();
fatalx("exited event loop");
@@ -686,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;
@@ -705,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);
@@ -724,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 5e7c38fa..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>
@@ -16,6 +16,8 @@
* 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>
@@ -730,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 d1cd4800..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>
@@ -16,12 +16,19 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
+#include "includes.h"
+
#include <sys/types.h>
+#if HAVE_SYS_MOUNT_H
#include <sys/mount.h>
+#endif
#include <sys/queue.h>
#include <sys/tree.h>
#include <sys/socket.h>
#include <sys/stat.h>
+#ifdef HAVE_SYS_STATFS_H
+#include <sys/statfs.h>
+#endif
#include <ctype.h>
#include <dirent.h>
@@ -228,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;
@@ -340,8 +346,10 @@ queue_fs_message_walk(uint64_t *evpid, char *buf, size_t len,
(void)snprintf(msgid_str, sizeof msgid_str, "%08" PRIx32, msgid);
while ((dp = readdir(dir)) != NULL) {
+#if defined(HAVE_STRUCT_DIR_D_TYPE)
if (dp->d_type != DT_REG)
continue;
+#endif
/* ignore files other than envelopes */
if (strlen(dp->d_name) != 16 ||
@@ -410,6 +418,7 @@ queue_fs_envelope_walk(uint64_t *evpid, char *buf, size_t len)
static int
fsqueue_check_space(void)
{
+#ifdef __OpenBSD__
struct statfs buf;
uint64_t used;
uint64_t total;
@@ -453,7 +462,7 @@ fsqueue_check_space(void)
log_warnx("warn: temporarily rejecting messages");
return 0;
}
-
+#endif
return 1;
}
@@ -616,7 +625,12 @@ fsqueue_qwalk(void *hdl, uint64_t *evpid)
break;
if (e->fts_namelen != 16)
break;
+#if HAVE_STRUCT_STAT_ST_MTIM
if (timespeccmp(&e->fts_statp->st_mtim, &startup, >))
+#endif
+#if HAVE_STRUCT_STAT_ST_MTIMSPEC
+ if (timespeccmp(&e->fts_statp->st_mtimspec, &startup, >))
+#endif
break;
tmp = NULL;
*evpid = strtoull(e->fts_name, &tmp, 16);
diff --git a/smtpd/queue_null.c b/usr.sbin/smtpd/queue_null.c
index d92f98a7..1e608be8 100644
--- a/smtpd/queue_null.c
+++ b/usr.sbin/smtpd/queue_null.c
@@ -16,6 +16,8 @@
* 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>
diff --git a/smtpd/queue_proc.c b/usr.sbin/smtpd/queue_proc.c
index 753271c1..d6e0f409 100644
--- a/smtpd/queue_proc.c
+++ b/usr.sbin/smtpd/queue_proc.c
@@ -16,6 +16,8 @@
* 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>
diff --git a/smtpd/queue_ram.c b/usr.sbin/smtpd/queue_ram.c
index 56b9fa5b..50ce17e1 100644
--- a/smtpd/queue_ram.c
+++ b/usr.sbin/smtpd/queue_ram.c
@@ -16,6 +16,8 @@
* 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>
diff --git a/smtpd/report_smtp.c b/usr.sbin/smtpd/report_smtp.c
index ab17901f..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>
@@ -16,6 +16,8 @@
* 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>
@@ -36,7 +38,11 @@
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
+#if defined(HAVE_VIS_H) && !defined(BROKEN_STRNVIS)
#include <vis.h>
+#else
+#include "bsd-vis.h"
+#endif
#include "smtpd.h"
#include "log.h"
@@ -174,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);
@@ -191,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);
}
@@ -200,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);
@@ -217,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 ce047b24..f0f0f8ea 100644
--- a/smtpd/resolver.c
+++ b/usr.sbin/smtpd/resolver.c
@@ -16,6 +16,8 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
+#include "includes.h"
+
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/tree.h>
@@ -65,6 +67,9 @@ static void resolver_res_query_cb(struct asr_result *, void *);
static int request_cmp(struct request *, struct request *);
SPLAY_PROTOTYPE(reqtree, request, entry, request_cmp);
+/* musl work-around */
+void portable_freeaddrinfo(struct addrinfo *);
+
static struct reqtree reqs;
void
@@ -216,7 +221,7 @@ resolver_dispatch_request(struct mproc *proc, struct imsg *imsg)
if ((s = calloc(1, sizeof(*s))) &&
(s->host = malloc(NI_MAXHOST)) &&
(s->serv = malloc(NI_MAXSERV)) &&
- (q = getnameinfo_async(sa, sa->sa_len, s->host, NI_MAXHOST,
+ (q = getnameinfo_async(sa, SA_LEN(sa), s->host, NI_MAXHOST,
s->serv, NI_MAXSERV, flags, NULL)) &&
(event_asr_run(q, resolver_getnameinfo_cb, s))) {
s->reqid = reqid;
@@ -312,14 +317,14 @@ resolver_dispatch_result(struct mproc *proc, struct imsg *imsg)
m_get_string(&m, &cname);
m_end(&m);
- ai->ai_addr = malloc(ss.ss_len);
+ ai->ai_addr = malloc(SS_LEN(&ss));
if (ai->ai_addr == NULL) {
log_warn("%s: malloc", __func__);
free(ai);
break;
}
- memmove(ai->ai_addr, &ss, ss.ss_len);
+ memmove(ai->ai_addr, &ss, SS_LEN(&ss));
if (cname) {
ai->ai_canonname = strdup(cname);
@@ -406,7 +411,7 @@ resolver_getaddrinfo_cb(struct asr_result *ar, void *arg)
m_close(s->proc);
if (ar->ar_addrinfo)
- freeaddrinfo(ar->ar_addrinfo);
+ portable_freeaddrinfo(ar->ar_addrinfo);
free(s);
}
diff --git a/smtpd/rfc5322.c b/usr.sbin/smtpd/rfc5322.c
index 02ea7976..0af66772 100644
--- a/smtpd/rfc5322.c
+++ b/usr.sbin/smtpd/rfc5322.c
@@ -16,6 +16,8 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
+#include "includes.h"
+
#include <ctype.h>
#include <errno.h>
#include <limits.h>
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 db9d1aa5..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>
@@ -16,6 +16,8 @@
* 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>
@@ -152,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;
@@ -159,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 bbb9affa..786d36fb 100644
--- a/smtpd/runq.c
+++ b/usr.sbin/smtpd/runq.c
@@ -16,6 +16,8 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
+#include "includes.h"
+
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/queue.h>
diff --git a/smtpd/scheduler.c b/usr.sbin/smtpd/scheduler.c
index b3bda175..ea70a83d 100644
--- a/smtpd/scheduler.c
+++ b/usr.sbin/smtpd/scheduler.c
@@ -19,6 +19,8 @@
* 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>
@@ -30,6 +32,7 @@
#include <err.h>
#include <errno.h>
#include <event.h>
+#include <grp.h> /* needed for setgroups */
#include <imsg.h>
#include <inttypes.h>
#include <pwd.h>
@@ -470,8 +473,10 @@ scheduler(void)
evtimer_set(&ev, scheduler_timeout, NULL);
scheduler_reset_events();
+#if HAVE_PLEDGE
if (pledge("stdio", NULL) == -1)
err(1, "pledge");
+#endif
event_dispatch();
fatalx("exited event loop");
diff --git a/smtpd/scheduler_backend.c b/usr.sbin/smtpd/scheduler_backend.c
index 061f1129..ad2b4cab 100644
--- a/smtpd/scheduler_backend.c
+++ b/usr.sbin/smtpd/scheduler_backend.c
@@ -16,6 +16,8 @@
* 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>
diff --git a/smtpd/scheduler_null.c b/usr.sbin/smtpd/scheduler_null.c
index a8c43331..40db6205 100644
--- a/smtpd/scheduler_null.c
+++ b/usr.sbin/smtpd/scheduler_null.c
@@ -16,6 +16,8 @@
* 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>
diff --git a/smtpd/scheduler_proc.c b/usr.sbin/smtpd/scheduler_proc.c
index 0f8c44b9..5f4e8b70 100644
--- a/smtpd/scheduler_proc.c
+++ b/usr.sbin/smtpd/scheduler_proc.c
@@ -16,6 +16,8 @@
* 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>
diff --git a/smtpd/scheduler_ramqueue.c b/usr.sbin/smtpd/scheduler_ramqueue.c
index 8d5efc10..0c04fc0b 100644
--- a/smtpd/scheduler_ramqueue.c
+++ b/usr.sbin/smtpd/scheduler_ramqueue.c
@@ -17,6 +17,8 @@
* 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>
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 35776c4f..602fd0d6 100644
--- a/smtpd/smtp.c
+++ b/usr.sbin/smtpd/smtp.c
@@ -18,6 +18,8 @@
* 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>
@@ -26,6 +28,7 @@
#include <err.h>
#include <errno.h>
#include <event.h>
+#include <grp.h> /* needed for setgroups */
#include <imsg.h>
#include <netdb.h>
#include <pwd.h>
@@ -64,6 +67,8 @@ static void smtp_accepted(struct listener *, int, const struct sockaddr_storage
#define SMTP_FD_RESERVE 5
+#define getdtablecount() 0
+
static size_t sessions;
static size_t maxsessions;
@@ -146,10 +151,26 @@ smtp_setup_listeners(void)
fatal("smtpd: socket");
}
opt = 1;
+#ifdef SO_REUSEADDR
if (setsockopt(l->fd, SOL_SOCKET, SO_REUSEADDR, &opt,
sizeof(opt)) == -1)
fatal("smtpd: setsockopt");
- if (bind(l->fd, (struct sockaddr *)&l->ss, l->ss.ss_len) == -1)
+#else
+ if (setsockopt(l->fd, SOL_SOCKET, SO_REUSEPORT, &opt,
+ sizeof(opt)) < 0)
+ fatal("smtpd: setsockopt");
+#endif
+#ifdef IPV6_V6ONLY
+ /*
+ * If using IPv6, bind only to IPv6 if possible.
+ * This avoids ambiguities with IPv4-mapped IPv6 addresses.
+ */
+ if (l->ss.ss_family == AF_INET6)
+ if (setsockopt(l->fd, IPPROTO_IPV6, IPV6_V6ONLY, &opt,
+ sizeof(opt)) < 0)
+ fatal("smtpd: setsockopt");
+#endif
+ if (bind(l->fd, (struct sockaddr *)&l->ss, SS_LEN(&l->ss)) == -1)
fatal("smtpd: bind");
}
}
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 c6a6d52d..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>
@@ -19,6 +19,8 @@
* 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>
@@ -39,7 +41,11 @@
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
+#if defined(HAVE_VIS_H) && !defined(BROKEN_STRNVIS)
#include <vis.h>
+#else
+#include "bsd-vis.h"
+#endif
#include "smtpd.h"
#include "log.h"
@@ -253,6 +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;
@@ -430,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;
@@ -678,7 +710,7 @@ smtp_getaddrinfo_cb(void *arg, int gaierrno, struct addrinfo *ai0)
break;
}
}
- freeaddrinfo(ai0);
+ portable_freeaddrinfo(ai0);
}
smtp_lookup_servername(s);
@@ -689,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;
@@ -778,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 {
@@ -847,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;
@@ -894,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;
}
@@ -904,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",
@@ -943,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));
}
@@ -952,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;
}
@@ -961,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));
}
@@ -993,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);
@@ -1017,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) {
@@ -1084,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';
@@ -1109,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) {
@@ -1177,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;
@@ -1212,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);
/*
@@ -1640,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);
}
@@ -1715,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
@@ -1731,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);
@@ -1751,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",
@@ -2038,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));
@@ -2058,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
@@ -2093,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':
@@ -2116,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);
}
@@ -2160,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
@@ -2172,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)
@@ -2215,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);
}
@@ -2385,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;
@@ -2553,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;
@@ -2589,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);
}
@@ -2600,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);
}
@@ -2614,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;
@@ -2997,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 deb99c63..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>
@@ -16,6 +16,8 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
+#include "includes.h"
+
#include <sys/types.h>
#include <sys/socket.h>
@@ -147,19 +149,25 @@ main(int argc, char **argv)
fatal("SSL_CTX_set_ssl_version");
SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_NONE , NULL);
+#if HAVE_PLEDGE
if (pledge("stdio inet dns tmppath", NULL) == -1)
fatal("pledge");
+#endif
if (!noaction)
parse_message(stdin);
+#if HAVE_PLEDGE
if (pledge("stdio inet dns", NULL) == -1)
fatal("pledge");
+#endif
parse_server(server);
+#if HAVE_PLEDGE
if (pledge("stdio inet", NULL) == -1)
fatal("pledge");
+#endif
resume();
@@ -185,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';
@@ -292,7 +300,7 @@ parse_message(FILE *ifp)
if (len >= 2 && line[len - 2] == '\r' && line[len - 1] == '\n')
line[--len - 1] = '\n';
- if (fwrite(line, 1, len, mail.fp) != len)
+ if (fwrite(line, 1, len, mail.fp) != (size_t)len)
fatal("fwrite");
if (line[len - 1] != '\n' && fputc('\n', mail.fp) == EOF)
@@ -318,7 +326,7 @@ resume(void)
if (ai == NULL)
fatalx("no more host");
- getnameinfo(ai->ai_addr, ai->ai_addr->sa_len,
+ getnameinfo(ai->ai_addr, SA_LEN(ai->ai_addr),
host, sizeof(host), serv, sizeof(serv),
NI_NUMERICHOST | NI_NUMERICSERV);
log_debug("trying host %s port %s...", host, serv);
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 a985172a..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>
@@ -21,6 +21,8 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
+#include "includes.h"
+
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/queue.h>
@@ -29,6 +31,12 @@
#include <sys/wait.h>
#include <sys/stat.h>
+#include <net/if.h>
+/* #include <net/if_media.h> */
+/* #include <net/if_types.h> */
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
#include <err.h>
#include <errno.h>
#include <event.h>
@@ -43,17 +51,30 @@
#include <syslog.h>
#include <time.h>
#include <unistd.h>
+#if defined(HAVE_VIS_H) && !defined(BROKEN_STRNVIS)
#include <vis.h>
+#else
+#include "bsd-vis.h"
+#endif
#include <limits.h>
#include "smtpd.h"
#include "parser.h"
#include "log.h"
+#ifndef PATH_GZCAT
#define PATH_GZCAT "/usr/bin/gzcat"
+#endif
#define PATH_CAT "/bin/cat"
#define PATH_QUEUE "/queue"
+#ifndef PATH_ENCRYPT
#define PATH_ENCRYPT "/usr/bin/encrypt"
+#endif
+
+#ifndef HAVE_DB_API
+#define makemap(x, y, z) 1
+#endif
+
int srv_connect(void);
int srv_connected(void);
@@ -1041,9 +1062,20 @@ int
main(int argc, char **argv)
{
gid_t gid;
+ struct group *gr;
int privileged;
char *argv_mailq[] = { "show", "queue", NULL };
+#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;
@@ -1125,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();
@@ -1134,10 +1166,12 @@ sendmail_compat(int argc, char **argv)
if (setresgid(gid, gid, gid) == -1)
err(1, "setresgid");
+#if HAVE_PLEDGE
/* we'll reduce further down the road */
if (pledge("stdio rpath wpath cpath tmppath flock "
"dns getpw recvfd", NULL) == -1)
err(1, "pledge");
+#endif
sendmail = 1;
exit(enqueue(argc, argv, offlinefp));
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 3b9038cd..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>
@@ -32,11 +32,29 @@
#define SMTPD_VUSERNAME_SIZE (255 + 1)
#define SMTPD_SUBADDRESS_SIZE (255 + 1)
+#ifndef SMTPD_USER
#define SMTPD_USER "_smtpd"
+#endif
+#ifndef PATH_CHROOT
#define PATH_CHROOT "/var/empty"
+#endif
+#ifndef SMTPD_QUEUE_USER
#define SMTPD_QUEUE_USER "_smtpq"
+#endif
+#ifndef SMTPD_QUEUE_GROUP
#define SMTPD_QUEUE_GROUP "_smtpq"
+#endif
+#ifndef PATH_SPOOL
#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 1ec261e4..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>
@@ -18,6 +18,9 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
+#include "includes.h"
+
+#include <sys/file.h> /* Needed for flock */
#include <sys/types.h>
#include <sys/queue.h>
#include <sys/tree.h>
@@ -27,21 +30,44 @@
#include <sys/uio.h>
#include <sys/mman.h>
+#ifdef BSD_AUTH
#include <bsd_auth.h>
+#endif
+
+#ifdef USE_PAM
+#if defined(HAVE_SECURITY_PAM_APPL_H)
+#include <security/pam_appl.h>
+#elif defined (HAVE_PAM_PAM_APPL_H)
+#include <pam/pam_appl.h>
+#endif
+#endif
+
+#ifdef HAVE_CRYPT_H
+#include <crypt.h> /* needed for crypt() */
+#endif
#include <dirent.h>
#include <err.h>
#include <errno.h>
#include <event.h>
#include <fcntl.h>
+#include <grp.h> /* needed for setgroups */
#include <fts.h>
#include <grp.h>
#include <imsg.h>
#include <inttypes.h>
+#include <libgen.h>
+#ifdef HAVE_LOGIN_CAP_H
#include <login_cap.h>
+#endif
+#ifdef HAVE_PATHS_H
#include <paths.h>
+#endif
#include <poll.h>
#include <pwd.h>
#include <signal.h>
+#ifdef HAVE_SHADOW_H
+#include <shadow.h> /* needed for getspnam() */
+#endif
#include <stdio.h>
#include <syslog.h>
#include <limits.h>
@@ -50,7 +76,11 @@
#include <sysexits.h>
#include <time.h>
#include <unistd.h>
+#ifdef HAVE_UTIL_H
+#include <util.h>
+#endif
+#include <openssl/rand.h>
#include <openssl/ssl.h>
#include <openssl/evp.h>
@@ -58,6 +88,8 @@
#include "log.h"
#include "ssl.h"
+extern char *__progname;
+
#define SMTPD_MAXARG 32
static void parent_imsg(struct mproc *, struct imsg *);
@@ -81,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,
@@ -112,6 +144,8 @@ struct child {
struct offline {
TAILQ_ENTRY(offline) entry;
+ uid_t uid;
+ gid_t gid;
char *path;
};
@@ -152,11 +186,15 @@ int control_socket = -1;
struct tree children;
+/* Saved arguments to main(). */
+char **saved_argv;
+int saved_argc;
+
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;
@@ -260,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);
@@ -482,6 +520,28 @@ main(int argc, char *argv[])
char *rexec = NULL;
struct smtpd *conf;
+#ifdef NEED_PROGNAME
+ __progname = get_progname(argv[0]);
+#endif
+ __progname = xstrdup(__progname);
+
+ /* Save argv. Duplicate so setproctitle emulation doesn't clobber it */
+ saved_argc = argc;
+ saved_argv = xcalloc(argc + 1, sizeof(*saved_argv));
+ for (i = 0; i < argc; i++)
+ 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;
+
if ((conf = config_default()) == NULL)
err(1, NULL);
@@ -612,6 +672,9 @@ main(int argc, char *argv[])
if (parse_config(conf, conffile, opts))
exit(1);
+ if (RAND_status() != 1)
+ errx(1, "PRNG is not seeded");
+
if (strlcpy(env->sc_conffile, conffile, PATH_MAX)
>= PATH_MAX)
errx(1, "config file exceeds PATH_MAX");
@@ -845,8 +908,7 @@ start_child(int save_argc, char **save_argv, char *rexec)
} else if (fcntl(sp[0], F_SETFD, 0) == -1)
fatal("%s: fcntl", rexec);
- if (closefrom(4) == -1)
- fatal("%s: closefrom", rexec);
+ xclosefrom(4);
for (argc = 0; argc < save_argc; argc++)
argv[argc] = save_argv[argc];
@@ -1079,13 +1141,18 @@ smtpd(void) {
offline_timeout.tv_usec = 0;
evtimer_add(&offline_ev, &offline_timeout);
- fork_processors();
+ if (pidfile(NULL) < 0)
+ err(1, "pidfile");
+
+ 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
event_dispatch();
fatalx("exited event loop");
@@ -1179,8 +1246,7 @@ fork_proc_backend(const char *key, const char *conf, const char *procname)
if (pid == 0) {
/* child process */
dup2(sp[0], STDIN_FILENO);
- if (closefrom(STDERR_FILENO + 1) == -1)
- exit(1);
+ closefrom(STDERR_FILENO + 1);
if (procname == NULL)
procname = name;
@@ -1257,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_processors_dict, &iter, &name, (void **)&processor))
- fork_processor(name, processor->command, processor->user, processor->group, processor->chroot);
+ 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_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;
@@ -1304,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;
}
@@ -1329,11 +1420,11 @@ 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);
- if (closefrom(STDERR_FILENO + 1) == -1)
- err(1, "closefrom");
- if (setsid() == -1)
+ if (setsid() < 0)
err(1, "setsid");
if (signal(SIGPIPE, SIG_DFL) == SIG_ERR ||
signal(SIGINT, SIG_DFL) == SIG_ERR ||
@@ -1382,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,
@@ -1419,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);
@@ -1485,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) ||
@@ -1495,9 +1593,8 @@ forkmda(struct mproc *p, uint64_t id, struct deliver *deliver)
dup2(allout, STDOUT_FILENO) == -1 ||
dup2(allout, STDERR_FILENO) == -1)
err(1, "forkmda: dup2");
- if (closefrom(STDERR_FILENO + 1) == -1)
- err(1, "closefrom");
- if (setsid() == -1)
+ closefrom(STDERR_FILENO + 1);
+ if (setsid() < 0)
err(1, "setsid");
if (signal(SIGPIPE, SIG_DFL) == SIG_ERR ||
signal(SIGINT, SIG_DFL) == SIG_ERR ||
@@ -1509,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
@@ -1550,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;
@@ -1570,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;
@@ -1607,8 +1710,7 @@ offline_enqueue(char *name)
ssize_t len;
arglist args;
- if (closefrom(STDERR_FILENO + 1) == -1)
- _exit(1);
+ closefrom(STDERR_FILENO + 1);
memset(&args, 0, sizeof(args));
@@ -1633,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",
@@ -1689,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);
@@ -1718,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);
}
}
@@ -2049,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);
@@ -2075,8 +2194,9 @@ imsg_to_str(int type)
}
}
+#ifdef BSD_AUTH
int
-parent_auth_user(const char *username, const char *password)
+parent_auth_bsd(const char *username, const char *password)
{
char user[LOGIN_NAME_MAX];
char pass[LINE_MAX];
@@ -2090,3 +2210,121 @@ parent_auth_user(const char *username, const char *password)
return LKA_OK;
return LKA_PERMFAIL;
}
+#endif
+
+#ifdef USE_PAM
+int
+pam_conv_password(int num_msg, const struct pam_message **msg,
+ struct pam_response **respp, void *password)
+{
+ struct pam_response *response;
+
+ if (num_msg != 1)
+ return PAM_CONV_ERR;
+
+ response = calloc(1, sizeof(struct pam_response));
+ if (response == NULL || (response->resp = strdup(password)) == NULL) {
+ free(response);
+ return PAM_BUF_ERR;
+ }
+
+ *respp = response;
+ return PAM_SUCCESS;
+}
+int
+parent_auth_pam(const char *username, const char *password)
+{
+ int rc;
+ pam_handle_t *pamh = NULL;
+ struct pam_conv conv = { pam_conv_password, (char *)password };
+
+ if ((rc = pam_start(USE_PAM_SERVICE, username, &conv, &pamh)) != PAM_SUCCESS)
+ goto end;
+ if ((rc = pam_authenticate(pamh, 0)) != PAM_SUCCESS)
+ goto end;
+ if ((rc = pam_acct_mgmt(pamh, 0)) != PAM_SUCCESS)
+ goto end;
+
+end:
+ pam_end(pamh, rc);
+
+ switch (rc) {
+ case PAM_SUCCESS:
+ return LKA_OK;
+ case PAM_SYSTEM_ERR:
+ case PAM_ABORT:
+ case PAM_AUTHINFO_UNAVAIL:
+ return LKA_TEMPFAIL;
+ default:
+ return LKA_PERMFAIL;
+ }
+}
+#endif
+
+#ifdef HAVE_GETSPNAM
+int
+parent_auth_getspnam(const char *username, const char *password)
+{
+ struct spwd *pw;
+ char *ep;
+
+ errno = 0;
+ do {
+ pw = getspnam(username);
+ } while (pw == NULL && errno == EINTR);
+
+ if (pw == NULL) {
+ if (errno)
+ return LKA_TEMPFAIL;
+ return LKA_PERMFAIL;
+ }
+
+ if ((ep = crypt(password, pw->sp_pwdp)) == NULL)
+ return LKA_PERMFAIL;
+
+ if (strcmp(pw->sp_pwdp, ep) == 0)
+ return LKA_OK;
+
+ return LKA_PERMFAIL;
+}
+#endif
+
+int
+parent_auth_pwd(const char *username, const char *password)
+{
+ struct passwd *pw;
+ char *ep;
+
+ errno = 0;
+ do {
+ pw = getpwnam(username);
+ } while (pw == NULL && errno == EINTR);
+
+ if (pw == NULL) {
+ if (errno)
+ return LKA_TEMPFAIL;
+ return LKA_PERMFAIL;
+ }
+
+ if ((ep = crypt(password, pw->pw_passwd)) == NULL)
+ return LKA_PERMFAIL;
+
+ if (strcmp(pw->pw_passwd, ep) == 0)
+ return LKA_OK;
+
+ return LKA_PERMFAIL;
+}
+
+int
+parent_auth_user(const char *username, const char *password)
+{
+#if defined(BSD_AUTH)
+ return (parent_auth_bsd(username, password));
+#elif defined(USE_PAM)
+ return (parent_auth_pam(username, password));
+#elif defined(HAVE_GETSPNAM)
+ return (parent_auth_getspnam(username, password));
+#else
+ return (parent_auth_pwd(username, password));
+#endif
+}
diff --git a/usr.sbin/smtpd/smtpd.conf b/usr.sbin/smtpd/smtpd.conf
new file mode 100644
index 00000000..a7ba6c64
--- /dev/null
+++ b/usr.sbin/smtpd/smtpd.conf
@@ -0,0 +1,19 @@
+# $OpenBSD: smtpd.conf,v 1.10 2018/05/24 11:40:17 gilles Exp $
+
+# This is the smtpd server system-wide configuration file.
+# See smtpd.conf(5) for more information.
+
+table aliases file:/etc/mail/aliases
+
+# To accept external mail, replace with: listen on all
+#
+listen on localhost
+
+action "local" maildir alias <aliases>
+action "relay" relay
+
+# Uncomment the following to accept external mail for domain "example.org"
+#
+# match from any for domain "example.org" action "local"
+match for local action "local"
+match from local for any action "relay"
diff --git a/smtpd/smtpd.conf.5 b/usr.sbin/smtpd/smtpd.conf.5
index ca4087c3..36207c39 100644
--- a/smtpd/smtpd.conf.5
+++ b/usr.sbin/smtpd/smtpd.conf.5
@@ -1,4 +1,4 @@
-.\" $OpenBSD: smtpd.conf.5,v 1.228 2019/11/19 22:04:04 gilles 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: November 19 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,19 +522,29 @@ With the
option, clients must also provide a valid certificate
to establish an SMTP session.
.El
-.It Ic listen on Cm socket Oo Ar mask-src Oc Op Cm tag Ar tag
+.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.
-If the
-.Cm tag
-option is specified,
-sessions initiated from the socket will be tagged
+.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
@@ -588,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,
@@ -600,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
@@ -652,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
@@ -899,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.
@@ -915,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
@@ -928,37 +990,39 @@ The following phases are currently supported:
.It commit Ta after message is fully is submitted
.El
.Pp
-At each phase,
-multiple criteria 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 criteria 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
-Criteria 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
@@ -966,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
@@ -1078,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,
@@ -1119,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 b1c8fe37..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>
@@ -18,6 +18,12 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
+#include <event.h>
+
+#include <imsg.h>
+
+#include "openbsd-compat.h"
+
#ifndef nitems
#define nitems(_a) (sizeof((_a)) / sizeof((_a)[0]))
#endif
@@ -37,9 +43,14 @@
(expected_sz), (imsg)->hdr.len - IMSG_HEADER_SIZE); \
} while (0)
-#define CONF_FILE "/etc/mail/smtpd.conf"
-#define MAILNAME_FILE "/etc/mail/mailname"
-#define CA_FILE "/etc/ssl/cert.pem"
+#ifndef SMTPD_CONFDIR
+#define SMTPD_CONFDIR "/etc"
+#endif
+#define CONF_FILE SMTPD_CONFDIR "/smtpd.conf"
+#define MAILNAME_FILE SMTPD_CONFDIR "/mailname"
+#ifndef CA_FILE
+#define CA_FILE "/etc/ssl/cert.pem"
+#endif
#define PROC_COUNT 7
@@ -49,19 +60,34 @@
#define EXPAND_BUFFER 1024
#define SMTPD_QUEUE_EXPIRY (4 * 24 * 60 * 60)
-#define SMTPD_SOCKET "/var/run/smtpd.sock"
+#ifndef SMTPD_USER
+#define SMTPD_USER "_smtpd"
+#endif
+#ifndef SMTPD_QUEUE_USER
+#define SMTPD_QUEUE_USER "_smtpq"
+#endif
+#ifndef SMTPD_SOCKDIR
+#define SMTPD_SOCKDIR "/var/run"
+#endif
+#define SMTPD_SOCKET SMTPD_SOCKDIR "/smtpd.sock"
+#ifndef SMTPD_NAME
#define SMTPD_NAME "OpenSMTPD"
-#define SMTPD_VERSION "6.6.1"
+#endif
+#define SMTPD_VERSION "6.7.0-portable"
#define SMTPD_SESSION_TIMEOUT 300
#define SMTPD_BACKLOG 5
+#ifndef PATH_SMTPCTL
#define PATH_SMTPCTL "/usr/sbin/smtpctl"
+#endif
#define PATH_OFFLINE "/offline"
#define PATH_PURGE "/purge"
#define PATH_TEMPORARY "/temporary"
+#ifndef PATH_LIBEXEC
#define PATH_LIBEXEC "/usr/local/libexec/smtpd"
+#endif
/*
@@ -98,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
@@ -489,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;
@@ -584,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
@@ -622,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
@@ -1031,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;
@@ -1054,6 +1091,7 @@ struct filter_config {
char *rewrite;
char *report;
uint8_t junk;
+ uint8_t bypass;
char *proc;
const char **chain;
@@ -1084,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;
@@ -1134,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;
@@ -1161,6 +1208,8 @@ struct dispatcher_remote {
char *mail_from;
char *smarthost;
+ int smarthost_domain;
+
char *auth;
int tls_required;
int tls_noverify;
@@ -1168,6 +1217,8 @@ struct dispatcher_remote {
int backup;
char *backupmx;
+ char *filtername;
+
int srs;
};
@@ -1333,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 *);
@@ -1372,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);
@@ -1396,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 *);
@@ -1709,6 +1765,7 @@ int base64_encode(unsigned char const *, size_t, char *, size_t);
int base64_decode(char const *, unsigned char *, size_t);
int base64_encode_rfc3548(unsigned char const *, size_t,
char *, size_t);
+void xclosefrom(int);
void log_trace_verbose(int);
void log_trace(int, 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 d28fa58c..46bf3129 100644
--- a/smtpd/spfwalk.c
+++ b/usr.sbin/smtpd/spfwalk.c
@@ -14,10 +14,15 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
+#include "includes.h"
+
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/tree.h>
+#ifdef HAVE_ARPA_NAMESER_COMPAT_H
+#include <arpa/nameser_compat.h>
+#endif
#include <arpa/inet.h>
#include <arpa/nameser.h>
#include <netinet/in.h>
@@ -28,27 +33,36 @@
#include <err.h>
#include <errno.h>
#include <event.h>
+#include <imsg.h>
+#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <unistd.h>
-#define LINE_MAX 1024
#include "smtpd-defines.h"
#include "smtpd-api.h"
#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;
@@ -59,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;
@@ -81,18 +96,23 @@ 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);
+#if HAVE_PLEDGE
if (pledge("dns stdio", NULL) == -1)
err(1, "pledge");
+#endif
event_dispatch();
@@ -100,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;
@@ -121,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);
@@ -130,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)
@@ -173,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,
@@ -190,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;
}
}
@@ -226,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;
@@ -237,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;
@@ -251,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;
@@ -265,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;
@@ -303,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 05737d8d..bb4f4d9e 100644
--- a/smtpd/srs.c
+++ b/usr.sbin/smtpd/srs.c
@@ -16,6 +16,8 @@
* 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>
diff --git a/smtpd/ssl.c b/usr.sbin/smtpd/ssl.c
index bd18ad61..97f7b1df 100644
--- a/smtpd/ssl.c
+++ b/usr.sbin/smtpd/ssl.c
@@ -18,6 +18,8 @@
* 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>
@@ -86,7 +88,7 @@ ssl_setup(SSL_CTX **ctxp, struct pki *pki,
if (sni_cb)
SSL_CTX_set_tlsext_servername_callback(ctx, sni_cb);
- SSL_CTX_set_dh_auto(ctx, pki->pki_dhe);
+ SSL_CTX_set_dh_auto(ctx, 0);
SSL_CTX_set_ecdh_auto(ctx, 1);
@@ -395,8 +397,13 @@ ssl_load_pkey(const void *data, size_t datalen, char *buf, off_t len,
memcpy(exdata, data, datalen);
if (rsa)
RSA_set_ex_data(rsa, 0, exdata);
+#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 */
EC_KEY_free(eckey); /* dereference, will be cleaned up with pkey */
}
diff --git a/smtpd/ssl.h b/usr.sbin/smtpd/ssl.h
index 1c0dc072..11c80c68 100644
--- a/smtpd/ssl.h
+++ b/usr.sbin/smtpd/ssl.h
@@ -65,6 +65,7 @@ int ssl_ctx_fake_private_key(SSL_CTX *, const void *, size_t,
/* ssl_privsep.c */
int ssl_by_mem_ctrl(X509_LOOKUP *, int, const char *, long, char **);
+int SSL_CTX_use_certificate_chain_mem(SSL_CTX *, void *, int);
/* ssl_verify.c */
int ssl_check_name(X509 *, const char *, int *);
diff --git a/smtpd/ssl_smtpd.c b/usr.sbin/smtpd/ssl_smtpd.c
index 1f1e62d2..4e5b7e75 100644
--- a/smtpd/ssl_smtpd.c
+++ b/usr.sbin/smtpd/ssl_smtpd.c
@@ -18,6 +18,8 @@
* 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>
diff --git a/smtpd/ssl_verify.c b/usr.sbin/smtpd/ssl_verify.c
index 02148862..2e784b97 100644
--- a/smtpd/ssl_verify.c
+++ b/usr.sbin/smtpd/ssl_verify.c
@@ -17,6 +17,7 @@
/* Adapted from lib/libtls/tls_verify.c */
+#include "includes.h"
#include <sys/socket.h>
diff --git a/smtpd/stat_backend.c b/usr.sbin/smtpd/stat_backend.c
index 027f37a5..30cb299b 100644
--- a/smtpd/stat_backend.c
+++ b/usr.sbin/smtpd/stat_backend.c
@@ -16,6 +16,8 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
+#include "includes.h"
+
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/queue.h>
diff --git a/smtpd/stat_ramstat.c b/usr.sbin/smtpd/stat_ramstat.c
index ede2e130..bbf1541a 100644
--- a/smtpd/stat_ramstat.c
+++ b/usr.sbin/smtpd/stat_ramstat.c
@@ -15,6 +15,7 @@
* 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>
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 b79451ca..469eeee1 100644
--- a/smtpd/table.c
+++ b/usr.sbin/smtpd/table.c
@@ -17,6 +17,8 @@
* 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>
@@ -43,7 +45,9 @@
struct table_backend *table_backend_lookup(const char *);
extern struct table_backend table_backend_static;
+#ifdef HAVE_DB_API
extern struct table_backend table_backend_db;
+#endif
extern struct table_backend table_backend_getpwnam;
extern struct table_backend table_backend_proc;
@@ -56,7 +60,9 @@ static unsigned int last_table_id = 0;
static struct table_backend *backends[] = {
&table_backend_static,
+#ifdef HAVE_DB_API
&table_backend_db,
+#endif
&table_backend_getpwnam,
&table_backend_proc,
NULL
@@ -395,7 +401,7 @@ table_netaddr_match(const char *s1, const char *s2)
return 0;
if (n1.ss.ss_family != n2.ss.ss_family)
return 0;
- if (n1.ss.ss_len != n2.ss.ss_len)
+ if (SS_LEN(&n1.ss) != SS_LEN(&n2.ss))
return 0;
return table_match_mask(&n1.ss, &n2);
}
@@ -648,7 +654,9 @@ parse_sockaddr(struct sockaddr *sa, int family, const char *str)
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);
@@ -673,7 +681,9 @@ parse_sockaddr(struct sockaddr *sa, int family, const char *str)
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;
@@ -682,7 +692,7 @@ parse_sockaddr(struct sockaddr *sa, int family, const char *str)
if (IN6_IS_ADDR_LINKLOCAL(&in6a) ||
IN6_IS_ADDR_MC_LINKLOCAL(&in6a) ||
- IN6_IS_ADDR_MC_INTFACELOCAL(&in6a))
+ IN6_IS_ADDR_MC_NODELOCAL(&in6a))
if ((sin6->sin6_scope_id = if_nametoindex(cp)))
return (0);
diff --git a/smtpd/table_db.c b/usr.sbin/smtpd/table_db.c
index daa6a3f8..f7d766dd 100644
--- a/smtpd/table_db.c
+++ b/usr.sbin/smtpd/table_db.c
@@ -16,6 +16,8 @@
* 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>
@@ -24,8 +26,13 @@
#include <netinet/in.h>
#include <arpa/inet.h>
-
+#ifdef HAVE_DB_H
#include <db.h>
+#elif defined(HAVE_DB1_DB_H)
+#include <db1/db.h>
+#elif defined(HAVE_DB_185_H)
+#include <db_185.h>
+#endif
#include <ctype.h>
#include <err.h>
#include <event.h>
diff --git a/smtpd/table_getpwnam.c b/usr.sbin/smtpd/table_getpwnam.c
index 78e6edc2..ccf889be 100644
--- a/smtpd/table_getpwnam.c
+++ b/usr.sbin/smtpd/table_getpwnam.c
@@ -16,6 +16,8 @@
* 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>
diff --git a/smtpd/table_proc.c b/usr.sbin/smtpd/table_proc.c
index dad041a4..44589bd7 100644
--- a/smtpd/table_proc.c
+++ b/usr.sbin/smtpd/table_proc.c
@@ -16,6 +16,8 @@
* 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>
@@ -26,7 +28,9 @@
#include <event.h>
#include <fcntl.h>
#include <imsg.h>
+#ifdef HAVE_PATHS_H
#include <paths.h>
+#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
diff --git a/smtpd/table_static.c b/usr.sbin/smtpd/table_static.c
index f9519cb4..8f78ae11 100644
--- a/smtpd/table_static.c
+++ b/usr.sbin/smtpd/table_static.c
@@ -17,6 +17,8 @@
* 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>
diff --git a/smtpd/to.c b/usr.sbin/smtpd/to.c
index 052a2354..81a1bb54 100644
--- a/smtpd/to.c
+++ b/usr.sbin/smtpd/to.c
@@ -18,6 +18,8 @@
* 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>
@@ -56,12 +58,14 @@ static int alias_is_filename(struct expandnode *, const char *, size_t);
static int alias_is_include(struct expandnode *, const char *, size_t);
static int alias_is_error(struct expandnode *, const char *, size_t);
+static int broken_inet_net_pton_ipv6(const char *, void *, size_t);
+
const char *
sockaddr_to_text(struct sockaddr *sa)
{
static char buf[NI_MAXHOST];
- if (getnameinfo(sa, sa->sa_len, buf, sizeof(buf), NULL, 0,
+ if (getnameinfo(sa, SA_LEN(sa), buf, sizeof(buf), NULL, 0,
NI_NUMERICHOST))
return ("(unknown)");
else
@@ -75,7 +79,9 @@ in6addr_to_text(const struct in6_addr *addr)
uint16_t tmp16;
memset(&sa_in6, 0, sizeof(sa_in6));
+#ifdef HAVE_STRUCT_SOCKADDR_IN6_SIN6_LEN
sa_in6.sin6_len = sizeof(sa_in6);
+#endif
sa_in6.sin6_family = AF_INET6;
memcpy(&sa_in6.sin6_addr, addr, sizeof(sa_in6.sin6_addr));
@@ -191,15 +197,20 @@ time_to_text(time_t when)
char *day[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
char *month[] = {"Jan","Feb","Mar","Apr","May","Jun",
"Jul","Aug","Sep","Oct","Nov","Dec"};
- char *tz;
+ const char *tz;
long offset;
lt = localtime(&when);
if (lt == NULL || when == 0)
fatalx("time_to_text: localtime");
+#if HAVE_STRUCT_TM_TM_GMTOFF
offset = lt->tm_gmtoff;
tz = lt->tm_zone;
+#elif defined HAVE_DECL_ALTZONE && defined HAVE_DECL_TIMEZONE
+ offset = lt->tm_isdst > 0 ? altzone : timezone;
+ tz = lt->tm_isdst > 0 ? tzname[1] : tzname[0];
+#endif
/* We do not use strftime because it is subject to locale substitution*/
if (!bsnprintf(buf, sizeof(buf),
@@ -283,7 +294,9 @@ text_to_netaddr(struct netaddr *netaddr, const char *s)
if (bits != -1) {
ssin.sin_family = AF_INET;
memcpy(&ss, &ssin, sizeof(ssin));
+#ifdef HAVE_STRUCT_SOCKADDR_STORAGE_SS_LEN
ss.ss_len = sizeof(struct sockaddr_in);
+#endif
} else {
if (s[0] != '[') {
if ((len = strlcpy(buf, s, sizeof buf)) >= sizeof buf)
@@ -301,11 +314,19 @@ text_to_netaddr(struct netaddr *netaddr, const char *s)
}
bits = inet_net_pton(AF_INET6, buf, &ssin6.sin6_addr,
sizeof(struct in6_addr));
- if (bits == -1)
- return 0;
+ if (bits == -1) {
+ if (errno != EAFNOSUPPORT)
+ return 0;
+ bits = broken_inet_net_pton_ipv6(buf, &ssin6.sin6_addr,
+ sizeof(struct in6_addr));
+ if (bits == -1)
+ return 0;
+ }
ssin6.sin6_family = AF_INET6;
memcpy(&ss, &ssin6, sizeof(ssin6));
+#ifdef HAVE_STRUCT_SOCKADDR_STORAGE_SS_LEN
ss.ss_len = sizeof(struct sockaddr_in6);
+#endif
}
netaddr->ss = ss;
@@ -825,3 +846,35 @@ alias_is_error(struct expandnode *alias, const char *line, size_t len)
alias->type = EXPAND_ERROR;
return 1;
}
+
+static int
+broken_inet_net_pton_ipv6(const char *src, void *dst, size_t size)
+{
+ int ret;
+ int bits;
+ char buf[sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:255:255:255:255/128")];
+ char *sep;
+ const char *errstr;
+
+ if (strlcpy(buf, src, sizeof buf) >= sizeof buf) {
+ errno = EMSGSIZE;
+ return (-1);
+ }
+
+ sep = strchr(buf, '/');
+ if (sep != NULL)
+ *sep++ = '\0';
+
+ ret = inet_pton(AF_INET6, buf, dst);
+ if (ret != 1)
+ return (-1);
+
+ if (sep == NULL)
+ return 128;
+
+ bits = strtonum(sep, 0, 128, &errstr);
+ if (errstr)
+ return (-1);
+
+ return bits;
+}
diff --git a/smtpd/tree.c b/usr.sbin/smtpd/tree.c
index 70aef047..1d720a59 100644
--- a/smtpd/tree.c
+++ b/usr.sbin/smtpd/tree.c
@@ -16,6 +16,8 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
+#include "includes.h"
+
#include <sys/types.h>
#include <sys/tree.h>
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 fe50b026..974d5727 100644
--- a/smtpd/unpack_dns.c
+++ b/usr.sbin/smtpd/unpack_dns.c
@@ -16,6 +16,11 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
+#include "includes.h"
+
+#ifdef HAVE_ARPA_NAMESER_COMPAT_H
+#include <arpa/nameser_compat.h>
+#endif
#include <arpa/inet.h>
#include <string.h>
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 f59ad1e4..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.
@@ -19,6 +19,8 @@
* 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>
@@ -176,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;
@@ -462,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;
@@ -489,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
@@ -842,3 +844,27 @@ log_trace_verbose(int v)
/* Set debug logging in log.c */
log_setverbose(v & TRACE_DEBUG ? 2 : foreground_log);
}
+
+void
+xclosefrom(int lowfd)
+{
+#if defined HAVE_CLOSEFROM_INT
+ if (closefrom(lowfd) == -1)
+ err(1, "closefrom");
+#else
+ closefrom(lowfd);
+#endif
+}
+
+void
+portable_freeaddrinfo(struct addrinfo *ai)
+{
+ struct addrinfo *p;
+
+ do {
+ p = ai;
+ ai = ai->ai_next;
+ free(p->ai_canonname);
+ free(p);
+ } while (ai);
+}
diff --git a/smtpd/waitq.c b/usr.sbin/smtpd/waitq.c
index dc459372..082a1e51 100644
--- a/smtpd/waitq.c
+++ b/usr.sbin/smtpd/waitq.c
@@ -16,6 +16,8 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
+#include "includes.h"
+
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/queue.h>