├── .gitignore ├── AUTHORS ├── BUGS ├── COPYING ├── ChangeLog.old ├── HOWTO ├── INSTALL ├── Makefile.am ├── NEWS ├── README ├── TODO ├── acinclude.m4 ├── autogen.sh ├── configure.ac ├── doc ├── DIAGRAM ├── Makefile.am ├── mailq.1 ├── nullmailer-dsn.1 ├── nullmailer-inject.1 ├── nullmailer-queue.8 ├── nullmailer-send.8 ├── nullmailer-smtpd.1 ├── nullmailer.7 └── sendmail.1 ├── lib ├── Makefile.am ├── ac │ ├── dirent.h │ ├── time.h │ └── wait.h ├── address-old.cc ├── address.cc ├── address.h ├── argparse.cc ├── argparse.h ├── autoclose.h ├── base64.cc ├── base64.h ├── canonicalize.cc ├── canonicalize.h ├── cli++ │ ├── Makefile.am │ ├── cli++.h │ ├── cli++topod.pl │ ├── clitest.cc │ ├── main.cc │ ├── messages.cc │ └── only_long.cc ├── config_path.cc ├── config_read.cc ├── config_readint.cc ├── config_readlist.cc ├── config_syserr.cc ├── configio.h ├── connect.h ├── defines.h ├── errcodes.cc ├── errcodes.h ├── fdbuf │ ├── Makefile.am │ ├── fdbuf.cc │ ├── fdbuf.h │ ├── fdbuf_copy.cc │ ├── fdbuf_test.cc │ ├── fdibuf.cc │ ├── fdibuf.h │ ├── fdibuf_mystring.cc │ ├── fdibuf_netstring.cc │ ├── fdobuf.cc │ ├── fdobuf.h │ ├── fdobuf_chownmod.cc │ ├── fdobuf_seek.cc │ ├── fdobuf_signed.cc │ ├── fdobuf_unsigned.cc │ ├── tlsibuf.cc │ ├── tlsibuf.h │ ├── tlsobuf.cc │ └── tlsobuf.h ├── forkexec.cc ├── forkexec.h ├── hostname.cc ├── hostname.h ├── itoa.cc ├── itoa.h ├── list.h ├── listtest.cc ├── make_defines.sh ├── makefield.cc ├── makefield.h ├── mergelib.sh ├── mystring │ ├── Makefile.am │ ├── append.cc │ ├── assign.cc │ ├── count.cc │ ├── fdobuf.cc │ ├── find_first_ch.cc │ ├── find_first_of.cc │ ├── find_last_ch.cc │ ├── find_last_of.cc │ ├── iter.cc │ ├── iter.h │ ├── join.cc │ ├── join.h │ ├── lower.cc │ ├── lstrip.cc │ ├── mystring.cc │ ├── mystring.h │ ├── operator_in.cc │ ├── rep.cc │ ├── rep.h │ ├── rstrip.cc │ ├── starts_with.cc │ ├── strip.cc │ ├── sub.cc │ ├── subst.cc │ ├── trace.h │ └── upper.cc ├── netstring.cc ├── netstring.h ├── selfpipe.cc ├── selfpipe.h ├── setenv.cc ├── setenv.h └── tcpconnect.cc ├── makedist.in ├── protocols ├── Makefile.am ├── protocol.cc ├── protocol.h ├── qmqp.cc ├── smtp.cc ├── tls_gnutls.cc └── tls_none.cc ├── scripts ├── nullmailer-log.run ├── nullmailer.run └── nullmailer.service ├── spec ├── src ├── Makefile.am ├── address-main.cc ├── dsn.cc ├── inject.cc ├── mailq.cc ├── queue.cc ├── send.cc ├── sendmail.cc └── smtpd.cc └── test ├── Makefile.am ├── accept-qmqp.sh ├── accept-smtp.sh ├── address-test.cc ├── address-trace.cc ├── argparse-test.cc ├── authtest-smtp.sh ├── clitest.cc ├── clitest.sh ├── functions.in ├── runtests └── tests ├── dsn ├── inject ├── bad-headers ├── date ├── from ├── from_ ├── message-id ├── queue ├── recips ├── return-path └── sender ├── mailq ├── protocols ├── queue ├── rewrite └── validate ├── send ├── smtp-auth └── smtpd /.gitignore: -------------------------------------------------------------------------------- 1 | *.[ao] 2 | *~ 3 | .deps 4 | ANNOUNCEMENT 5 | ChangeLog 6 | Makefile 7 | Makefile.in 8 | aclocal.m4 9 | autom4te.cache 10 | compile 11 | config.h 12 | config.h.in 13 | config.log 14 | config.status 15 | configure 16 | depcomp 17 | install-sh 18 | lib/defines.cc 19 | missing 20 | mkinstalldirs 21 | protocols/qmqp 22 | protocols/smtp 23 | src/mailq 24 | src/nullmailer-dsn 25 | src/nullmailer-inject 26 | src/nullmailer-queue 27 | src/nullmailer-send 28 | src/nullmailer-smtpd 29 | src/sendmail 30 | stamp-h.in 31 | stamp-h1 32 | test/address-test 33 | test/argparse-test 34 | test/clitest0 35 | test/clitest1 36 | test/functions 37 | test/log 38 | test/runtests 39 | test-tmp 40 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | Bruce Guenter 2 | -------------------------------------------------------------------------------- /BUGS: -------------------------------------------------------------------------------- 1 | If you find anything in these programs that you would consider a bug, 2 | please report it immediately to: 3 | Bruce Guenter 4 | Thank you! 5 | -------------------------------------------------------------------------------- /HOWTO: -------------------------------------------------------------------------------- 1 | Configuration of this program in the typical case is quite simple. 2 | 3 | 1. Compile and install the program. If you are using the RPM, skip this 4 | step. See the INSTALL file for more details. Note that this package is 5 | designed to execute as a non-privileged user named "nullmail". When you 6 | do the final install, add the "nullmail" user (typically with either a 7 | non-privileged group or a new "nullmail" group) and run "make 8 | install-root" to set up the files with the proper permissions. 9 | 10 | 2. Set up your hostname in SYSCONFDIR[1]/nullmailer/me. Usually this 11 | will be as simple as typing: 12 | /bin/hostname >SYSCONFDIR/nullmailer/me 13 | You may also wish to set up the defaultdomain and defaulthost 14 | configuration files. See the man page for nullmailer-inject for more 15 | details. 16 | 17 | 3. Into the file SYSCONFDIR/nullmailer/remotes, put one line for each of 18 | your relay hosts (the computer or computers to which you will send all 19 | your mail): 20 | HOSTNAME PROTOCOL 21 | You will have specified the SYSCONFDIR during configuration of the 22 | install step. On most systems, it will be /usr/local/etc. The RPMs use 23 | /etc. HOSTNAME is the fully-qualified host name of the relay host, or 24 | its numerical IP address. PROTOCOL will typically be "smtp" (without 25 | the quotes), but QMQP[2] is also supported with the "qmqp" module. 26 | 27 | 4. Start nullmailer-send. If you are using the RPM, ignore the rest of 28 | the instructions and type: 29 | svc-start nullmailer 30 | Otherwise, you will need to set up the appropriate scripts to run 31 | nullmailer-send in the background, with its output going to a logging 32 | program. The following is an appropriate run script for use with 33 | daemontools[3]: 34 | #!/bin/sh 35 | exec setuidgid nullmail nullmailer-send 2>&1 36 | 37 | 5. You're done! The included sendmail emulator front-end should allow 38 | most (if not all) sendmail-compatible programs to run without any 39 | changes. See the man page for nullmailer-inject for details on outgoing 40 | email message header formatting options. 41 | 42 | Footnotes: 43 | 1. SYSCONFDIR is the system configuration directory, as configured 44 | during compilation. It defaults to /usr/local/etc 45 | 2. QMQP is a high-speed mail queueing protocol, used with qmail. See 46 | http://cr.yp.to/proto/qmqp.html 47 | 3. See http://cr.yp.to/daemontools.html. 48 | -------------------------------------------------------------------------------- /INSTALL: -------------------------------------------------------------------------------- 1 | To build and install nullmailer, run: 2 | 3 | sh autogen.sh 4 | ./configure 5 | make 6 | sudo make install install-root 7 | 8 | (Running autogen.sh is optional for release packages) 9 | 10 | To enable SSL/TLS support, add "--enable-tls" to the configure command 11 | line above. 12 | 13 | The default installation expects that a user and group "nullmail" have 14 | already been set up. The install steps will create all appropriate 15 | configuration and queue directories, and change their ownership as needed. 16 | 17 | You may set the installation directory and other parameters by options 18 | to ./configure. To see them, run: 19 | 20 | ./configure --help 21 | -------------------------------------------------------------------------------- /Makefile.am: -------------------------------------------------------------------------------- 1 | localstatedir = @localstatedir@/spool/nullmailer 2 | sysconfdir = @sysconfdir@/nullmailer 3 | 4 | EXTRA_DIST = BUGS HOWTO INSTALL TODO spec \ 5 | scripts/nullmailer.run scripts/nullmailer-log.run \ 6 | scripts/nullmailer.service 7 | SUBDIRS = doc lib protocols src test 8 | 9 | install-data-local: 10 | @$(NORMAL_INSTALL) 11 | $(mkinstalldirs) $(DESTDIR)$(localstatedir)/failed 12 | chmod 700 $(DESTDIR)$(localstatedir)/failed 13 | $(mkinstalldirs) $(DESTDIR)$(localstatedir)/queue 14 | chmod 700 $(DESTDIR)$(localstatedir)/queue 15 | $(mkinstalldirs) $(DESTDIR)$(localstatedir)/tmp 16 | chmod 700 $(DESTDIR)$(localstatedir)/tmp 17 | $(mkinstalldirs) $(DESTDIR)$(sysconfdir) 18 | $(RM) -f $(DESTDIR)$(localstatedir)/trigger 19 | mkfifo $(DESTDIR)$(localstatedir)/trigger 20 | chmod 600 $(DESTDIR)$(localstatedir)/trigger 21 | 22 | install-root: 23 | chown nullmail $(DESTDIR)$(localstatedir)/* 24 | chown nullmail $(DESTDIR)$(sbindir)/nullmailer-queue 25 | chmod u+s $(DESTDIR)$(sbindir)/nullmailer-queue 26 | chown nullmail $(DESTDIR)$(bindir)/mailq 27 | chmod u+s $(DESTDIR)$(bindir)/mailq 28 | 29 | dist-hook: 30 | sed -e s/@VERSION\@/@VERSION@/ \ 31 | <$(srcdir)/spec >$(distdir)/nullmailer-@VERSION@.spec 32 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | nullmailer 2 | Simple relay-only mail transport agent 3 | Bruce Guenter 4 | Version 2.2 5 | 2018-10-12 6 | 7 | This is nullmailer, a sendmail/qmail/etc replacement MTA for hosts which 8 | relay to a fixed set of smart relays. It is designed to be simple to 9 | configure, secure, and easily extendable. 10 | 11 | A mailing list has been set up to discuss this package. To subscribe, 12 | send an email to: 13 | nullmailer-subscribe@lists.untroubled.org 14 | A mailing list archive is available at: 15 | http://lists.untroubled.org/?list=nullmailer 16 | 17 | Development versions of nullmailer are available at GitHub: 18 | https://github.com/bruceg/nullmailer 19 | 20 | This package is Copyright (C) 2018 Bruce Guenter, and may be copied 21 | according to the GNU GENERAL PUBLIC LICENSE (GPL) Version 2 or a later 22 | version. A copy of this license is included with this package. This 23 | package comes with no warranty of any kind. 24 | -------------------------------------------------------------------------------- /TODO: -------------------------------------------------------------------------------- 1 | - Convert all proto modules to use timedout reads. 2 | 3 | - Add patterns to the remotes file, to allow messages to be delivered to 4 | different remotes based on the sender or recipient addresses 5 | 6 | - Remove "adminaddr" facility from -queue, and add a more general 7 | destination address rewriting facility to -inject: 8 | - Read a list containing "PATTERN:ADDRESS" lines. 9 | - PATTERN can be a literal "user@FQDN" or just "user", in which case 10 | it must be matched exactly (before qualification). 11 | - PATTERN can be "@FQDN" which matches any user. 12 | 13 | - Generate DDNs for messages older than a configurable time. 14 | 15 | - For version 3: three-state queueing? 16 | - Queue message partially (tmp -> holding) 17 | - Send to smarthost immediately 18 | - Remove from holding if sending succeeds 19 | - Complete queueing (holding -> queue) if sending is deferred 20 | - Return with an error if sending fails (permanently) 21 | - Move all from holding -> queue on startup of nullmailer-send 22 | -------------------------------------------------------------------------------- /acinclude.m4: -------------------------------------------------------------------------------- 1 | dnl TRY_CXX_FLAG(FLAG,[ACTION-IF-FOUND[,ACTION-IF-NOT-FOUND]]) 2 | AC_DEFUN([TRY_CXX_FLAG], 3 | [echo >conftest.cc 4 | if ${CXX-g++} ${CXXFLAGS} -c [$1] conftest.cc >/dev/null 2>&1; then 5 | ifelse([$2], , :, [rm -f conftest* 6 | $2]) 7 | else 8 | ifelse([$3], , :, [rm -f conftest* 9 | $3]) 10 | fi 11 | rm -f conftest*]) 12 | 13 | AC_DEFUN([CXX_NO_RTTI], 14 | [AC_CACHE_CHECK(whether ${CXX-g++} accepts -fno-rtti, 15 | local_cv_flag_NO_RTTI, 16 | TRY_CXX_FLAG(-fno-rtti, 17 | local_cv_flag_NO_RTTI=yes, 18 | local_cv_flag_NO_RTTI=no)) 19 | test "$local_cv_flag_NO_RTTI" = yes && CXXFLAGS="$CXXFLAGS -fno-rtti" 20 | ]) 21 | 22 | AC_DEFUN([CXX_NO_EXCEPTIONS], 23 | [AC_CACHE_CHECK(whether ${CXX-g++} accepts -fno-exceptions, 24 | local_cv_flag_NO_EXCEPTIONS, 25 | TRY_CXX_FLAG(-fno-exceptions, 26 | local_cv_flag_NO_EXCEPTIONS=yes, 27 | local_cv_flag_NO_EXCEPTIONS=no)) 28 | test "$local_cv_flag_NO_EXCEPTIONS" = yes && CXXFLAGS="$CXXFLAGS -fno-exceptions" 29 | ]) 30 | 31 | dnl TRY_STRUCT_TM_MEMBER(MEMBER,FLAGNAME) 32 | AC_DEFUN([TRY_STRUCT_TM_MEMBER], 33 | [ AC_CACHE_CHECK(whether struct tm contains [$1], 34 | [$2], 35 | AC_COMPILE_IFELSE([AC_LANG_SOURCE([ 36 | #if TIME_WITH_SYS_TIME 37 | # include 38 | # include 39 | #else 40 | # if HAVE_SYS_TIME_H 41 | # include 42 | # else 43 | # include 44 | # endif 45 | #endif 46 | int main() { struct tm* foo; foo->[$1]; } 47 | ])], 48 | [$2]=yes, 49 | [$2]=no)) 50 | ]) 51 | 52 | AC_DEFUN([TEST_STRUCT_TM],[ 53 | TRY_STRUCT_TM_MEMBER(tm_isdst, local_cv_flag_TM_HAS_ISDST) 54 | TRY_STRUCT_TM_MEMBER(__tm_isdst, local_cv_flag_TM_HAS___ISDST) 55 | if test "$local_cv_flag_TM_HAS_ISDST" = yes 56 | then AC_DEFINE(TM_HAS_ISDST,tm_isdst,[struct tm has tm_isdst member]) 57 | elif test "$local_cv_flag_TM_HAS___ISDST" = yes 58 | then AC_DEFINE(TM_HAS_ISDST,__tm_isdst,[struct tm has tm_isdst member]) 59 | fi 60 | TRY_STRUCT_TM_MEMBER(tm_gmtoff, local_cv_flag_TM_HAS_GMTOFF) 61 | TRY_STRUCT_TM_MEMBER(__tm_gmtoff, local_cv_flag_TM_HAS___GMTOFF) 62 | if test "$local_cv_flag_TM_HAS_GMTOFF" = yes 63 | then AC_DEFINE(TM_HAS_GMTOFF,tm_gmtoff,[struct tm has tm_gmtoff member]) 64 | elif test "$local_cv_flag_TM_HAS___GMTOFF" = yes 65 | then AC_DEFINE(TM_HAS_GMTOFF,__tm_gmtoff,[struct tm has tm_gmtoff member]) 66 | fi 67 | ]) 68 | 69 | dnl TRY_STRUCT_UTSNAME_MEMBER(MEMBER,FLAGNAME) 70 | AC_DEFUN([TRY_STRUCT_UTSNAME_MEMBER], 71 | [ AC_CACHE_CHECK(whether struct utsname contains [$1], 72 | [$2], 73 | AC_COMPILE_IFELSE([AC_LANG_SOURCE([ 74 | #include 75 | int main() { struct utsname* foo; foo->[$1]; } 76 | ])], 77 | [$2]=yes, 78 | [$2]=no)) 79 | ]) 80 | 81 | AC_DEFUN([TEST_STRUCT_UTSNAME],[ 82 | TRY_STRUCT_UTSNAME_MEMBER(domainname, local_cv_flag_UTSNAME_HAS_DOMAINNAME) 83 | TRY_STRUCT_UTSNAME_MEMBER(__domainname, 84 | local_cv_flag_UTSNAME_HAS___DOMAINNAME) 85 | if test "$local_cv_flag_UTSNAME_HAS_DOMAINNAME" = yes 86 | then AC_DEFINE(UTSNAME_HAS_DOMAINNAME,domainname,[struct utsname has domainname member]) 87 | elif test "$local_cv_flag_UTSNAME_HAS___DOMAINNAME" = yes 88 | then AC_DEFINE(UTSNAME_HAS_DOMAINNAME,__domainname,[struct utsname has domainname member]) 89 | fi 90 | ]) 91 | 92 | AC_DEFUN([CHECK_NAMED_PIPE_BUG], 93 | [ AC_CACHE_CHECK(whether named pipes are buggy, 94 | local_cv_flag_NAMEDPIPEBUG, 95 | cat >conftest.c < 97 | #include 98 | #include 99 | #include 100 | int main(int argc, char** argv) 101 | { 102 | struct timeval tv; 103 | fd_set rfds; 104 | int fd = open(*(argv+1), O_RDONLY | O_NONBLOCK); 105 | FD_ZERO(&rfds); 106 | FD_SET(fd,&rfds); 107 | tv.tv_sec = tv.tv_usec = 0; 108 | return (select(fd+1, &rfds, 0, 0,&tv) > 0) ? 0 : 1; 109 | } 110 | EOF 111 | if ! ${CC} ${CFLAGS} conftest.c -o conftest 2>/dev/null 112 | then 113 | echo Compile failed 114 | exit 1 115 | fi 116 | mkfifo conftest.pipe 117 | if ./conftest conftest.pipe 118 | then 119 | AC_DEFINE(NAMEDPIPEBUG, 1, [Named pipes have read/write bug]) 120 | local_cv_flag_NAMEDPIPEBUG=yes 121 | else 122 | local_cv_flag_NAMEDPIPEBUG=no 123 | fi 124 | rm -f conftest*) 125 | ]) 126 | -------------------------------------------------------------------------------- /autogen.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | aclocal -I . 3 | autoheader 4 | # automake needs a file named "ChangeLog" 5 | if ! test -f "ChangeLog"; then 6 | echo "INFO: creating phony ChangeLog for automake to succeed"; 7 | touch "ChangeLog"; 8 | fi 9 | automake --add-missing 10 | autoconf 11 | -------------------------------------------------------------------------------- /configure.ac: -------------------------------------------------------------------------------- 1 | AC_INIT([nullmailer], [2.2]) 2 | AC_LANG([C++]) 3 | AC_CONFIG_SRCDIR([lib/defines.h]) 4 | AM_INIT_AUTOMAKE 5 | AM_CONFIG_HEADER(config.h) 6 | AC_PROG_MAKE_SET 7 | 8 | dnl Checks for programs. 9 | AC_PROG_CC 10 | AC_PROG_CXX 11 | CXX_NO_RTTI 12 | CXX_NO_EXCEPTIONS 13 | AC_SYS_LARGEFILE 14 | 15 | CFLAGS="$CFLAGS -W -Wall" 16 | CXXFLAGS="$CXXFLAGS -W -Wall" 17 | 18 | AC_PROG_INSTALL 19 | AC_PROG_RANLIB 20 | AC_PATH_PROG(STRIP, strip) 21 | AC_PATH_PROG(RM, rm) 22 | AC_PATH_PROG(MKDIR, mkdir) 23 | 24 | dnl Checks for libraries. 25 | AC_CHECK_LIB(xnet, socket) 26 | AC_CHECK_LIB(inet, socket) 27 | AC_CHECK_LIB(socket, socket) 28 | 29 | dnl Checks for header files. 30 | AC_HEADER_DIRENT 31 | AC_HEADER_STDC 32 | AC_HEADER_SYS_WAIT 33 | AC_HEADER_TIME 34 | dnl AC_CHECK_HEADERS(fcntl.h shadow.h crypt.h) 35 | AC_CHECK_HEADERS(sys/time.h unistd.h) 36 | 37 | dnl Checks for typedefs, structures, and compiler characteristics. 38 | dnl AC_TYPE_UID_T 39 | dnl AC_TYPE_SIGNAL 40 | dnl AC_C_INLINE 41 | dnl AC_TYPE_PID_T 42 | AC_TYPE_SIZE_T 43 | 44 | TEST_STRUCT_TM 45 | TEST_STRUCT_UTSNAME 46 | CHECK_NAMED_PIPE_BUG 47 | 48 | dnl Checks for library functions. 49 | dnl AC_CHECK_FUNCS(gettimeofday mkdir putenv rmdir socket) 50 | AC_CHECK_FUNCS(setenv srandom) 51 | 52 | AC_MSG_CHECKING(for getaddrinfo) 53 | AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ 54 | #include 55 | #include 56 | #include 57 | #include 58 | ]], [[ 59 | getaddrinfo(NULL, NULL, NULL, NULL) 60 | ]])], [has_getaddrinfo=yes], [has_getaddrinfo=no]) 61 | if test "$has_getaddrinfo" = yes; then 62 | AC_MSG_RESULT(yes) 63 | AC_DEFINE(HAVE_GETADDRINFO,,[getaddrinfo code enabled]) 64 | else 65 | AC_MSG_RESULT(no) 66 | AC_MSG_RESULT(disabled: getaddrinfo missing) 67 | fi 68 | AC_SUBST(HAVE_GETADDRINFO) 69 | 70 | AC_DEFINE(BUFSIZE, 4096, [Generic buffer size]) 71 | AM_CONDITIONAL(FDBUF_NO_MYSTRING, false) 72 | 73 | AC_ARG_ENABLE(tls, 74 | [ --enable-tls Enable SSL/TLS support], 75 | [case "${enableval}" in 76 | yes) tls=true; AC_DEFINE(HAVE_TLS, 1, [Build in support for TLS]) ;; 77 | no) tls=false ;; 78 | *) AC_MSG_ERROR(bad value ${enableval} for --enable-tls) ;; 79 | esac],[tls=false]) 80 | AM_CONDITIONAL(TLS, $tls) 81 | 82 | if $tls; then 83 | AC_CHECK_LIB(gnutls, gnutls_certificate_set_verify_function, 84 | [AC_DEFINE(HAVE_GNUTLS_SET_VERIFY_FUNCTION, 1, [libgnutls has gnutls_certificate_set_verify_function])]) 85 | AC_CHECK_LIB(gnutls, gnutls_priority_set_direct, 86 | [AC_DEFINE(HAVE_GNUTLS_PRIORITY_SET_DIRECT, 1, [libgnutls has gnutls_priority_set_direct])]) 87 | fi 88 | 89 | AC_CONFIG_FILES([Makefile doc/Makefile lib/Makefile lib/cli++/Makefile lib/fdbuf/Makefile lib/mystring/Makefile protocols/Makefile src/Makefile test/Makefile]) 90 | AC_OUTPUT 91 | -------------------------------------------------------------------------------- /doc/DIAGRAM: -------------------------------------------------------------------------------- 1 | sendmail --------------- mailq 2 | / \ 3 | / \ 4 | / \ 5 | nullmailer-inject nullmailer-smtpd 6 | \ / 7 | \ / 8 | \ / 9 | nullmailer-queue 10 | | 11 | | 12 | | 13 | QUEUE/tmp -> QUEUE/queue -> QUEUE/failed 14 | | 15 | | 16 | | 17 | nullmailer-send ---- nullmailer-dsn 18 | | 19 | | 20 | | 21 | BIN/PROTOCOL 22 | -------------------------------------------------------------------------------- /doc/Makefile.am: -------------------------------------------------------------------------------- 1 | # info_TEXINFOS = nullmailer.texi 2 | man_MANS = \ 3 | nullmailer-dsn.1 \ 4 | nullmailer-inject.1 \ 5 | sendmail.1 \ 6 | nullmailer.7 \ 7 | nullmailer-queue.8 \ 8 | nullmailer-send.8 9 | EXTRA_DIST = DIAGRAM $(man_MANS) 10 | -------------------------------------------------------------------------------- /doc/mailq.1: -------------------------------------------------------------------------------- 1 | .TH mailq 1 2 | .SH NAME 3 | mailq \- List the contents of the 4 | .B nullmailer 5 | queue. 6 | .SH SYNOPSIS 7 | .B mailq 8 | .SH DESCRIPTION 9 | This program loads the contents of the 10 | .B nullmailer 11 | queue and presents a short summary of its contents. 12 | .SH OPTIONS 13 | None. 14 | .SH RETURN VALUE 15 | Exits 0 if it was successful, otherwise it prints a diagnostic message 16 | to standard error and exits 1. 17 | .SH SEE ALSO 18 | nullmailer-queue(8), 19 | sendmail(1) 20 | -------------------------------------------------------------------------------- /doc/nullmailer-dsn.1: -------------------------------------------------------------------------------- 1 | .TH nullmailer-dsn 1 2 | .SH NAME 3 | nullmailer-dsn \- Reformat a queued message into a delivery status notification (DSN) 4 | .SH SYNOPSIS 5 | .B nullmailer-dsn 6 | [ 7 | .B options 8 | ] 9 | .I status-code 10 | < 11 | .I message 12 | .SH DESCRIPTION 13 | This program reads a nullmailer queue message from standard input, 14 | transforms it into a delivery status notification, and writes the result 15 | to standard output. The output is formatted to be fed into 16 | .BR nullmailer-queue . 17 | .P 18 | All times in the options are expressed as seconds since midnight GMT January 1, 1970 (the UNIX epoch). 19 | .SH OPTIONS 20 | .TP 21 | .BI \-\-diagnostic\-code= STRING 22 | Sets the actual diagnostic code issued by the mail transport. Must be in the form of 23 | "\fItype\fR; \fItext\fR", where 24 | .I type 25 | would typically be the protocol name, such as 26 | .BR SMTP . 27 | This adds a 28 | .B Diagnostic-Code 29 | header for each recipient. 30 | .TP 31 | .BI \-\-envelope\-id= STRING 32 | Original envelope ID. Setting this adds a 33 | .B Original-Envelope-Id 34 | header to the delivery status section. 35 | .TP 36 | .BI \-\-last\-attempt= TIME 37 | Sets the time of the last attempted delivery. Defaults to the access time of the input message. 38 | This sets the date in the 39 | .B Last-Attempt-Date 40 | header for each recipient. 41 | .TP 42 | .BI \-\-max\-lines= COUNT 43 | Sets the maximum number of lines of the original message to copy into the generated message. 44 | A value of zero copies only the original header. 45 | Negative values copy the whole message. 46 | Defaults to 47 | .I bouncelines 48 | below, or zero if that is empty. 49 | .TP 50 | .BI \-\-orig\-timestamp= TIME 51 | Sets the time of the original message. Defaults to the change time of 52 | the input message. This sets the date in the 53 | .B Arrival-Date 54 | header in the delivery status section. 55 | .TP 56 | .BI \-\-remote= STRING 57 | Sets the name of the remote MTA server. 58 | This adds a 59 | .B Remote-MTA 60 | header for each recipient. 61 | .TP 62 | .BI \-\-retry\-until= TIME 63 | Sets the time of the (future) final delivery attempt. No default. This adds a 64 | .B Will-Retry-Until 65 | header for each recipient. 66 | .SH RETURN VALUE 67 | Exits 0 if it was successful, otherwise it prints a diagnostic message 68 | to standard error and exits 1. 69 | .SH CONTROL FILES 70 | .TP 71 | .B adminaddr 72 | If 73 | .B doublebounceto 74 | is empty, double bounces are delivered to this address instead. 75 | If no address is configured, double bounces are not generated. 76 | .TP 77 | .B bouncelines 78 | Sets the maximum number of lines of the original message to copy. 79 | May be overridden by 80 | .B \-\-max\-lines 81 | as above. 82 | .TP 83 | .B bounceto 84 | The address to which all bounces should be sent. 85 | If it is empty, the original sender address is used. 86 | .TP 87 | .B defaultdomain 88 | The content of this file is appended to any host name that does not 89 | contain a period (except 90 | .BR localhost ), 91 | including 92 | .I defaulthost 93 | and 94 | .IR idhost . 95 | Defaults to the value of the 96 | .B me 97 | control file, if it exists, otherwise the literal name 98 | .BR defaultdomain . 99 | .TP 100 | .B defaulthost 101 | The content of this file is appended to any address that is missing a 102 | host name. 103 | Defaults to the value of the 104 | .I me 105 | control file, if it exists, otherwise the literal name 106 | .BR defaulthost . 107 | .TP 108 | .B doublebounceto 109 | If the original sender was empty (the original message was a delivery 110 | status or disposition notification), the double bounce is sent to the 111 | address in this file. 112 | .TP 113 | .B idhost 114 | The content of this file is used when building the message-id string 115 | for the message. 116 | Defaults to the canonicalized value of 117 | .IR defaulthost . 118 | .TP 119 | .B me 120 | The fully-qualified host name of the computer running nullmailer. 121 | Defaults to the literal name 122 | .BR me . 123 | .SH SEE ALSO 124 | nullmailer-queue(8), 125 | nullmailer-send(8) 126 | -------------------------------------------------------------------------------- /doc/nullmailer-queue.8: -------------------------------------------------------------------------------- 1 | .TH nullmailer-queue 8 2 | .SH NAME 3 | nullmailer-queue \- insert mail messages into the queue 4 | .SH SYNOPSIS 5 | .B nullmailer-queue 6 | .SH DESCRIPTION 7 | This program reads a formatted mail message from standard input and 8 | safely injects it into the outgoing mail queue. 9 | .PP 10 | The data sent into standard input is expected to have the following 11 | format: one line containing the envelope sender, one or more lines 12 | containing the recipients, a single blank line, and then the contents 13 | of the message exactly as it is to be transmitted to the destination. 14 | All lines are terminated with a single line-feed character. 15 | All addresses must contain a fully-qualified domain name. 16 | .PP 17 | .SH RETURN VALUE 18 | Exits 0 if it successfully queues the message. 19 | If it failed to queue the message, it exits 1 and prints an error 20 | message to stdandard output. 21 | .SH CONTROL FILES 22 | .TP 23 | .B adminaddr 24 | If this file is not empty, all recipients to users at either 25 | "localhost" (the literal string) or the canonical host name (from the 26 | .I me 27 | control file) are remapped to this address. 28 | This is provided to allow local daemons to be able to send email to 29 | "somebody@localhost" and have it go somewhere sensible instead of 30 | being bounced by your relay host. To send to multiple addresses, put 31 | them all on one line separated by a comma. 32 | .TP 33 | .B allmailfrom 34 | If this file is not empty, its contents will override the envelope 35 | sender on all messages. 36 | .SH OTHER FILES 37 | .TP 38 | .B /var/spool/nullmailer/queue 39 | The directory into which the completed messages are moved. 40 | .TP 41 | .B /var/spool/nullmailer/tmp 42 | The directory in which messages are formed temporarily. 43 | .TP 44 | .B /var/spool/nullmailer/trigger 45 | A pipe used to trigger 46 | .BR nullmailer-send 47 | to immediately start sending the message from the queue. 48 | .PP 49 | Note that due to 50 | .B nullmailer-queue 51 | using hard links to manage emails both 52 | .I /var/spool/nullmailer/queue 53 | and 54 | .I /var/spool/nullmailer/tmp 55 | MUST reside on the same filesystem. 56 | .SH SEE ALSO 57 | nullmailer-inject(1), 58 | nullmailer-send(8) 59 | .SH LIMITATIONS 60 | This program should enforce system-wide configurable message length 61 | limits. 62 | -------------------------------------------------------------------------------- /doc/nullmailer-smtpd.1: -------------------------------------------------------------------------------- 1 | .TH nullmailer-smtpd 1 2 | .SH NAME 3 | nullmailer-smtpd \- Simple SMTP agent for emulating sendmail -bs mode. 4 | .SH SYNOPSIS 5 | .B nullmailer-smtpd 6 | .SH DESCRIPTION 7 | This program emulates a simple SMTP interface that queues messages directly to 8 | .BR nullmailer-queue . 9 | It is invoked by the 10 | .BR sendmail 11 | wrapper when the 12 | .B \-bs 13 | option is used. 14 | .SH OPTIONS 15 | None. 16 | .SH RETURN VALUE 17 | Exits 0 if it was successful, otherwise it prints a diagnostic message 18 | to standard error and exits 1. 19 | .SH WARNINGS 20 | This is not a general purpose SMTP server. In particular, it cannot 21 | handle authentication, and it only does minimal validation of email 22 | addresses. Do not use this as a SMTP server on untrusted network 23 | interfaces. 24 | .SH SEE ALSO 25 | sendmail(1), 26 | nullmailer-queue(8) 27 | -------------------------------------------------------------------------------- /doc/nullmailer.7: -------------------------------------------------------------------------------- 1 | .TH nullmailer 7 2 | .SH NAME 3 | nullmailer \- Overview of nullmailer documentation 4 | .SH INTRODUCTION 5 | .B nullmailer 6 | is a simple and secure relay-only mail transport agent. 7 | .P 8 | Documentation on how messages are reformatted and injected into the 9 | queue can be found in 10 | .BR nullmailer-inject (1). 11 | Documentation on how messages are actually inserted into the queue can 12 | be found in 13 | .BR nullmailer-queue (8). 14 | The process of sending queued messages is described in 15 | .BR nullmailer-send (8). 16 | .P 17 | The following table lists all the control files used by 18 | .BR nullmailer . 19 | .P 20 | .RS 21 | .nf 22 | .ta 5c 23 | control file used by 24 | .I adminaddr \fBnullmailer-dsn\fR, \fBnullmailer-queue 25 | .I allmailfrom \fBnullmailer-queue 26 | .I defaultdomain \fBnullmailer-dsn\fR, \fBnullmailer-inject 27 | .I defaulthost \fBnullmailer-dsn\fR, \fBnullmailer-inject 28 | .I doublebounceto \fBnullmailer-dsn 29 | .I helohost \fBnullmailer-send 30 | .I idhost \fBnullmailer-dsn\fR, \fBnullmailer-inject 31 | .I maxpause \fBnullmailer-send 32 | .I me \fBnullmailer-dsn\fR, \fBnullmailer-inject 33 | .I pausetime \fBnullmailer-send 34 | .I remotes \fBnullmailer-send 35 | .I sendtimeout \fBnullmailer-send 36 | .fi 37 | .RE 38 | -------------------------------------------------------------------------------- /doc/sendmail.1: -------------------------------------------------------------------------------- 1 | .TH sendmail 1 2 | .SH NAME 3 | sendmail \- sendmail emulator interface for nullmailer 4 | .SH SYNOPSIS 5 | .B sendmail 6 | [ 7 | .B flags 8 | ] [ 9 | .I recipients 10 | ] < 11 | .I message 12 | .SH DESCRIPTION 13 | This program is a front end program for 14 | .B nullmailer-inject 15 | and 16 | .BR nullmailer-smtpd . 17 | It is used by programs that expect a 18 | .I sendmail 19 | interface for sending email. 20 | After parsing the command-line arguments, this program executes either 21 | .B nullmailer-inject 22 | or 23 | .B nullmailer-smtpd 24 | depending on the presence of the 25 | .I \-bs 26 | option on the command-line. 27 | See the documentation for 28 | .B nullmailer-inject 29 | for details on how messages are reformatted and queued. 30 | .SH OPTIONS 31 | .TP 32 | .B \-B TYPE 33 | .TP 34 | .B \-C FILE 35 | .TP 36 | .B \-d DEBUG 37 | .TP 38 | .B \-h COUNT 39 | .TP 40 | .B \-i 41 | .TP 42 | .B \-L TAG 43 | .TP 44 | .B \-N DSN 45 | .TP 46 | .B \-n 47 | .TP 48 | .B \-O OPTION 49 | .TP 50 | .B \-o OPTION 51 | .TP 52 | .B \-p PROTOCOL 53 | .TP 54 | .B \-q TIME 55 | .TP 56 | .B \-R RETURN 57 | .TP 58 | .B \-U 59 | .TP 60 | .B \-V ENVID 61 | .TP 62 | .B \-v 63 | .TP 64 | .B \-X LOGFILE 65 | Ignored for compatibility 66 | .TP 67 | .B \-bm 68 | Read mail from standard input (default). 69 | .TP 70 | .B \-bp 71 | List information about mail queue. This executes 72 | .BR mailq . 73 | .TP 74 | .B \-bs 75 | Use the SMTP protocol on standard input and standard output. This 76 | executes 77 | .BR nullmailer-smtpd . 78 | .TP 79 | .B \-F ADDRESS 80 | Sets the full name of the sender. 81 | .TP 82 | .B \-f ADDRESS 83 | Sets the envelope sender address. 84 | .TP 85 | .B \-r ADDRESS 86 | An alternate and obsolete form of the \-f flag. 87 | .TP 88 | .B \-t 89 | Read message for recipients and ignore the command-line arguments. 90 | .SH SEE ALSO 91 | mailq(1), 92 | nullmailer-inject(1), 93 | nullmailer-smtpd(1) 94 | -------------------------------------------------------------------------------- /lib/Makefile.am: -------------------------------------------------------------------------------- 1 | SUBDIRS = cli++ fdbuf mystring 2 | noinst_LIBRARIES = libmisc.a libnullmailer.a 3 | noinst_HEADERS = list.h 4 | EXTRA_DIST = make_defines.sh listtest.cc mergelib.sh 5 | CLEANFILES = defines.cc 6 | 7 | libmisc_a_SOURCES = \ 8 | ac/dirent.h ac/time.h ac/wait.h \ 9 | address.h address.cc \ 10 | argparse.h argparse.cc \ 11 | autoclose.h \ 12 | base64.h base64.cc \ 13 | canonicalize.h canonicalize.cc \ 14 | configio.h config_path.cc \ 15 | config_read.cc config_readlist.cc config_readint.cc config_syserr.cc \ 16 | connect.h tcpconnect.cc \ 17 | defines.h \ 18 | errcodes.h errcodes.cc \ 19 | hostname.h hostname.cc \ 20 | itoa.h itoa.cc \ 21 | makefield.cc makefield.h \ 22 | netstring.h netstring.cc \ 23 | forkexec.cc forkexec.h \ 24 | selfpipe.cc selfpipe.h \ 25 | setenv.cc setenv.h 26 | nodist_libmisc_a_SOURCES = defines.cc 27 | 28 | libnullmailer_a_SOURCES = 29 | libnullmailer.a: mergelib.sh libmisc.a fdbuf/libfdbuf.a \ 30 | mystring/libmystring.a Makefile 31 | $(RM) -f libnullmailer.a 32 | sh $(srcdir)/mergelib.sh libnullmailer.a \ 33 | libmisc.a \ 34 | fdbuf/libfdbuf.a \ 35 | mystring/libmystring.a 36 | 37 | defines.cc: Makefile make_defines.sh 38 | @echo Creating defines.cc 39 | @sh $(srcdir)/make_defines.sh \ 40 | @localstatedir@/spool/nullmailer \ 41 | @sysconfdir@/nullmailer \ 42 | @libexecdir@/nullmailer \ 43 | @bindir@ \ 44 | @sbindir@ 45 | -------------------------------------------------------------------------------- /lib/ac/dirent.h: -------------------------------------------------------------------------------- 1 | #include 2 | #if HAVE_DIRENT_H 3 | # include 4 | # define NAMLEN(dirent) strlen((dirent)->d_name) 5 | #else 6 | # define dirent direct 7 | # define NAMLEN(dirent) (dirent)->d_namlen 8 | # if HAVE_SYS_NDIR_H 9 | # include 10 | # endif 11 | # if HAVE_SYS_DIR_H 12 | # include 13 | # endif 14 | # if HAVE_NDIR_H 15 | # include 16 | # endif 17 | #endif 18 | -------------------------------------------------------------------------------- /lib/ac/time.h: -------------------------------------------------------------------------------- 1 | #if TIME_WITH_SYS_TIME 2 | # include 3 | # include 4 | #else 5 | # if HAVE_SYS_TIME_H 6 | # include 7 | # else 8 | # include 9 | # endif 10 | #endif 11 | -------------------------------------------------------------------------------- /lib/ac/wait.h: -------------------------------------------------------------------------------- 1 | #include 2 | #if HAVE_SYS_WAIT_H 3 | # include 4 | #endif 5 | #ifndef WEXITSTATUS 6 | # define WEXITSTATUS(stat_val) ((unsigned)(stat_val) >> 8) 7 | #endif 8 | #ifndef WIFEXITED 9 | # define WIFEXITED(stat_val) (((stat_val) & 255) == 0) 10 | #endif 11 | -------------------------------------------------------------------------------- /lib/address.h: -------------------------------------------------------------------------------- 1 | #ifndef NULLMAILER__ADDRESS__H__ 2 | #define NULLMAILER__ADDRESS__H__ 3 | 4 | #include "mystring/mystring.h" 5 | 6 | bool parse_addresses(mystring& line, mystring& list); 7 | 8 | #endif // NULLMAILER__ADDRESS__H__ 9 | -------------------------------------------------------------------------------- /lib/argparse.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include "argparse.h" 3 | 4 | static const char* parse_arg(mystring& arg, const char* start, const char* end) 5 | { 6 | const char* ptr; 7 | for (ptr = start; ptr < end && ! isspace(*ptr); ++ptr) { 8 | switch (*ptr) { 9 | case '\'': 10 | arg.append(start, ptr - start); 11 | for (start = ++ptr; ptr < end && *ptr != '\''; ++ptr) ; 12 | arg.append(start, ptr - start); 13 | start = ptr + 1; 14 | continue; 15 | case '"': 16 | arg.append(start, ptr - start); 17 | for (start = ++ptr; ptr < end && *ptr != '\"'; ptr++) { 18 | if (*ptr == '\\') { 19 | arg.append(start, ptr - start); 20 | if (++ptr < end) 21 | arg.append(ptr, 1); 22 | start = ++ptr; 23 | } 24 | } 25 | arg.append(start, ptr - start); 26 | start = ptr + 1; 27 | continue; 28 | case '\\': 29 | arg.append(start, ptr - start); 30 | if (++ptr < end) 31 | arg.append(ptr, 1); 32 | start = ++ptr; 33 | continue; 34 | } 35 | } 36 | if ((ptr - start) > 0) 37 | arg.append(start, ptr - start); 38 | return ptr; 39 | } 40 | 41 | unsigned parse_args(arglist& lst, const mystring& str) 42 | { 43 | lst.empty(); 44 | const char* ptr = str.c_str(); 45 | const char* end = ptr + str.length(); 46 | unsigned count = 0; 47 | while (ptr < end) { 48 | // Skip any leading spaces 49 | if (isspace(*ptr)) 50 | ++ptr; 51 | else { 52 | mystring s; 53 | ptr = parse_arg(s, ptr, end); 54 | if (ptr == 0) 55 | break; 56 | lst.append(s); 57 | ++count; 58 | } 59 | } 60 | return count; 61 | } 62 | -------------------------------------------------------------------------------- /lib/argparse.h: -------------------------------------------------------------------------------- 1 | #ifndef NULLMAILER__ARGPARSE__H 2 | #define NULLMAILER__ARGPARSE__H 3 | 4 | #include "mystring/mystring.h" 5 | #include "list.h" 6 | 7 | typedef list arglist; 8 | unsigned parse_args(arglist&, const mystring& str); 9 | 10 | #endif 11 | -------------------------------------------------------------------------------- /lib/autoclose.h: -------------------------------------------------------------------------------- 1 | #ifndef NULLMAILER_AUTOCLOSE__H__ 2 | #define NULLMAILER_AUTOCLOSE__H__ 3 | 4 | #include 5 | 6 | // Simple inline wrapper to automatically close an open file descriptor 7 | class autoclose 8 | { 9 | private: 10 | int fd; 11 | 12 | public: 13 | inline autoclose(int f = -1) : fd(f) { } 14 | inline ~autoclose() { close(); } 15 | inline operator int() const { return fd; } 16 | inline int operator =(int f) 17 | { 18 | close(); 19 | return fd = f; 20 | } 21 | inline void close() 22 | { 23 | if (fd >= 0) { 24 | ::close(fd); 25 | fd = -1; 26 | } 27 | } 28 | }; 29 | 30 | // Simple inline wrapper to handle opening and closing a pipe pair 31 | class autoclose_pipe 32 | { 33 | private: 34 | int fds[2]; 35 | 36 | public: 37 | inline autoclose_pipe() 38 | { 39 | fds[0] = fds[1] = -1; 40 | } 41 | inline ~autoclose_pipe() 42 | { 43 | close(); 44 | } 45 | inline int operator[](int i) const { return fds[i]; } 46 | inline bool open() 47 | { 48 | return pipe(fds) == 0; 49 | } 50 | inline void close() 51 | { 52 | if (fds[0] >= 0) { 53 | ::close(fds[0]); 54 | ::close(fds[1]); 55 | fds[0] = fds[1] = -1; 56 | } 57 | } 58 | // Close one half of the pair, return the other, and mark both as if they were closed. 59 | inline int extract(int which) 60 | { 61 | int result = fds[which]; 62 | ::close(fds[1-which]); 63 | fds[0] = fds[1] = -1; 64 | return result; 65 | } 66 | }; 67 | 68 | #endif // NULLMAILER_AUTOCLOSE__H__ 69 | -------------------------------------------------------------------------------- /lib/base64.cc: -------------------------------------------------------------------------------- 1 | // nullmailer -- a simple relay-only MTA 2 | // Copyright (C) 2018 Bruce Guenter 3 | // 4 | // This program is free software; you can redistribute it and/or modify 5 | // it under the terms of the GNU General Public License as published by 6 | // the Free Software Foundation; either version 2 of the License, or 7 | // (at your option) any later version. 8 | // 9 | // This program is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU General Public License 15 | // along with this program; if not, write to the Free Software 16 | // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 17 | // 18 | // You can contact me at . There is also a mailing list 19 | // available to discuss this package. To subscribe, send an email to 20 | // . 21 | 22 | #include "base64.h" 23 | 24 | static char basis[] = 25 | "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; 26 | 27 | void base64_encode(const mystring& in, mystring& out) 28 | { 29 | size_t length; 30 | const unsigned char* ptr; 31 | char buf[4]; 32 | for (length = in.length(), ptr = (const unsigned char*)in.c_str(); 33 | length >= 3; 34 | length -= 3, ptr += 3) { 35 | base64_encode_chunk(ptr, 3, buf); 36 | out.append(buf, 4); 37 | } 38 | if (length > 0) { 39 | base64_encode_chunk(ptr, length, buf); 40 | out.append(buf, 4); 41 | } 42 | } 43 | 44 | void base64_encode_chunk(const unsigned char bin[3], unsigned len, 45 | char encoded[4]) 46 | { 47 | encoded[0] = basis[bin[0] >> 2]; 48 | switch(len) { 49 | case 1: 50 | encoded[1] = basis[(bin[0] << 4) & 0x3f]; 51 | encoded[2] = encoded[3] = '='; 52 | break; 53 | case 2: 54 | encoded[1] = basis[(bin[0] << 4 | bin[1] >> 4) & 0x3f]; 55 | encoded[2] = basis[(bin[1] << 2) & 0x3f]; 56 | encoded[3] = '='; 57 | break; 58 | case 3: 59 | encoded[1] = basis[(bin[0] << 4 | bin[1] >> 4) & 0x3f]; 60 | encoded[2] = basis[(bin[1] << 2 | bin[2] >> 6) & 0x3f]; 61 | encoded[3] = basis[bin[2] & 0x3f]; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /lib/base64.h: -------------------------------------------------------------------------------- 1 | #ifndef NULLMAILER_BASE64__H__ 2 | #define NULLMAILER_BASE64__H__ 3 | 4 | #include "mystring/mystring.h" 5 | 6 | extern void base64_encode(const mystring& in, mystring& out); 7 | extern void base64_encode_chunk(const unsigned char bin[3], unsigned len, 8 | char encoded[4]); 9 | 10 | #endif // NULLMAILER_BASE64__H__ 11 | -------------------------------------------------------------------------------- /lib/canonicalize.cc: -------------------------------------------------------------------------------- 1 | // nullmailer -- a simple relay-only MTA 2 | // Copyright (C) 2018 Bruce Guenter 3 | // 4 | // This program is free software; you can redistribute it and/or modify 5 | // it under the terms of the GNU General Public License as published by 6 | // the Free Software Foundation; either version 2 of the License, or 7 | // (at your option) any later version. 8 | // 9 | // This program is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU General Public License 15 | // along with this program; if not, write to the Free Software 16 | // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 17 | // 18 | // You can contact me at . There is also a mailing list 19 | // available to discuss this package. To subscribe, send an email to 20 | // . 21 | 22 | #include "config.h" 23 | #include "mystring/mystring.h" 24 | #include "canonicalize.h" 25 | #include "hostname.h" 26 | 27 | void canonicalize(mystring& domain) 28 | { 29 | if(!domain) 30 | domain = defaulthost; 31 | if(domain != "localhost" && domain.find_first('.') < 0) { 32 | if(!!defaultdomain) { 33 | if (!!domain) domain += "."; 34 | domain += defaultdomain; 35 | } 36 | } 37 | } 38 | 39 | -------------------------------------------------------------------------------- /lib/canonicalize.h: -------------------------------------------------------------------------------- 1 | #ifndef NULLMAILER__CANONICALIZE__H__ 2 | #define NULLMAILER__CANONICALIZE__H__ 3 | 4 | #include "mystring/mystring.h" 5 | void canonicalize(mystring& domain); 6 | 7 | #endif // NULLMAILER__CANONICALIZE__H__ 8 | -------------------------------------------------------------------------------- /lib/cli++/Makefile.am: -------------------------------------------------------------------------------- 1 | noinst_LIBRARIES = libcli++.a 2 | EXTRA_DIST = clitest.cc cli++topod.pl 3 | 4 | AM_CPPFLAGS = -I$(top_srcdir)/lib 5 | #LIBS = @LIBS@ -L. -lcli++ -L../lib -lvmailmgr 6 | 7 | libcli___a_SOURCES = cli++.h main.cc messages.cc only_long.cc 8 | #clitest_SOURCES = clitest.cc 9 | 10 | -------------------------------------------------------------------------------- /lib/cli++/cli++.h: -------------------------------------------------------------------------------- 1 | #ifndef VMAILMGR__CLIPP__CLIPP__H__ 2 | #define VMAILMGR__CLIPP__CLIPP__H__ 3 | 4 | typedef bool (*cli_funcptr)(void*); 5 | 6 | struct cli_stringlist 7 | { 8 | const char* string; 9 | cli_stringlist* next; 10 | 11 | cli_stringlist(const char* s) 12 | : string(s), next(0) 13 | { 14 | } 15 | }; 16 | 17 | struct cli_option 18 | { 19 | char ch; 20 | const char* name; 21 | enum { flag, counter, integer, string, stringlist, ulong } type; 22 | int flag_value; 23 | void* dataptr; 24 | const char* helpstr; 25 | const char* defaultstr; 26 | 27 | int set(const char* arg); 28 | int parse_long_eq(const char* arg, int as_short); 29 | int parse_long_noeq(const char* arg, int as_short); 30 | }; 31 | 32 | #define CLI_OPTION_END {0, 0, cli_option::flag, 0, 0, 0, 0} 33 | 34 | /* The following are required from the CLI program */ 35 | extern const char* cli_program; 36 | extern const char* cli_help_prefix; 37 | extern const char* cli_help_suffix; 38 | extern const char* cli_args_usage; 39 | extern const int cli_args_min; 40 | extern const int cli_args_max; 41 | extern cli_option cli_options[]; 42 | extern const bool cli_only_long; 43 | extern int cli_main(int argc, char* argv[]); 44 | 45 | /* The following are provided to the CLI program */ 46 | extern const char* argv0; 47 | extern const char* argv0base; 48 | extern const char* argv0dir; 49 | extern void usage(int exit_value, const char* errorstr = 0); 50 | extern int cli_parse_args(int argc, char* argv[]); 51 | 52 | extern void cli_error(int exit_value, 53 | const char*, 54 | const char* = 0, 55 | const char* = 0, 56 | const char* = 0); 57 | 58 | extern void cli_syserror(int exit_value, 59 | const char*, 60 | const char* = 0, 61 | const char* = 0, 62 | const char* = 0); 63 | 64 | extern void cli_warning(const char*, 65 | const char* = 0, 66 | const char* = 0, 67 | const char* = 0); 68 | 69 | #endif 70 | -------------------------------------------------------------------------------- /lib/cli++/cli++topod.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | sub cstr2pod { 4 | local($_) = shift; 5 | s/\\"/"/go; 6 | s/"([^\"]*)"/"C<$1>"/go; 7 | $_; 8 | } 9 | 10 | $section = 1; 11 | 12 | @section_order = ( 13 | 'NAME', 14 | 'SYNOPSIS', 15 | 'DESCRIPTION', 16 | 'OPTIONS', 17 | 'RETURN VALUE', 18 | 'ERRORS', 19 | 'EXAMPLES', 20 | 'ENVIRONMENT', 21 | 'FILES', 22 | 'SEE ALSO', 23 | 'NOTES', 24 | 'CAVEATS', 25 | 'WARNINGS', 26 | 'DIAGNOSTICS', 27 | 'BUGS', 28 | 'RESTRICTIONS', 29 | 'AUTHOR', 30 | 'AUTHORS', 31 | 'HISTORY' 32 | ); 33 | 34 | sub type2word { 35 | my($type) = shift; 36 | return 'INT' if $type eq 'integer'; 37 | return 'UINT' if $type eq 'ulong'; 38 | return 'STR' if $type eq 'string' || $type eq 'stringlist'; 39 | return '' if $type eq 'flag' || $type eq 'counter'; 40 | die "Invalid cli option type '$type'"; 41 | } 42 | 43 | sub add_option { 44 | my($short, $long, $type, $desc) = @_; 45 | 46 | my $s = '[B<'; 47 | my $o = '=item B<'; 48 | if($short) { 49 | $s .= "-$short"; 50 | $o .= "-$short"; 51 | if($type) { 52 | $s .= " $type"; 53 | $o .= " $type"; 54 | } 55 | } 56 | if($short && $long) { 57 | $s .= ">]\n[B<"; 58 | $o .= ">, B<"; 59 | } 60 | if($long) { 61 | $s .= "--$long"; 62 | $o .= "--$long"; 63 | if($type) { 64 | $s .= "=$type"; 65 | $o .= "=$type"; 66 | } 67 | } 68 | $s .= ">]\n"; 69 | $o .= ">\n\n$desc\n\n"; 70 | 71 | $synopsis .= $s; 72 | $options = "=over 8\n\n" unless $options; 73 | $options .= $o; 74 | } 75 | 76 | sub parse_option { 77 | local($_) = shift; 78 | s/^\s*\{\s*//o; 79 | s/\s*\},?\s*/ /o; 80 | 81 | my $short = $1 if s/^'([^\'])',\s*//o; 82 | die "Invalid cli option" unless $short || s/^0,\s*//o; 83 | 84 | my $long = $1 if s/^"([^\"]+)",\s*//o; 85 | die "Invalid cli_option" unless $long || s/^0,\s*//o; 86 | 87 | my $type = $1 if s/^cli_option::(\S+),\s*//o; 88 | die "Invalid cli_option" unless $type; 89 | $type = &type2word($type); 90 | 91 | my $val = $1 if s/^([^,]+),\s*//o; 92 | my $var = $1 if s/^&([^,]+),\s*//o; 93 | 94 | my $desc = cstr2pod($1) if s/^"([^,]+)",\s*//o; 95 | die "Invalid cli_option" unless $desc; 96 | $desc =~ s/\.?$/./o if $desc; 97 | 98 | my $default = $1 if s/^"([^\"]+)"\s+//o; 99 | die "Invalid cli_option" unless $default || s/^0\s+//o; 100 | $desc .= " Defaults to $default." if $default; 101 | 102 | s/\s*\/\/\s*/ /go; 103 | s/^\s*//o; 104 | 105 | add_option($short, $long, $type, $_ || $desc); 106 | } 107 | 108 | sub parse_options { 109 | $synopsis = "B<$program>\n"; 110 | 111 | my $line; 112 | while(<>) { 113 | s/^\s+//o; 114 | s/\s+$//o; 115 | if($line && /^\{/o) { 116 | &parse_option($line); 117 | $line = ""; 118 | } 119 | next if /^\{\s*0\s*\},?/o; 120 | next if /^\{\s*0\s*,\s*\},?/o; 121 | last if /^\s*\};/o; 122 | $line =~ s/$/ $_/; 123 | } 124 | &parse_option($line) if $line; 125 | 126 | $synopsis .= "I<$usage>" if $usage; 127 | $options .= "=back" if $options; 128 | $sections{'SYNOPSIS'} = $synopsis; 129 | $sections{'OPTIONS'} = $options; 130 | } 131 | 132 | sub parse_notes { 133 | my $section; 134 | my $title; 135 | while(<>) { 136 | chomp; 137 | last unless /^$/o || s/^\/\/\s*//o; 138 | if(/^[\sA-Z]+$/o) { 139 | $sections{$title} = $section if $title && $section; 140 | undef $section; 141 | $title = $_; 142 | } else { 143 | $section .= "$_\n"; 144 | } 145 | } 146 | $sections{$title} = $section if $title && $section; 147 | } 148 | 149 | sub parse_header_line { 150 | local($_, $comment) = @_; 151 | if(s/^\s*const\s+char\s*\*\s*cli_(\S+)\s*=\s*//o) { 152 | my $name = $1; 153 | s/;\s*$//o; 154 | s/^\"//; 155 | s/\"$//o; 156 | s/\\n$//o; 157 | s/\\n""/\n/go; 158 | $program = $_ if $name eq 'program'; 159 | $prefix = $_ if $name eq 'help_prefix'; 160 | $usage = $_ if $name eq 'args_usage'; 161 | $suffix = $_ if $name eq 'help_suffix'; 162 | } 163 | } 164 | 165 | sub parse_header { 166 | my $comment = ''; 167 | my $line = ''; 168 | while(<>) { 169 | s/^\s+//o; 170 | s/\s+$//o; 171 | if(s/^.*Copyright\s*\(C\)\s*[\d,]+\s*//o) { 172 | $author = $_; 173 | } else { 174 | last if ($program && $prefix && /^$/o); 175 | next if /^#/o; 176 | $comment .= "$1\n" if s|\s*//\s*(.*)$||o; 177 | $line =~ s/$/\n$_/; 178 | if(/;$/o) { 179 | &parse_header_line($line, $comment); 180 | undef $line; 181 | undef $comment; 182 | } 183 | } 184 | } 185 | } 186 | 187 | sub parse_description { 188 | while(<>) { 189 | s/^\s+//o; 190 | s/\s+$//o; 191 | last if / cli_options\[\]\s*=\s*\{/o; 192 | next unless s/^\/\/\s*//o; 193 | $description .= "$_\n"; 194 | } 195 | } 196 | 197 | &parse_header; 198 | &parse_description; 199 | &parse_options; 200 | &parse_notes; 201 | 202 | $description .= "\n\n$suffix\n" if $suffix; 203 | 204 | $sections{'NAME'} = "$program - $prefix"; 205 | $sections{'DESCRIPTION'} = $description; 206 | $sections{'AUTHORS'} = $author if $author; 207 | 208 | foreach $section (@section_order) { 209 | print "=head1 $section\n\n$sections{$section}\n\n" 210 | if $sections{$section}; 211 | } 212 | 213 | 1; 214 | -------------------------------------------------------------------------------- /lib/cli++/clitest.cc: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2018 Bruce Guenter 2 | // 3 | // This library is free software; you can redistribute it and/or 4 | // modify it under the terms of the GNU Lesser General Public 5 | // License as published by the Free Software Foundation; either 6 | // version 2.1 of the License, or (at your option) any later version. 7 | // 8 | // This library is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 11 | // Lesser General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU Lesser General Public 14 | // License along with this library; if not, write to the Free Software 15 | // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 16 | 17 | #include 18 | #include "cli++.h" 19 | #include "fdbuf/fdbuf.h" 20 | 21 | const char* cli_program = "clitest"; 22 | const char* cli_help_prefix = "Does nothing but set flags\n"; 23 | const char* cli_help_suffix = ""; 24 | const char* cli_args_usage = ""; 25 | const int cli_args_min = 0; 26 | const int cli_args_max = -1; 27 | int o_flag = 0; 28 | int o_int = 0; 29 | char* o_string = "nostring"; 30 | cli_option cli_options[] = { 31 | { 'f', "flag", cli_option::flag, 1, &o_flag, "Sets a flag", 0 }, 32 | { 'i', "int", cli_option::integer, 0, &o_int, "Sets an integer", 0 }, 33 | { 's', "str", cli_option::string, 0, &o_string, "Sets a string", 0}, 34 | {0} }; 35 | 36 | int cli_main(int argc, char* argv[]) 37 | { 38 | fout << "argv0=" << argv0 << endl 39 | << " argv0dir=" << argv0dir << endl 40 | << " argv0base=" << argv0base << endl; 41 | fout << "The flag is set to " << o_flag << endl; 42 | fout << "The integer is set to " << o_int << endl; 43 | fout << "The string is set to " << o_string << endl; 44 | for(int i = 0; i < argc; i++) 45 | fout << "argv[" << i << "] = '" << argv[i] << "'\n"; 46 | return 0; 47 | } 48 | -------------------------------------------------------------------------------- /lib/cli++/messages.cc: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2018 Bruce Guenter 2 | // 3 | // This library is free software; you can redistribute it and/or 4 | // modify it under the terms of the GNU Lesser General Public 5 | // License as published by the Free Software Foundation; either 6 | // version 2.1 of the License, or (at your option) any later version. 7 | // 8 | // This library is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 11 | // Lesser General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU Lesser General Public 14 | // License along with this library; if not, write to the Free Software 15 | // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 16 | 17 | #include 18 | #include 19 | #include "fdbuf/fdbuf.h" 20 | #include 21 | #include 22 | #include "cli++.h" 23 | 24 | extern const char* argv0; 25 | 26 | static void cli_msg(const char* prefix, 27 | const char* a, 28 | const char* b, 29 | const char* c, 30 | const char* d, 31 | bool add_error = false) 32 | { 33 | ferr << cli_program << ": " << prefix << a; 34 | if(b) ferr << b; 35 | if(c) ferr << c; 36 | if(d) ferr << d; 37 | if (add_error) 38 | ferr << ": " << strerror(errno); 39 | ferr << endl; 40 | } 41 | 42 | void cli_error(int exit_value, 43 | const char* a, 44 | const char* b, 45 | const char* c, 46 | const char* d) 47 | { 48 | cli_msg("Error: ", a, b, c, d, false); 49 | exit(exit_value); 50 | } 51 | 52 | void cli_syserror(int exit_value, 53 | const char* a, 54 | const char* b, 55 | const char* c, 56 | const char* d) 57 | { 58 | cli_msg("Error: ", a, b, c, d, true); 59 | exit(exit_value); 60 | } 61 | 62 | void cli_warning(const char* a, 63 | const char* b, 64 | const char* c, 65 | const char* d) 66 | { 67 | cli_msg("Warning: ", a, b, c, d, false); 68 | } 69 | -------------------------------------------------------------------------------- /lib/cli++/only_long.cc: -------------------------------------------------------------------------------- 1 | #include "cli++.h" 2 | 3 | const bool cli_only_long = false; 4 | -------------------------------------------------------------------------------- /lib/config_path.cc: -------------------------------------------------------------------------------- 1 | // nullmailer -- a simple relay-only MTA 2 | // Copyright (C) 2018 Bruce Guenter 3 | // 4 | // This program is free software; you can redistribute it and/or modify 5 | // it under the terms of the GNU General Public License as published by 6 | // the Free Software Foundation; either version 2 of the License, or 7 | // (at your option) any later version. 8 | // 9 | // This program is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU General Public License 15 | // along with this program; if not, write to the Free Software 16 | // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 17 | // 18 | // You can contact me at . There is also a mailing list 19 | // available to discuss this package. To subscribe, send an email to 20 | // . 21 | 22 | #include "config.h" 23 | #include "defines.h" 24 | #include 25 | #include "configio.h" 26 | #include "fdbuf/fdbuf.h" 27 | 28 | mystring config_BIN_DIR; 29 | mystring config_CONFIG_DIR; 30 | mystring config_HOME_DIR; 31 | mystring config_PROTOCOLS_DIR; 32 | mystring config_SBIN_DIR; 33 | 34 | static mystring test_prefix; 35 | static bool initialized = false; 36 | 37 | mystring config_path(const char* dflt, const char* testdir, const char* subdir, const char* filename) 38 | { 39 | if (!initialized) { 40 | // Check if the program is running setuid, to avoid privilege escalation. 41 | if (getuid() == geteuid()) 42 | test_prefix = getenv("NULLMAILER_TEST_PREFIX"); 43 | } 44 | mystring result = test_prefix; 45 | if (!result) 46 | result = dflt; 47 | else { 48 | result += '/'; 49 | result += testdir; 50 | } 51 | result += '/'; 52 | if (subdir) { 53 | result += subdir; 54 | result += '/'; 55 | } 56 | result += filename; 57 | return result; 58 | } 59 | -------------------------------------------------------------------------------- /lib/config_read.cc: -------------------------------------------------------------------------------- 1 | // nullmailer -- a simple relay-only MTA 2 | // Copyright (C) 2018 Bruce Guenter 3 | // 4 | // This program is free software; you can redistribute it and/or modify 5 | // it under the terms of the GNU General Public License as published by 6 | // the Free Software Foundation; either version 2 of the License, or 7 | // (at your option) any later version. 8 | // 9 | // This program is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU General Public License 15 | // along with this program; if not, write to the Free Software 16 | // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 17 | // 18 | // You can contact me at . There is also a mailing list 19 | // available to discuss this package. To subscribe, send an email to 20 | // . 21 | 22 | #include "config.h" 23 | #include "defines.h" 24 | #include "configio.h" 25 | #include "fdbuf/fdbuf.h" 26 | 27 | bool config_read(const char* filename, mystring& result) 28 | { 29 | const mystring fullname = CONFIG_PATH(CONFIG, NULL, filename); 30 | fdibuf in(fullname.c_str()); 31 | if (!in) 32 | return config_syserr(fullname.c_str()); 33 | if(!in.getline(result)) 34 | return false; 35 | result = result.strip(); 36 | return result.length() > 0; 37 | } 38 | -------------------------------------------------------------------------------- /lib/config_readint.cc: -------------------------------------------------------------------------------- 1 | // nullmailer -- a simple relay-only MTA 2 | // Copyright (C) 2018 Bruce Guenter 3 | // 4 | // This program is free software; you can redistribute it and/or modify 5 | // it under the terms of the GNU General Public License as published by 6 | // the Free Software Foundation; either version 2 of the License, or 7 | // (at your option) any later version. 8 | // 9 | // This program is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU General Public License 15 | // along with this program; if not, write to the Free Software 16 | // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 17 | // 18 | // You can contact me at . There is also a mailing list 19 | // available to discuss this package. To subscribe, send an email to 20 | // . 21 | 22 | #include "config.h" 23 | #include 24 | #include 25 | #include "defines.h" 26 | #include "configio.h" 27 | #include "fdbuf/fdbuf.h" 28 | 29 | bool config_readint(const char* filename, int& result) 30 | { 31 | mystring tmp; 32 | if(!config_read(filename, tmp)) 33 | return false; 34 | char* endptr; 35 | long longresult = strtol(tmp.c_str(), &endptr, 10); 36 | result = (int) longresult; 37 | return endptr > tmp.c_str() && longresult >= INT_MIN && longresult <= INT_MAX; 38 | } 39 | -------------------------------------------------------------------------------- /lib/config_readlist.cc: -------------------------------------------------------------------------------- 1 | // nullmailer -- a simple relay-only MTA 2 | // Copyright (C) 2018 Bruce Guenter 3 | // 4 | // This program is free software; you can redistribute it and/or modify 5 | // it under the terms of the GNU General Public License as published by 6 | // the Free Software Foundation; either version 2 of the License, or 7 | // (at your option) any later version. 8 | // 9 | // This program is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU General Public License 15 | // along with this program; if not, write to the Free Software 16 | // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 17 | // 18 | // You can contact me at . There is also a mailing list 19 | // available to discuss this package. To subscribe, send an email to 20 | // . 21 | 22 | #include "config.h" 23 | #include "defines.h" 24 | #include "configio.h" 25 | #include "fdbuf/fdbuf.h" 26 | 27 | bool config_readlist(const char* filename, list& result) 28 | { 29 | const mystring fullname = CONFIG_PATH(CONFIG, NULL, filename); 30 | fdibuf in(fullname.c_str()); 31 | if(!in) 32 | return config_syserr(fullname.c_str()); 33 | mystring tmp; 34 | bool nonempty = false; 35 | while(in.getline(tmp)) { 36 | tmp = tmp.strip(); 37 | if(tmp[0] != '#' && tmp.length() > 0) { 38 | result.append(tmp); 39 | nonempty = true; 40 | } 41 | } 42 | return nonempty; 43 | } 44 | -------------------------------------------------------------------------------- /lib/config_syserr.cc: -------------------------------------------------------------------------------- 1 | // nullmailer -- a simple relay-only MTA 2 | // Copyright (C) 2018 Bruce Guenter 3 | // 4 | // This program is free software; you can redistribute it and/or modify 5 | // it under the terms of the GNU General Public License as published by 6 | // the Free Software Foundation; either version 2 of the License, or 7 | // (at your option) any later version. 8 | // 9 | // This program is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU General Public License 15 | // along with this program; if not, write to the Free Software 16 | // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 17 | // 18 | // You can contact me at . There is also a mailing list 19 | // available to discuss this package. To subscribe, send an email to 20 | // . 21 | 22 | #include 23 | #include "config.h" 24 | #include "errcodes.h" 25 | #include "configio.h" 26 | #include "cli++/cli++.h" 27 | 28 | bool config_syserr(const char* filename) 29 | { 30 | if (errno != ENOENT) 31 | cli_syserror(ERR_CONFIG, "Could not read config file \"", filename, "\""); 32 | return false; 33 | } 34 | -------------------------------------------------------------------------------- /lib/configio.h: -------------------------------------------------------------------------------- 1 | #ifndef NULLMAILER__CONFIGIO__H__ 2 | #define NULLMAILER__CONFIGIO__H__ 3 | 4 | #include "mystring/mystring.h" 5 | #include "list.h" 6 | 7 | mystring config_path(const char* dflt, const char* testdir, const char* subdir, const char* filename); 8 | #define CONFIG_PATH(NAME, SUBDIR, FILENAME) config_path(NAME##_DIR, NAME##_TEST_DIR, SUBDIR, FILENAME) 9 | 10 | bool config_read(const char* filename, mystring& result); 11 | bool config_readlist(const char* filename, list& result); 12 | bool config_readint(const char* filename, int& result); 13 | bool config_syserr(const char* filename); 14 | 15 | #endif // NULLMAILER__CONFIGIO__H__ 16 | -------------------------------------------------------------------------------- /lib/connect.h: -------------------------------------------------------------------------------- 1 | #ifndef NULLMAILER_CONNECT__H__ 2 | #define NULLMAILER_CONNECT__H__ 3 | 4 | extern int tcpconnect(const char* hostname, int port, const char* source); 5 | 6 | #endif // NULLMAILER_CONNECT__H__ 7 | -------------------------------------------------------------------------------- /lib/defines.h: -------------------------------------------------------------------------------- 1 | #ifndef NULLMAILER__DEFINES__H__ 2 | #define NULLMAILER__DEFINES__H__ 3 | 4 | extern const char QUEUE_DIR[]; 5 | extern const char CONFIG_DIR[]; 6 | extern const char PROTOCOLS_DIR[]; 7 | extern const char BIN_DIR[]; 8 | extern const char SBIN_DIR[]; 9 | 10 | #define QUEUE_TEST_DIR "queue" 11 | #define CONFIG_TEST_DIR "conf" 12 | #define PROTOCOLS_TEST_DIR "protocols" 13 | #define BIN_TEST_DIR "bin" 14 | #define SBIN_TEST_DIR "sbin" 15 | 16 | #endif /* NULLMAILER__DEFINES__H__ */ 17 | -------------------------------------------------------------------------------- /lib/errcodes.cc: -------------------------------------------------------------------------------- 1 | // nullmailer -- a simple relay-only MTA 2 | // Copyright (C) 2018 Bruce Guenter 3 | // 4 | // This program is free software; you can redistribute it and/or modify 5 | // it under the terms of the GNU General Public License as published by 6 | // the Free Software Foundation; either version 2 of the License, or 7 | // (at your option) any later version. 8 | // 9 | // This program is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU General Public License 15 | // along with this program; if not, write to the Free Software 16 | // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 17 | // 18 | // You can contact me at . There is also a mailing list 19 | // available to discuss this package. To subscribe, send an email to 20 | // . 21 | 22 | #include "errcodes.h" 23 | 24 | const char* errorstr(int code) 25 | { 26 | switch (code) { 27 | case 0: return "No error"; 28 | case ERR_HOST_NOT_FOUND: return "Host not found"; 29 | case ERR_NO_ADDRESS: return "Host has no address"; 30 | case ERR_GHBN_FATAL: return "Fatal error in gethostbyname"; 31 | case ERR_GHBN_TEMP: return "Temporary error in gethostbyname"; 32 | case ERR_SOCKET: return "Socket failed"; 33 | case ERR_CONN_REFUSED: return "Connection refused"; 34 | case ERR_CONN_TIMEDOUT: return "Connection timed out"; 35 | case ERR_CONN_UNREACHABLE: return "Host or network unreachable"; 36 | case ERR_CONN_FAILED: return "Connection failed"; 37 | case ERR_PROTO: return "Protocol error"; 38 | case ERR_MSG_OPEN: return "Could not open message"; 39 | case ERR_MSG_READ: return "Could not read message"; 40 | case ERR_MSG_WRITE: return "Could not write message"; 41 | case ERR_EXEC_FAILED: return "Could not exec program"; 42 | case ERR_MSG_TEMPFAIL: return "Temporary error in sending the message"; 43 | case ERR_CONFIG: return "Could not read config files"; 44 | case ERR_MSG_REFUSED: return "Server refused the message"; 45 | case ERR_MSG_PERMFAIL: return "Permanent error in sending the message"; 46 | case ERR_BIND_FAILED: return "Failed to bind source address"; 47 | case ERR_AUTH_FAILED: return "Failed to authenticate to server"; 48 | } 49 | return (code & ERR_PERMANENT_FLAG) 50 | ? "Unspecified permanent error" 51 | : "Unspecified temporary error"; 52 | } 53 | -------------------------------------------------------------------------------- /lib/errcodes.h: -------------------------------------------------------------------------------- 1 | #ifndef NULLMAILER__ERRCODES__H__ 2 | #define NULLMAILER__ERRCODES__H__ 3 | 4 | // Temporary errors 5 | #define ERR_USAGE 1 // Invalid command-line arguments 6 | #define ERR_HOST_NOT_FOUND 2 // gethostbyname failed with HOST_NOT_FOUND 7 | #define ERR_NO_ADDRESS 3 // gethostbyname failed with NO_ADDRESS 8 | #define ERR_GHBN_TEMP 5 // gethostbyname failed with TRY_AGAIN 9 | #define ERR_SOCKET 6 // socket failed 10 | #define ERR_CONN_REFUSED 7 // connect failed with ECONNREFUSED 11 | #define ERR_CONN_TIMEDOUT 8 // connect failed with ETIMEDOUT 12 | #define ERR_CONN_UNREACHABLE 9 // connect failed with ENETUNREACH 13 | #define ERR_CONN_FAILED 10 // connect failed 14 | #define ERR_PROTO 11 // unexpected result from server 15 | #define ERR_MSG_OPEN 12 // could not open the message 16 | #define ERR_MSG_READ 13 // reading the message failed 17 | #define ERR_MSG_WRITE 14 // writing the message failed 18 | #define ERR_EXEC_FAILED 15 // executing a program failed 19 | #define ERR_MSG_TEMPFAIL 16 // server temporarily failed to receive 20 | #define ERR_UNKNOWN 17 // Arbitrary error code 21 | #define ERR_CONFIG 18 // Error reading a config file 22 | #define ERR_BIND_FAILED 19 // Failed to bind source address 23 | #define ERR_AUTH_FAILED 20 // Failed to authenticate to server 24 | 25 | // Permanent errors 26 | #define ERR_GHBN_FATAL 33 // gethostbyname failed with NO_RECOVERY 27 | #define ERR_MSG_REFUSED 34 // server refused the message 28 | #define ERR_MSG_PERMFAIL 35 // server permanently failed to receive 29 | 30 | #define ERR_PERMANENT_FLAG 32 31 | 32 | extern const char* errorstr(int); 33 | 34 | #endif // NULLMAILER__ERRCODES__H__ 35 | -------------------------------------------------------------------------------- /lib/fdbuf/Makefile.am: -------------------------------------------------------------------------------- 1 | noinst_LIBRARIES = libfdbuf.a 2 | AM_CPPFLAGS = -I$(top_srcdir)/lib 3 | 4 | if FDBUF_NO_MYSTRING 5 | mystring_sources = 6 | else 7 | mystring_sources = fdibuf_mystring.cc fdibuf_netstring.cc 8 | endif 9 | 10 | if TLS 11 | tls_sources = tlsibuf.h tlsibuf.cc tlsobuf.h tlsobuf.cc 12 | else 13 | tls_sources = 14 | endif 15 | 16 | libfdbuf_a_SOURCES = \ 17 | fdbuf.h \ 18 | fdbuf.cc \ 19 | fdbuf_copy.cc \ 20 | fdibuf.h \ 21 | fdibuf.cc \ 22 | fdobuf.h \ 23 | fdobuf.cc \ 24 | fdobuf_chownmod.cc \ 25 | fdobuf_seek.cc \ 26 | fdobuf_signed.cc \ 27 | fdobuf_unsigned.cc \ 28 | $(tls_sources) \ 29 | $(mystring_sources) 30 | -------------------------------------------------------------------------------- /lib/fdbuf/fdbuf.cc: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2018 Bruce Guenter 2 | // 3 | // This program is free software; you can redistribute it and/or modify 4 | // it under the terms of the GNU General Public License as published by 5 | // the Free Software Foundation; either version 2 of the License, or 6 | // (at your option) any later version. 7 | // 8 | // This program is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with this program; if not, write to the Free Software 15 | // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 16 | 17 | #include "fdbuf.h" 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | /////////////////////////////////////////////////////////////////////////////// 24 | // Class fdbuf 25 | /////////////////////////////////////////////////////////////////////////////// 26 | fdbuf::fdbuf(int fdesc, bool dc, unsigned bufsz) 27 | : buf(new char[bufsz]), 28 | buflength(0), 29 | bufstart(0), 30 | offset(0), 31 | errnum(0), 32 | flags(0), 33 | bufsize(bufsz), 34 | fd(fdesc), 35 | do_close(dc) 36 | { 37 | if(!buf) { 38 | flags = flag_error; 39 | errnum = errno; 40 | } 41 | if(fdesc < 0) 42 | flags |= flag_closed; 43 | #ifdef _REENTRANT 44 | pthread_mutex_t tmp = PTHREAD_MUTEX_INITIALIZER; 45 | mutex = tmp; 46 | pthread_mutex_init(&mutex, 0); 47 | #else 48 | #ifdef FDBUF_MUTEX_DEBUG 49 | mutex_count = 0; 50 | #endif 51 | #endif 52 | } 53 | 54 | fdbuf::~fdbuf() 55 | { 56 | close(); 57 | #ifdef _REENTRANT 58 | pthread_mutex_destroy(&mutex); 59 | #endif 60 | delete[] buf; 61 | } 62 | 63 | bool fdbuf::error() const 64 | { 65 | return flags & flag_error; 66 | } 67 | 68 | bool fdbuf::closed() const 69 | { 70 | return flags & flag_closed; 71 | } 72 | 73 | bool fdbuf::close() 74 | { 75 | if(do_close && fd >= 0 && !(flags & flag_closed)) { 76 | if(::close(fd) == -1) { 77 | errnum = errno; 78 | flags |= flag_error; 79 | return false; 80 | } 81 | flags |= flag_closed; 82 | } 83 | return true; 84 | } 85 | 86 | #if defined(FDBUF_MUTEX_DEBUG) && !defined(_REENTRANT) 87 | { 88 | int* null = 0; 89 | (*null)++; 90 | kill(getpid(), 9); 91 | } 92 | 93 | // Debugging code 94 | void fdbuf::lock() 95 | { 96 | if(mutex) 97 | abort(); 98 | ++mutex; 99 | } 100 | 101 | void fdbuf::unlock() 102 | { 103 | if(mutex != 1) 104 | abort(); 105 | --mutex; 106 | } 107 | #endif 108 | -------------------------------------------------------------------------------- /lib/fdbuf/fdbuf.h: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2018 Bruce Guenter 2 | // 3 | // This program is free software; you can redistribute it and/or modify 4 | // it under the terms of the GNU General Public License as published by 5 | // the Free Software Foundation; either version 2 of the License, or 6 | // (at your option) any later version. 7 | // 8 | // This program is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with this program; if not, write to the Free Software 15 | // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 16 | 17 | #ifndef FDBUF__H__ 18 | #define FDBUF__H__ 19 | 20 | #include "config.h" 21 | #include 22 | #include 23 | #include 24 | 25 | #ifdef _REENTRANT 26 | #include 27 | #endif 28 | 29 | #ifndef FDBUF_SIZE 30 | #define FDBUF_SIZE 4096 31 | #endif 32 | 33 | class mystring; 34 | 35 | class fdbuf 36 | { 37 | public: 38 | enum flagbits { flag_eof=1, flag_error=2, flag_closed=4 }; 39 | 40 | fdbuf(int fdesc, bool dc, unsigned bufsz = FDBUF_SIZE); 41 | ~fdbuf(); 42 | bool error() const; 43 | bool closed() const; 44 | bool close(); 45 | #ifdef _REENTRANT 46 | void lock() { pthread_mutex_lock(&mutex); } 47 | void unlock() { pthread_mutex_unlock(&mutex); } 48 | #else 49 | #ifdef FDBUF_MUTEX_DEBUG 50 | void lock(); 51 | void unlock(); 52 | #else 53 | void lock() { } 54 | void unlock() { } 55 | #endif 56 | #endif 57 | protected: 58 | char* const buf; 59 | unsigned buflength; // Length of the data in the buffer 60 | unsigned bufstart; // Start of the data in the buffer 61 | unsigned offset; // Current file read/write offset 62 | int errnum; // Saved error flag 63 | unsigned flags; // Status flags 64 | 65 | const unsigned bufsize; // Total buffer size 66 | const int fd; 67 | const bool do_close; // True to close on destructor 68 | 69 | #ifdef _REENTRANT 70 | pthread_mutex_t mutex; 71 | #else 72 | #ifdef FDBUF_MUTEX_DEBUG 73 | unsigned mutex; 74 | #endif 75 | #endif 76 | }; 77 | 78 | bool fdbuf_copy(class fdibuf&, class fdobuf&, bool noflush = false); 79 | 80 | #include "fdbuf/fdibuf.h" 81 | #include "fdbuf/fdobuf.h" 82 | 83 | #endif // FDBUF__H__ 84 | -------------------------------------------------------------------------------- /lib/fdbuf/fdbuf_copy.cc: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2018 Bruce Guenter 2 | // 3 | // This program is free software; you can redistribute it and/or modify 4 | // it under the terms of the GNU General Public License as published by 5 | // the Free Software Foundation; either version 2 of the License, or 6 | // (at your option) any later version. 7 | // 8 | // This program is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with this program; if not, write to the Free Software 15 | // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 16 | 17 | #include "fdbuf.h" 18 | 19 | /////////////////////////////////////////////////////////////////////////////// 20 | // Other routines 21 | /////////////////////////////////////////////////////////////////////////////// 22 | bool fdbuf_copy(fdibuf& in, fdobuf& out, bool noflush) 23 | { 24 | if(in.eof()) 25 | return true; 26 | if(!in || !out) 27 | return false; 28 | do { 29 | char buf[FDBUF_SIZE]; 30 | if(!in.read(buf, FDBUF_SIZE) && in.last_count() == 0) 31 | break; 32 | if(!out.write(buf, in.last_count()) && out.last_count() < in.last_count()) 33 | return false; 34 | } while(!in.eof()); 35 | if(!noflush && !out.flush()) 36 | return false; 37 | return in.eof(); 38 | } 39 | -------------------------------------------------------------------------------- /lib/fdbuf/fdbuf_test.cc: -------------------------------------------------------------------------------- 1 | #include "fdbuf.h" 2 | 3 | int main() 4 | { 5 | fdibuf in("testfile"); 6 | char buf[17]; buf[16] = 0; 7 | 8 | in.read(buf, 16); fout.write(buf); fout.flush(); 9 | 10 | in.seek(1024); 11 | in.read(buf, 16); fout.write(buf); fout.flush(); 12 | 13 | in.seek(8192+16); 14 | in.read(buf, 16); fout.write(buf); fout.flush(); 15 | return 0; 16 | } 17 | -------------------------------------------------------------------------------- /lib/fdbuf/fdibuf.cc: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2018 Bruce Guenter 2 | // 3 | // This program is free software; you can redistribute it and/or modify 4 | // it under the terms of the GNU General Public License as published by 5 | // the Free Software Foundation; either version 2 of the License, or 6 | // (at your option) any later version. 7 | // 8 | // This program is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with this program; if not, write to the Free Software 15 | // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 16 | 17 | #include "fdbuf.h" 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | /////////////////////////////////////////////////////////////////////////////// 24 | // Class fdibuf 25 | /////////////////////////////////////////////////////////////////////////////// 26 | fdibuf::fdibuf(int fdesc, bool dc, unsigned bufsz) 27 | : fdbuf(fdesc, dc, bufsz) 28 | { 29 | } 30 | 31 | fdibuf::fdibuf(const char* filename, unsigned bufsz) 32 | : fdbuf(open(filename, O_RDONLY), true, bufsz) 33 | { 34 | if(fd == -1) { 35 | flags = flag_error; 36 | errnum = errno; 37 | } 38 | } 39 | 40 | fdibuf::~fdibuf() 41 | { 42 | } 43 | 44 | bool fdibuf::eof() const 45 | { 46 | return (flags & flag_eof) && (bufstart >= buflength); 47 | } 48 | 49 | bool fdibuf::operator!() const 50 | { 51 | return eof() || error() || closed(); 52 | } 53 | 54 | // refill is protected -- no locking 55 | bool fdibuf::refill() 56 | { 57 | if(flags) 58 | return false; 59 | if(bufstart != 0) { 60 | if(bufstart < buflength) { 61 | buflength -= bufstart; 62 | memcpy(buf, buf+bufstart, buflength); 63 | } else 64 | buflength = 0; 65 | bufstart = 0; 66 | } 67 | unsigned oldbuflength = buflength; 68 | if(buflength < bufsize) { 69 | ssize_t red = _read(buf+buflength, bufsize-buflength); 70 | if(red < 0) { 71 | errnum = errno; 72 | flags |= flag_error; 73 | } 74 | else if(red == 0) 75 | flags |= flag_eof; 76 | else { 77 | buflength += red; 78 | offset += red; 79 | } 80 | } 81 | return buflength > oldbuflength; 82 | } 83 | 84 | bool fdibuf::get(char& ch) 85 | { 86 | lock(); 87 | count = 0; 88 | if(bufstart >= buflength) 89 | refill(); 90 | bool r = true; 91 | if(eof() || error()) 92 | r = false; 93 | else { 94 | ch = buf[bufstart++]; 95 | count = 1; 96 | } 97 | unlock(); 98 | return r; 99 | } 100 | 101 | bool fdibuf::read_large(char* data, unsigned datalen) 102 | { 103 | lock(); 104 | count = 0; 105 | 106 | // If there's any content in the buffer, memcpy it out first. 107 | unsigned len = buflength - bufstart; 108 | if(len > datalen) 109 | len = datalen; 110 | memcpy(data, buf+bufstart, len); 111 | data += len; 112 | datalen -= len; 113 | bufstart += len; 114 | count += len; 115 | 116 | // After the buffer is empty and there's still data to read, 117 | // read it straight from the fd instead of copying it through the buffer. 118 | while(datalen > 0) { 119 | ssize_t red = _read(data, datalen); 120 | if(red < 0) { 121 | errnum = errno; 122 | flags |= flag_error; 123 | break; 124 | } 125 | else if(red == 0) { 126 | flags |= flag_eof; 127 | break; 128 | } 129 | data += red; 130 | datalen -= red; 131 | offset += red; 132 | count += red; 133 | } 134 | unlock(); 135 | return datalen == 0; 136 | } 137 | 138 | bool fdibuf::read(char* data, unsigned datalen) 139 | { 140 | if(datalen >= bufsize) 141 | return read_large(data, datalen); 142 | lock(); 143 | count = 0; 144 | char* ptr = data; 145 | while(datalen && !eof()) { 146 | if(bufstart >= buflength) 147 | refill(); 148 | unsigned len = buflength-bufstart; 149 | if(len > datalen) 150 | len = datalen; 151 | memcpy(ptr, buf+bufstart, len); 152 | bufstart += len; 153 | datalen -= len; 154 | ptr += len; 155 | count += len; 156 | } 157 | unlock(); 158 | return !datalen; 159 | } 160 | 161 | bool fdibuf::seek(unsigned o) 162 | { 163 | lock(); 164 | unsigned buf_start = offset - buflength; 165 | if(o >= buf_start && o < offset) { 166 | bufstart = o - buf_start; 167 | } 168 | else { 169 | if(lseek(fd, o, SEEK_SET) != (off_t)o) { 170 | errnum = errno; 171 | flags |= flag_error; 172 | unlock(); 173 | return false; 174 | } 175 | offset = o; 176 | buflength = bufstart = 0; 177 | } 178 | count = 0; 179 | flags &= ~flag_eof; 180 | unlock(); 181 | return true; 182 | } 183 | 184 | bool fdibuf::seekfwd(unsigned o) 185 | { 186 | return seek(tell() + o); 187 | } 188 | 189 | ssize_t fdibuf::_read(char* buf, ssize_t len) 190 | { 191 | return ::read(fd, buf, len); 192 | } 193 | 194 | /////////////////////////////////////////////////////////////////////////////// 195 | // Globals 196 | /////////////////////////////////////////////////////////////////////////////// 197 | fdibuf fin(0); 198 | -------------------------------------------------------------------------------- /lib/fdbuf/fdibuf.h: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2018 Bruce Guenter 2 | // 3 | // This program is free software; you can redistribute it and/or modify 4 | // it under the terms of the GNU General Public License as published by 5 | // the Free Software Foundation; either version 2 of the License, or 6 | // (at your option) any later version. 7 | // 8 | // This program is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with this program; if not, write to the Free Software 15 | // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 16 | 17 | #ifndef FDBUF__FDIBUF__H__ 18 | #define FDBUF__FDIBUF__H__ 19 | 20 | #include "fdbuf.h" 21 | 22 | class fdibuf : protected fdbuf 23 | { 24 | public: 25 | fdibuf(const char* filename, unsigned bufsz = FDBUF_SIZE); 26 | fdibuf(int fdesc, bool dc = false, unsigned bufsz = FDBUF_SIZE); 27 | virtual ~fdibuf(); 28 | bool close() { lock(); bool r = fdbuf::close(); unlock(); return r; } 29 | bool eof() const; 30 | bool operator!() const ; 31 | operator bool() const { return !operator!(); } 32 | virtual bool get(char& ch); 33 | virtual bool getline(mystring& out, char terminator = '\n'); 34 | virtual bool getnetstring(mystring& out); 35 | virtual bool read(char*, unsigned); 36 | virtual bool read_large(char*, unsigned); 37 | bool read(unsigned char* b, unsigned l) { return read((char*)b, l); } 38 | bool read(signed char* b, unsigned l) { return read((char*)b, l); } 39 | unsigned last_count() { return count; } 40 | bool seek(unsigned o); 41 | bool seekfwd(unsigned o); 42 | bool rewind() { return seek(0); } 43 | unsigned tell() const { return offset-buflength+bufstart; } 44 | int error_number() const { return errnum; } 45 | protected: 46 | unsigned count; // Number of bytes read by last operation 47 | bool refill(); 48 | virtual ssize_t _read(char*, ssize_t); 49 | }; 50 | 51 | extern fdibuf fin; 52 | 53 | #endif // FDBUF__FDIBUF__H__ 54 | -------------------------------------------------------------------------------- /lib/fdbuf/fdibuf_mystring.cc: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2018 Bruce Guenter 2 | // 3 | // This program is free software; you can redistribute it and/or modify 4 | // it under the terms of the GNU General Public License as published by 5 | // the Free Software Foundation; either version 2 of the License, or 6 | // (at your option) any later version. 7 | // 8 | // This program is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with this program; if not, write to the Free Software 15 | // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 16 | 17 | #include 18 | #include "fdbuf.h" 19 | #include "mystring/mystring.h" 20 | 21 | bool fdibuf::getline(mystring& out, char terminator) 22 | { 23 | lock(); 24 | count = 0; 25 | if(bufstart >= buflength) 26 | refill(); 27 | if(eof() || error()) { 28 | unlock(); 29 | return false; 30 | } 31 | out = ""; 32 | while(!eof() && !error()) { 33 | char* ptr = buf+bufstart; 34 | unsigned bufleft = buflength - bufstart; 35 | const char* end = (const char*)memchr(ptr, terminator, bufleft); 36 | if(!end) { 37 | out += mystring(ptr, bufleft); 38 | bufstart = buflength; 39 | count += bufleft; 40 | refill(); 41 | } 42 | else { 43 | unsigned copylen = end - ptr; 44 | out += mystring(ptr, copylen); 45 | bufstart += copylen+1; 46 | count += copylen+1; 47 | break; 48 | } 49 | } 50 | unlock(); 51 | return true; 52 | } 53 | -------------------------------------------------------------------------------- /lib/fdbuf/fdibuf_netstring.cc: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2018 Bruce Guenter 2 | // 3 | // This program is free software; you can redistribute it and/or modify 4 | // it under the terms of the GNU General Public License as published by 5 | // the Free Software Foundation; either version 2 of the License, or 6 | // (at your option) any later version. 7 | // 8 | // This program is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with this program; if not, write to the Free Software 15 | // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 16 | 17 | #include 18 | #include "fdbuf.h" 19 | #include "mystring/mystring.h" 20 | 21 | bool fdibuf::getnetstring(mystring& out) 22 | { 23 | // Read in the size 24 | char ch; 25 | unsigned long size = 0; 26 | for(;;) { 27 | if(!get(ch)) 28 | return false; 29 | if(ch == ':') 30 | break; 31 | else if(ch >= '0' && ch <= '9') 32 | size = size*10 + (ch-'0'); 33 | else 34 | return false; 35 | } 36 | char tmp[size]; 37 | if(!read(tmp, size) || !get(ch) || ch != ',') 38 | return false; 39 | out = mystring(tmp, size); 40 | return true; 41 | } 42 | -------------------------------------------------------------------------------- /lib/fdbuf/fdobuf.cc: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2018 Bruce Guenter 2 | // 3 | // This program is free software; you can redistribute it and/or modify 4 | // it under the terms of the GNU General Public License as published by 5 | // the Free Software Foundation; either version 2 of the License, or 6 | // (at your option) any later version. 7 | // 8 | // This program is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with this program; if not, write to the Free Software 15 | // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 16 | 17 | #include "fdbuf.h" 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | /////////////////////////////////////////////////////////////////////////////// 24 | // Globals 25 | /////////////////////////////////////////////////////////////////////////////// 26 | fdobuf fout(1); 27 | fdobuf ferr(2); 28 | 29 | /////////////////////////////////////////////////////////////////////////////// 30 | // Class fdobuf 31 | /////////////////////////////////////////////////////////////////////////////// 32 | fdobuf::fdobuf(int fdesc, bool dc, unsigned bufsz) 33 | : fdbuf(fdesc, dc, bufsz), 34 | bufpos(0) 35 | { 36 | } 37 | 38 | fdobuf::fdobuf(const char* filename, int f, int mode, unsigned bufsz) 39 | : fdbuf(open(filename, O_WRONLY | f, mode), true, bufsz), 40 | bufpos(0) 41 | { 42 | if(fd == -1) { 43 | flags = flag_error; 44 | errnum = errno; 45 | } 46 | } 47 | 48 | fdobuf::~fdobuf() 49 | { 50 | flush(); 51 | } 52 | 53 | bool fdobuf::close() 54 | { 55 | if(!flush()) 56 | return false; 57 | lock(); 58 | bool r = fdbuf::close(); 59 | unlock(); 60 | return r; 61 | } 62 | 63 | bool fdobuf::operator!() const 64 | { 65 | return error() || closed(); 66 | } 67 | 68 | bool fdobuf::nflush(bool withsync) 69 | { 70 | if(flags) 71 | return false; 72 | while(bufstart < buflength) { 73 | ssize_t written = _write(buf+bufstart, buflength-bufstart); 74 | if(written < 0) { 75 | flags |= flag_error; 76 | errnum = errno; 77 | return false; 78 | } 79 | else { 80 | bufstart += written; 81 | offset += written; 82 | } 83 | } 84 | buflength = 0; 85 | bufstart = 0; 86 | bufpos = 0; 87 | if(withsync && (fsync(fd) == -1)) { 88 | flags |= flag_error; 89 | errnum = errno; 90 | return false; 91 | } 92 | return true; 93 | } 94 | 95 | bool fdobuf::flush() 96 | { 97 | lock(); 98 | bool r = nflush(false); 99 | unlock(); 100 | return r; 101 | } 102 | 103 | bool fdobuf::sync() 104 | { 105 | lock(); 106 | bool r = nflush(true); 107 | unlock(); 108 | return r; 109 | } 110 | 111 | bool fdobuf::write(char ch) 112 | { 113 | if(flags) 114 | return false; 115 | 116 | lock(); 117 | count = 0; 118 | buf[bufpos++] = ch; 119 | //if(buflength >= bufsize && !nflush(false)) { 120 | // unlock(); 121 | // return false; 122 | //} 123 | if(bufpos >= buflength) 124 | buflength = bufpos; 125 | if(buflength >= bufsize && !nflush(false)) { 126 | unlock(); 127 | return false; 128 | } 129 | count = 1; 130 | unlock(); 131 | return true; 132 | } 133 | 134 | bool fdobuf::write_large(const char* data, unsigned datalen) 135 | { 136 | if(flags) 137 | return false; 138 | 139 | lock(); 140 | count = 0; 141 | 142 | if(!nflush(false)) { 143 | unlock(); 144 | return false; 145 | } 146 | 147 | while(datalen > 0) { 148 | ssize_t written = _write(data, datalen); 149 | if(written < 0) { 150 | flags |= flag_error; 151 | errnum = errno; 152 | unlock(); 153 | return false; 154 | } 155 | datalen -= written; 156 | data += written; 157 | offset += written; 158 | count += written; 159 | } 160 | unlock(); 161 | return true; 162 | } 163 | 164 | bool fdobuf::write(const char* data, unsigned datalen) 165 | { 166 | if(datalen >= bufsize) 167 | return write_large(data, datalen); 168 | 169 | if(flags) 170 | return false; 171 | 172 | lock(); 173 | const char* ptr = data; 174 | count = 0; 175 | // Amount is the number of bytes available in the buffer 176 | unsigned amount = bufsize-bufpos; 177 | while(datalen >= amount) { 178 | // If we get here, this copy will completely fill the buffer, 179 | // requiring a flush 180 | memcpy(buf+bufpos, ptr, amount); 181 | bufpos = bufsize; 182 | buflength = bufsize; 183 | datalen -= amount; 184 | ptr += amount; 185 | if(!nflush(false)) { 186 | unlock(); 187 | return false; 188 | } 189 | count += amount; 190 | amount = bufsize-bufpos; 191 | } 192 | // At this point, the remaining data will fit into the buffer 193 | memcpy(buf+bufpos, ptr, datalen); 194 | count += datalen; 195 | bufpos += datalen; 196 | if(bufpos > buflength) buflength = bufpos; 197 | unlock(); 198 | return true; 199 | } 200 | 201 | ssize_t fdobuf::_write(const char* buf, ssize_t len) 202 | { 203 | return ::write(fd, buf, len); 204 | } 205 | 206 | /////////////////////////////////////////////////////////////////////////////// 207 | // Manipulators 208 | /////////////////////////////////////////////////////////////////////////////// 209 | fdobuf& endl(fdobuf& fd) 210 | { 211 | fd.write("\n", 1); 212 | fd.flush(); 213 | return fd; 214 | } 215 | -------------------------------------------------------------------------------- /lib/fdbuf/fdobuf.h: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2018 Bruce Guenter 2 | // 3 | // This program is free software; you can redistribute it and/or modify 4 | // it under the terms of the GNU General Public License as published by 5 | // the Free Software Foundation; either version 2 of the License, or 6 | // (at your option) any later version. 7 | // 8 | // This program is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with this program; if not, write to the Free Software 15 | // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 16 | 17 | #ifndef FDBUF__FDOBUF__H__ 18 | #define FDBUF__FDOBUF__H__ 19 | 20 | #include "fdbuf.h" 21 | 22 | class fdobuf : protected fdbuf 23 | { 24 | public: 25 | enum openflags { create=O_CREAT, 26 | excl=O_EXCL, 27 | trunc=O_TRUNC, 28 | append=O_APPEND }; 29 | 30 | fdobuf(const char* filename, int, int mode = 0666, 31 | unsigned bufsz = FDBUF_SIZE); 32 | fdobuf(int fdesc, bool dc=false, unsigned bufsz = FDBUF_SIZE); 33 | virtual ~fdobuf(); 34 | bool close(); 35 | bool operator!() const; 36 | operator bool() const 37 | { 38 | return !operator!(); 39 | } 40 | bool flush(); 41 | bool sync(); 42 | virtual bool write(char); 43 | bool write(unsigned char c) { return write((char)c); } 44 | bool write(signed char c) { return write((char)c); } 45 | virtual bool write(const char*, unsigned); 46 | bool write(const unsigned char* b, unsigned l) { return write((char*)b, l); } 47 | bool write(const signed char* b, unsigned l) { return write((char*)b, l); } 48 | virtual bool write_large(const char*, unsigned); 49 | unsigned last_count() { return count; } 50 | bool seek(unsigned o); 51 | bool rewind() { return seek(0); } 52 | unsigned tell() const { return offset + bufpos; } 53 | 54 | bool chown(uid_t, gid_t) const; 55 | bool chmod(mode_t) const; 56 | 57 | fdobuf& operator<<(const char* str) 58 | { 59 | write(str, strlen(str)); 60 | return *this; 61 | } 62 | fdobuf& operator<<(char ch) 63 | { 64 | write(ch); 65 | return *this; 66 | } 67 | fdobuf& operator<<(fdobuf& (*manip)(fdobuf&)) 68 | { 69 | return manip(*this); 70 | } 71 | fdobuf& operator<<(unsigned long); 72 | fdobuf& operator<<(signed long); 73 | fdobuf& operator<<(unsigned i) { return operator<<((unsigned long)i); } 74 | fdobuf& operator<<(signed i) { return operator<<((signed long)i); } 75 | fdobuf& operator<<(unsigned short i) { return operator<<((unsigned long)i); } 76 | fdobuf& operator<<(signed short i) { return operator<<((signed long)i); } 77 | 78 | int error_number() const { return errnum; } 79 | protected: 80 | virtual bool nflush(bool withsync); 81 | virtual ssize_t _write(const char* buf, ssize_t len); 82 | 83 | unsigned bufpos; // Current write position in the buffer 84 | unsigned count; // Number of bytes written by last operation 85 | }; 86 | 87 | fdobuf& endl(fdobuf& fd); 88 | 89 | extern fdobuf fout; 90 | extern fdobuf ferr; 91 | 92 | #endif // FDBUF__FDOBUF__H__ 93 | -------------------------------------------------------------------------------- /lib/fdbuf/fdobuf_chownmod.cc: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2018 Bruce Guenter 2 | // 3 | // This program is free software; you can redistribute it and/or modify 4 | // it under the terms of the GNU General Public License as published by 5 | // the Free Software Foundation; either version 2 of the License, or 6 | // (at your option) any later version. 7 | // 8 | // This program is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with this program; if not, write to the Free Software 15 | // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 16 | 17 | #include 18 | #include "fdbuf.h" 19 | #include 20 | #include 21 | 22 | bool fdobuf::chown(uid_t uid, gid_t gid) const 23 | { 24 | if(error()) 25 | return false; 26 | return fchown(fd, uid, gid) != -1; 27 | } 28 | 29 | bool fdobuf::chmod(mode_t mode) const 30 | { 31 | if(error()) 32 | return false; 33 | return fchmod(fd, mode) != -1; 34 | } 35 | -------------------------------------------------------------------------------- /lib/fdbuf/fdobuf_seek.cc: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2018 Bruce Guenter 2 | // 3 | // This program is free software; you can redistribute it and/or modify 4 | // it under the terms of the GNU General Public License as published by 5 | // the Free Software Foundation; either version 2 of the License, or 6 | // (at your option) any later version. 7 | // 8 | // This program is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with this program; if not, write to the Free Software 15 | // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 16 | 17 | #include "fdbuf.h" 18 | #include 19 | #include 20 | 21 | bool fdobuf::seek(unsigned o) 22 | { 23 | if(flags) 24 | return false; 25 | 26 | lock(); 27 | unsigned buf_start = offset; 28 | unsigned buf_end = offset + buflength; 29 | if(o >= buf_start && o < buf_end) { 30 | bufpos = o - buf_start; 31 | } 32 | else { 33 | if(!nflush(false)) { 34 | unlock(); 35 | return false; 36 | } 37 | if(lseek(fd, o, SEEK_SET) != (off_t)o) { 38 | errnum = errno; 39 | flags |= flag_error; 40 | unlock(); 41 | return false; 42 | } 43 | offset = o; 44 | } 45 | count = 0; 46 | unlock(); 47 | return true; 48 | } 49 | -------------------------------------------------------------------------------- /lib/fdbuf/fdobuf_signed.cc: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2018 Bruce Guenter 2 | // 3 | // This program is free software; you can redistribute it and/or modify 4 | // it under the terms of the GNU General Public License as published by 5 | // the Free Software Foundation; either version 2 of the License, or 6 | // (at your option) any later version. 7 | // 8 | // This program is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with this program; if not, write to the Free Software 15 | // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 16 | 17 | #include "fdbuf.h" 18 | #include 19 | 20 | #define MAXSTRLEN ((sizeof(signed long)*CHAR_BIT)/3) 21 | 22 | fdobuf& fdobuf::operator<<(signed long i) 23 | { 24 | if(i == 0) 25 | return operator<<('0'); 26 | if(i < 0) { 27 | operator<<('-'); 28 | i = -i; 29 | } 30 | char buf[MAXSTRLEN+1]; 31 | char* ptr = buf+MAXSTRLEN; 32 | *ptr-- = 0; 33 | while(i) { 34 | *ptr-- = i % 10 + '0'; 35 | i /= 10; 36 | } 37 | return operator<<(ptr+1); 38 | } 39 | -------------------------------------------------------------------------------- /lib/fdbuf/fdobuf_unsigned.cc: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2018 Bruce Guenter 2 | // 3 | // This program is free software; you can redistribute it and/or modify 4 | // it under the terms of the GNU General Public License as published by 5 | // the Free Software Foundation; either version 2 of the License, or 6 | // (at your option) any later version. 7 | // 8 | // This program is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with this program; if not, write to the Free Software 15 | // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 16 | 17 | #include "fdbuf.h" 18 | #include 19 | 20 | #define MAXSTRLEN ((sizeof(signed long)*CHAR_BIT)/3) 21 | 22 | fdobuf& fdobuf::operator<<(unsigned long i) 23 | { 24 | if(i == 0) 25 | return operator<<('0'); 26 | char buf[MAXSTRLEN+1]; 27 | char* ptr = buf+MAXSTRLEN; 28 | *ptr-- = 0; 29 | while(i) { 30 | *ptr-- = i % 10 + '0'; 31 | i /= 10; 32 | } 33 | return operator<<(ptr+1); 34 | } 35 | -------------------------------------------------------------------------------- /lib/fdbuf/tlsibuf.cc: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2018 Bruce Guenter 2 | // 3 | // This program is free software; you can redistribute it and/or modify 4 | // it under the terms of the GNU General Public License as published by 5 | // the Free Software Foundation; either version 2 of the License, or 6 | // (at your option) any later version. 7 | // 8 | // This program is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with this program; if not, write to the Free Software 15 | // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 16 | 17 | #include "tlsibuf.h" 18 | 19 | /////////////////////////////////////////////////////////////////////////////// 20 | // Class tlsibuf 21 | /////////////////////////////////////////////////////////////////////////////// 22 | tlsibuf::tlsibuf(gnutls_session_t s, unsigned bufsz) 23 | : fdibuf(-1, false, bufsz), session(s) 24 | { 25 | flags &= ~flag_closed; 26 | } 27 | 28 | ssize_t tlsibuf::_read(char* buf, ssize_t len) 29 | { 30 | int rc; 31 | do 32 | { 33 | rc = gnutls_record_recv(session, buf, len); 34 | } while (rc == GNUTLS_E_AGAIN); 35 | return rc; 36 | } 37 | -------------------------------------------------------------------------------- /lib/fdbuf/tlsibuf.h: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2018 Bruce Guenter 2 | // 3 | // This program is free software; you can redistribute it and/or modify 4 | // it under the terms of the GNU General Public License as published by 5 | // the Free Software Foundation; either version 2 of the License, or 6 | // (at your option) any later version. 7 | // 8 | // This program is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with this program; if not, write to the Free Software 15 | // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 16 | 17 | #ifndef FDBUF__TLSIBUF__H__ 18 | #define FDBUF__TLSIBUF__H__ 19 | 20 | #include "fdibuf.h" 21 | #include 22 | 23 | class tlsibuf : public fdibuf 24 | { 25 | public: 26 | tlsibuf(gnutls_session_t, unsigned bufsz = FDBUF_SIZE); 27 | protected: 28 | gnutls_session_t session; 29 | virtual ssize_t _read(char* buf, ssize_t len); 30 | }; 31 | 32 | #endif // FDBUF__TLSIBUF__H__ 33 | -------------------------------------------------------------------------------- /lib/fdbuf/tlsobuf.cc: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2018 Bruce Guenter 2 | // 3 | // This program is free software; you can redistribute it and/or modify 4 | // it under the terms of the GNU General Public License as published by 5 | // the Free Software Foundation; either version 2 of the License, or 6 | // (at your option) any later version. 7 | // 8 | // This program is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with this program; if not, write to the Free Software 15 | // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 16 | 17 | #include "tlsobuf.h" 18 | 19 | /////////////////////////////////////////////////////////////////////////////// 20 | // Class tlsobuf 21 | /////////////////////////////////////////////////////////////////////////////// 22 | tlsobuf::tlsobuf(gnutls_session_t s, unsigned bufsz) 23 | : fdobuf(-1, false, bufsz), session(s) 24 | { 25 | flags &= ~flag_closed; 26 | } 27 | 28 | ssize_t tlsobuf::_write(const char* buf, ssize_t len) 29 | { 30 | return gnutls_record_send(session, buf, len); 31 | } 32 | -------------------------------------------------------------------------------- /lib/fdbuf/tlsobuf.h: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2018 Bruce Guenter 2 | // 3 | // This program is free software; you can redistribute it and/or modify 4 | // it under the terms of the GNU General Public License as published by 5 | // the Free Software Foundation; either version 2 of the License, or 6 | // (at your option) any later version. 7 | // 8 | // This program is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with this program; if not, write to the Free Software 15 | // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 16 | 17 | #ifndef FDBUF__TLSOBUF__H__ 18 | #define FDBUF__TLSOBUF__H__ 19 | 20 | #include "fdobuf.h" 21 | #include 22 | 23 | class tlsobuf : public fdobuf 24 | { 25 | public: 26 | tlsobuf(gnutls_session_t, unsigned bufsz = FDBUF_SIZE); 27 | protected: 28 | gnutls_session_t session; 29 | virtual ssize_t _write(const char* buf, ssize_t len); 30 | }; 31 | 32 | #endif // FDBUF__TLSOBUF__H__ 33 | -------------------------------------------------------------------------------- /lib/forkexec.cc: -------------------------------------------------------------------------------- 1 | // nullmailer -- a simple relay-only MTA 2 | // Copyright (C) 2018 Bruce Guenter 3 | // 4 | // This program is free software; you can redistribute it and/or modify 5 | // it under the terms of the GNU General Public License as published by 6 | // the Free Software Foundation; either version 2 of the License, or 7 | // (at your option) any later version. 8 | // 9 | // This program is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU General Public License 15 | // along with this program; if not, write to the Free Software 16 | // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 17 | // 18 | // You can contact me at . There is also a mailing list 19 | // available to discuss this package. To subscribe, send an email to 20 | // . 21 | 22 | #include "config.h" 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include "cli++/cli++.h" 29 | #include "fdbuf/fdbuf.h" 30 | #include "mystring/mystring.h" 31 | #include "defines.h" 32 | #include "errcodes.h" 33 | #include "forkexec.h" 34 | 35 | #define ERR(MSG) do{ ferr << cli_program << ": " << MSG << ": " << strerror(errno) << endl; } while(0) 36 | #define FAIL(MSG) do{ ERR(MSG); return false; }while(0) 37 | 38 | static int fdnull = -1; 39 | 40 | fork_exec::fork_exec(const char* p) 41 | : pid(-1), name(p) 42 | { 43 | } 44 | 45 | fork_exec::~fork_exec() 46 | { 47 | wait(); 48 | } 49 | 50 | bool fork_exec::operator!() const 51 | { 52 | return pid < 0; 53 | } 54 | 55 | bool fork_exec::start(const char* args[], int redirn, int redirs[]) 56 | { 57 | autoclose_pipe pipes[redirn]; 58 | for (int i = 0; i < redirn; i++) { 59 | if (redirs[i] == REDIRECT_PIPE_TO || redirs[i] == REDIRECT_PIPE_FROM) 60 | if (!pipes[i].open()) 61 | FAIL("Could not create pipe"); 62 | if (redirs[i] == REDIRECT_NULL) 63 | if (fdnull < 0) 64 | if ((fdnull = open("/dev/null", O_RDWR)) < 0) 65 | FAIL("Could not open \"/dev/null\""); 66 | } 67 | if ((pid = fork()) < 0) 68 | FAIL("Could not fork"); 69 | if (pid == 0) { 70 | // Child process, exec program 71 | for (int i = 0; i < redirn; i++) { 72 | int r = redirs[i]; 73 | if (r == REDIRECT_NULL) 74 | dup2(fdnull, i); 75 | else if (r == REDIRECT_PIPE_FROM) { 76 | dup2(pipes[i][1], i); 77 | pipes[i].close(); 78 | } 79 | else if (r == REDIRECT_PIPE_TO) { 80 | dup2(pipes[i][0], i); 81 | pipes[i].close(); 82 | } 83 | else if (r > 0) { 84 | dup2(r, i); 85 | if (r >= redirn) 86 | close(r); 87 | } 88 | } 89 | execv(args[0], (char**)args); 90 | ERR("Could not exec " << name); 91 | _exit(ERR_EXEC_FAILED); 92 | } 93 | for (int i = 0; i < redirn; i++) { 94 | if (redirs[i] == REDIRECT_PIPE_TO) 95 | redirs[i] = pipes[i].extract(1); 96 | else if (redirs[i] == REDIRECT_PIPE_FROM) 97 | redirs[i] = pipes[i].extract(0); 98 | } 99 | return true; 100 | } 101 | 102 | bool fork_exec::start(const char* program, int redirn, int redirs[]) 103 | { 104 | const char* args[2] = { program, NULL }; 105 | return start(args, redirn, redirs); 106 | } 107 | 108 | int fork_exec::wait_status() 109 | { 110 | if (pid > 0) { 111 | int status; 112 | if (waitpid(pid, &status, 0) == pid) { 113 | pid = -1; 114 | return status; 115 | } 116 | } 117 | return -1; 118 | } 119 | 120 | bool fork_exec::wait() 121 | { 122 | if (pid > 0) { 123 | int status = wait_status(); 124 | if (status < 0) 125 | FAIL("Error catching the return value from " << name); 126 | if (WIFEXITED(status)) { 127 | status = WEXITSTATUS(status); 128 | if (status) { 129 | ferr << cli_program << ": " << name << " failed: " << status << endl; 130 | return false; 131 | } 132 | } 133 | else 134 | FAIL(name << " crashed or was killed"); 135 | } 136 | return true; 137 | } 138 | 139 | mystring program_path(const char* program) 140 | { 141 | return CONFIG_PATH(BIN, NULL, program); 142 | } 143 | 144 | static const char* nqpath() 145 | { 146 | static mystring cache; 147 | if (!cache) { 148 | const char* env; 149 | if ((env = getenv("NULLMAILER_QUEUE")) != NULL) 150 | cache = env; 151 | else 152 | cache = CONFIG_PATH(SBIN, NULL, "nullmailer-queue"); 153 | } 154 | return cache.c_str(); 155 | } 156 | 157 | queue_pipe::queue_pipe() 158 | : fork_exec("nullmailer-queue") 159 | { 160 | } 161 | 162 | int queue_pipe::start() 163 | { 164 | int redirs[] = { REDIRECT_PIPE_TO, REDIRECT_NULL, REDIRECT_NULL }; 165 | if (!fork_exec::start(nqpath(), 3, redirs)) 166 | return -1; 167 | return redirs[0]; 168 | } 169 | -------------------------------------------------------------------------------- /lib/forkexec.h: -------------------------------------------------------------------------------- 1 | #ifndef NULLMAILER__FORK_EXEC__H 2 | #define NULLMAILER__FORK_EXEC__H 3 | 4 | #include 5 | #include 6 | #include 7 | #include "mystring/mystring.h" 8 | #include "autoclose.h" 9 | #include "configio.h" 10 | 11 | mystring program_path(const char* program); 12 | 13 | #define REDIRECT_NONE -1 14 | #define REDIRECT_NULL -2 15 | #define REDIRECT_PIPE_FROM -3 16 | #define REDIRECT_PIPE_TO -4 17 | 18 | class fork_exec 19 | { 20 | private: 21 | pid_t pid; 22 | const char* name; 23 | 24 | public: 25 | fork_exec(const char*); 26 | ~fork_exec(); 27 | bool operator!() const; 28 | 29 | bool start(const char* args[], int redirn, int redirs[]); 30 | bool start(const char* program, int redirn, int redirs[]); 31 | bool wait(); 32 | int wait_status(); 33 | inline void kill(int sig) { ::kill(pid, sig); } 34 | }; 35 | 36 | class queue_pipe : public fork_exec 37 | { 38 | public: 39 | queue_pipe(); 40 | int start(); 41 | }; 42 | 43 | #endif 44 | -------------------------------------------------------------------------------- /lib/hostname.cc: -------------------------------------------------------------------------------- 1 | // nullmailer -- a simple relay-only MTA 2 | // Copyright (C) 2018 Bruce Guenter 3 | // 4 | // This program is free software; you can redistribute it and/or modify 5 | // it under the terms of the GNU General Public License as published by 6 | // the Free Software Foundation; either version 2 of the License, or 7 | // (at your option) any later version. 8 | // 9 | // This program is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU General Public License 15 | // along with this program; if not, write to the Free Software 16 | // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 17 | // 18 | // You can contact me at . There is also a mailing list 19 | // available to discuss this package. To subscribe, send an email to 20 | // . 21 | 22 | #include "config.h" 23 | #include "mystring/mystring.h" 24 | #include "configio.h" 25 | #include "hostname.h" 26 | #include "canonicalize.h" 27 | 28 | mystring me; 29 | mystring defaulthost; 30 | mystring defaultdomain; 31 | 32 | void read_hostnames() 33 | { 34 | int nome; 35 | nome = 0; 36 | if (!config_read("me", me)) { 37 | nome = 1; 38 | me = "me"; 39 | } 40 | if (!config_read("defaultdomain", defaultdomain)) 41 | defaultdomain = nome ? "defaultdomain" : me.c_str(); 42 | if (!config_read("defaulthost", defaulthost)) 43 | defaulthost = nome ? "defaulthost" : me.c_str(); 44 | canonicalize(defaulthost); 45 | } 46 | -------------------------------------------------------------------------------- /lib/hostname.h: -------------------------------------------------------------------------------- 1 | #ifndef NULLMAILER__HOSTNAME__H__ 2 | #define NULLMAILER__HOSTNAME__H__ 3 | 4 | extern mystring me; 5 | extern mystring defaulthost; 6 | extern mystring defaultdomain; 7 | 8 | void read_hostnames(); 9 | 10 | #endif 11 | -------------------------------------------------------------------------------- /lib/itoa.cc: -------------------------------------------------------------------------------- 1 | #include "itoa.h" 2 | 3 | const char *itoa(long v, int digits) 4 | { 5 | static char buf[INTLENGTH]; 6 | bool neg = false; 7 | if(v < 0) { 8 | v = -v; 9 | neg = true; 10 | } 11 | char* ptr = buf + INTLENGTH; 12 | *--ptr = '\0'; 13 | do { 14 | *--ptr = (v % 10) + '0'; 15 | v /= 10; 16 | --digits; 17 | } while(v != 0); 18 | while(digits > 0 && ptr > buf-1) 19 | *--ptr = '0', --digits; 20 | if(neg) 21 | *--ptr = '-'; 22 | return ptr; 23 | } 24 | -------------------------------------------------------------------------------- /lib/itoa.h: -------------------------------------------------------------------------------- 1 | #ifndef ITOA__H__ 2 | #define ITOA__H__ 3 | 4 | #ifndef INTLENGTH 5 | #define INTLENGTH 64 6 | /* 40 digits is long enough to handle unsigned 128-bit numbers */ 7 | #endif 8 | 9 | const char *itoa(long, int = 0); 10 | 11 | #endif 12 | -------------------------------------------------------------------------------- /lib/list.h: -------------------------------------------------------------------------------- 1 | #ifndef LIST__H__ 2 | #define LIST__H__ 3 | 4 | template struct list_node 5 | { 6 | list_node* next; 7 | T data; 8 | list_node(T d, list_node* n = 0) : next(n), data(d) { } 9 | ~list_node() { } 10 | }; 11 | 12 | template class list_iterator; 13 | template class const_list_iterator; 14 | 15 | template class list 16 | { 17 | public: 18 | typedef list_node node; 19 | typedef list_iterator iter; 20 | typedef const_list_iterator const_iter; 21 | friend class list_iterator; 22 | friend class const_list_iterator; 23 | 24 | list() 25 | : head(0), tail(0), cnt(0) 26 | { 27 | } 28 | list(const list&); 29 | 30 | ~list() 31 | { 32 | empty(); 33 | } 34 | 35 | unsigned count() const 36 | { 37 | return cnt; 38 | } 39 | 40 | void empty() 41 | { 42 | while(head) { 43 | node* next = head->next; 44 | delete head; 45 | head = next; 46 | } 47 | tail = 0; 48 | cnt = 0; 49 | } 50 | 51 | bool append(T data) 52 | { 53 | node* n = new node(data); 54 | if(tail) 55 | tail->next = n; 56 | else 57 | head = n; 58 | tail = n; 59 | ++cnt; 60 | return true; 61 | } 62 | bool prepend(T data) 63 | { 64 | head = new node(data, head); 65 | if(!tail) 66 | tail = head; 67 | ++cnt; 68 | return true; 69 | } 70 | bool remove(iter&); 71 | private: 72 | node* head; 73 | node* tail; 74 | unsigned cnt; 75 | }; 76 | 77 | template class const_list_iterator 78 | { 79 | friend class list; 80 | private: 81 | inline void go_next() 82 | { 83 | prev = curr; 84 | if(curr) 85 | curr = curr->next; 86 | } 87 | public: 88 | const_list_iterator(const list& l) 89 | : lst(l), prev(0), curr(l.head) 90 | { 91 | } 92 | void operator++() 93 | { 94 | go_next(); 95 | } 96 | void operator++(int) 97 | { 98 | go_next(); 99 | } 100 | T operator*() const 101 | { 102 | return curr->data; 103 | } 104 | bool operator!() const 105 | { 106 | return curr == 0; 107 | } 108 | operator bool() const 109 | { 110 | return !operator!(); 111 | } 112 | private: 113 | const list& lst; 114 | // g++ 3.2 insists 115 | const typename list::node* prev; 116 | const typename list::node* curr; 117 | }; 118 | 119 | template 120 | list::list(const list& that) 121 | : head(0), tail(0), cnt(0) 122 | { 123 | for(const_iter i = that; i; ++i) 124 | append(*i); 125 | } 126 | 127 | template class list_iterator 128 | { 129 | friend class list; 130 | private: 131 | inline void go_next() 132 | { 133 | prev = curr; 134 | if(curr) 135 | curr = curr->next; 136 | } 137 | public: 138 | list_iterator(list& l) 139 | : lst(l), prev(0), curr(l.head) 140 | { 141 | } 142 | void operator++() 143 | { 144 | go_next(); 145 | } 146 | void operator++(int) 147 | { 148 | go_next(); 149 | } 150 | T operator*() const 151 | { 152 | return curr->data; 153 | } 154 | T& operator*() 155 | { 156 | return curr->data; 157 | } 158 | bool operator!() const 159 | { 160 | return curr == 0; 161 | } 162 | operator bool() const 163 | { 164 | return !operator!(); 165 | } 166 | private: 167 | list& lst; 168 | typename list::node* prev; 169 | typename list::node* curr; 170 | }; 171 | 172 | template 173 | bool list::remove(list::iter& iter) 174 | { 175 | if(this != &iter.lst) 176 | return false; 177 | if(!iter.curr) 178 | return false; 179 | if(iter.prev) { 180 | iter.prev->next = iter.curr->next; 181 | if(iter.curr == tail) 182 | tail = iter.prev; 183 | delete iter.curr; 184 | iter.curr = iter.prev->next; 185 | } 186 | else { 187 | head = iter.curr->next; 188 | if(!head) 189 | tail = 0; 190 | delete iter.curr; 191 | iter.curr = head; 192 | } 193 | --cnt; 194 | return true; 195 | } 196 | 197 | #endif // LIST__H__ 198 | -------------------------------------------------------------------------------- /lib/listtest.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include "list.h" 3 | 4 | typedef list ilist; 5 | typedef ilist::iter iiter; 6 | 7 | void test_remove_first() 8 | { 9 | ilist l; 10 | l.append(1); 11 | l.append(2); 12 | iiter i(l); 13 | l.remove(i); 14 | if(!i) cout << "After removing first, iter no longer valid\n"; 15 | else if(*i != 2) cout << "After removing first, iter is wrong\n"; 16 | if(l.count() != 1) cout << "After removing first, count is wrong\n"; 17 | } 18 | 19 | void test_remove_mid() 20 | { 21 | ilist l; 22 | l.append(1); 23 | l.append(2); 24 | l.append(3); 25 | iiter i(l); 26 | i++; 27 | l.remove(i); 28 | if(!i) cout << "After removing middle, iter no longer valid\n"; 29 | else if(*i != 3) cout << "After removing middle, iter is wrong\n"; 30 | if(l.count() != 2) cout << "After removing middle, count is wrong\n"; 31 | } 32 | 33 | void test_remove_last() 34 | { 35 | ilist l; 36 | l.append(1); 37 | l.append(2); 38 | iiter i(l); 39 | i++; 40 | l.remove(i); 41 | if(i) cout << "After removing last, iter is still valid\n"; 42 | if(l.count() != 1) cout << "After removing last, count is wrong\n"; 43 | } 44 | 45 | int main() { 46 | test_remove_first(); 47 | test_remove_mid(); 48 | test_remove_last(); 49 | return 0; 50 | } 51 | -------------------------------------------------------------------------------- /lib/make_defines.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | ( 3 | echo "extern const char QUEUE_DIR[]=\"$1\";" 4 | echo "extern const char CONFIG_DIR[]=\"$2\";" 5 | echo "extern const char PROTOCOLS_DIR[]=\"$3\";" 6 | echo "extern const char BIN_DIR[]=\"$4\";" 7 | echo "extern const char SBIN_DIR[]=\"$5\";" 8 | ) > defines.cc 9 | -------------------------------------------------------------------------------- /lib/makefield.cc: -------------------------------------------------------------------------------- 1 | // nullmailer -- a simple relay-only MTA 2 | // Copyright (C) 2018 Bruce Guenter 3 | // 4 | // This program is free software; you can redistribute it and/or modify 5 | // it under the terms of the GNU General Public License as published by 6 | // the Free Software Foundation; either version 2 of the License, or 7 | // (at your option) any later version. 8 | // 9 | // This program is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU General Public License 15 | // along with this program; if not, write to the Free Software 16 | // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 17 | // 18 | // You can contact me at . There is also a mailing list 19 | // available to discuss this package. To subscribe, send an email to 20 | // . 21 | 22 | #include "config.h" 23 | #include "defines.h" 24 | #include 25 | #include "ac/time.h" 26 | #include "itoa.h" 27 | #include "mystring/mystring.h" 28 | 29 | mystring make_date(time_t t) 30 | { 31 | char buf[256]; 32 | if (t == 0) 33 | t = time(0); 34 | struct tm* l = localtime(&t); 35 | strftime(buf, 256, "%a, %d %b %Y %H:%M:%S ", l); 36 | #ifdef TM_HAS_GMTOFF 37 | long tznum = l->TM_HAS_GMTOFF/60; 38 | #else 39 | long tznum = -timezone/60; 40 | #ifdef TM_HAS_ISDST 41 | int daylight = l->TM_HAS_ISDST; 42 | #endif // TM_HAS_ISDST 43 | if(daylight) 44 | tznum += 60; 45 | #endif // TM_HAS_GMTOFF 46 | char tz[6]; 47 | tz[0] = '+'; 48 | if(tznum < 0) { 49 | tznum = -tznum; 50 | tz[0] = '-'; 51 | } 52 | long tzhours = tznum / 60; 53 | tz[1] = (tzhours/10)%10 + '0'; 54 | tz[2] = tzhours%10 + '0'; 55 | long tzmins = tznum % 60; 56 | tz[3] = (tzmins/10)%10 + '0'; 57 | tz[4] = tzmins%10 + '0'; 58 | tz[5] = 0; 59 | return mystringjoin(buf) + tz; 60 | } 61 | 62 | // Message ID strings have the form SECONDS.USEC.PID.nullmailer@HOST 63 | mystring make_messageid(const mystring& idhost) 64 | { 65 | struct timeval tv; 66 | gettimeofday(&tv, 0); 67 | mystring tmp = "<"; 68 | tmp += itoa(tv.tv_sec); 69 | tmp += '.'; 70 | tmp += itoa(tv.tv_usec, 6); 71 | tmp += '.'; 72 | tmp += itoa(getpid()); 73 | tmp += ".nullmailer@"; 74 | tmp += idhost; 75 | tmp += '>'; 76 | return tmp; 77 | } 78 | 79 | mystring make_boundary() 80 | { 81 | struct timeval tv; 82 | gettimeofday(&tv, 0); 83 | mystring tmp = itoa(tv.tv_sec); 84 | tmp += '.'; 85 | tmp += itoa(tv.tv_usec, 6); 86 | tmp += '.'; 87 | tmp += itoa(getpid()); 88 | return tmp; 89 | } 90 | -------------------------------------------------------------------------------- /lib/makefield.h: -------------------------------------------------------------------------------- 1 | #ifndef NULLMAILER__MAKEFIELD__H__ 2 | #define NULLMAILER__MAKEFIELD__H__ 3 | 4 | extern mystring make_date(time_t t = 0); 5 | extern mystring make_messageid(const mystring& idhost); 6 | extern mystring make_boundary(); 7 | 8 | #endif // NULLMAILER__MAKEFIELD__H__ 9 | -------------------------------------------------------------------------------- /lib/mergelib.sh: -------------------------------------------------------------------------------- 1 | set -e 2 | archive="$1" 3 | shift 4 | tmpdir=".libmerge.$archive.$$.$RANDOM.$USER" 5 | mkdir "$tmpdir" 6 | cd "$tmpdir" 7 | trap 'cd ..; rm -rf "$tmpdir"' EXIT 8 | for input in "$@"; do 9 | dir="`basename "$input"`" 10 | mkdir "$dir" 11 | cd "$dir" 12 | "${AR:-ar}" x ../../"$input" 13 | cd .. 14 | done 15 | "${AR:-ar}" rc ../"$archive" */* 16 | "${RANLIB:-ranlib}" ../"$archive" 17 | -------------------------------------------------------------------------------- /lib/mystring/Makefile.am: -------------------------------------------------------------------------------- 1 | noinst_LIBRARIES = libmystring.a 2 | EXTRA_DIST = ChangeLog iter.h join.h mystring.h rep.h trace.h 3 | 4 | AM_CPPFLAGS = -I$(top_srcdir)/lib 5 | 6 | libmystring_a_SOURCES = \ 7 | append.cc \ 8 | assign.cc \ 9 | count.cc \ 10 | fdobuf.cc \ 11 | find_first_ch.cc \ 12 | find_first_of.cc \ 13 | find_last_ch.cc \ 14 | find_last_of.cc \ 15 | iter.cc \ 16 | join.cc \ 17 | lower.cc \ 18 | lstrip.cc \ 19 | mystring.cc \ 20 | rep.cc \ 21 | starts_with.cc \ 22 | rstrip.cc \ 23 | sub.cc \ 24 | subst.cc \ 25 | strip.cc \ 26 | upper.cc 27 | -------------------------------------------------------------------------------- /lib/mystring/append.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include "mystring.h" 3 | #include "trace.h" 4 | 5 | void mystring::append(const char* str, size_t len) 6 | { 7 | if(!str || !len) 8 | return; 9 | if(!*this) 10 | assign(str, len); 11 | else 12 | rep = rep->append(str, len); 13 | } 14 | 15 | void mystring::append(const char* in) 16 | { 17 | if(in) 18 | append(in, strlen(in)); 19 | } 20 | -------------------------------------------------------------------------------- /lib/mystring/assign.cc: -------------------------------------------------------------------------------- 1 | #include "mystring.h" 2 | #include "trace.h" 3 | #include 4 | #include 5 | 6 | void mystring::dupnil() 7 | { 8 | trace(""); 9 | rep = &nil; 10 | rep->attach(); 11 | } 12 | 13 | void mystring::assign(const char* in) 14 | { 15 | if(in) 16 | assign(in, strlen(in)); 17 | else { 18 | mystringrep* tmp = rep; 19 | dupnil(); 20 | tmp->detach(); 21 | } 22 | } 23 | 24 | void mystring::assign(const char* in, size_t len) 25 | { 26 | trace("in='" << in << "'"); 27 | if(in != rep->buf) { 28 | mystringrep* tmp = rep; 29 | dup(in, len); 30 | tmp->detach(); 31 | } 32 | } 33 | 34 | void mystring::dup(const char* in, size_t len) 35 | { 36 | trace("in='" << in << "'"); 37 | rep = mystringrep::dup(in, len); 38 | rep->attach(); 39 | } 40 | 41 | void mystring::dup(const char* in) 42 | { 43 | if(in) 44 | dup(in, strlen(in)); 45 | else 46 | dupnil(); 47 | } 48 | 49 | void mystring::operator=(const mystringjoin& in) 50 | { 51 | mystringrep* tmp = rep; 52 | rep = in.traverse(); 53 | rep->attach(); 54 | tmp->detach(); 55 | } 56 | -------------------------------------------------------------------------------- /lib/mystring/count.cc: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2018 Bruce Guenter 2 | // 3 | // This program is free software; you can redistribute it and/or modify 4 | // it under the terms of the GNU General Public License as published by 5 | // the Free Software Foundation; either version 2 of the License, or 6 | // (at your option) any later version. 7 | // 8 | // This program is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with this program; if not, write to the Free Software 15 | // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 16 | 17 | #include "mystring.h" 18 | 19 | unsigned mystring::count(char ch) const 20 | { 21 | unsigned c = 0; 22 | for(int pos = find_first(ch); pos > 0; pos = find_first(ch, pos+1)) 23 | ++c; 24 | return c; 25 | } 26 | -------------------------------------------------------------------------------- /lib/mystring/fdobuf.cc: -------------------------------------------------------------------------------- 1 | #include "mystring.h" 2 | #include "fdbuf/fdbuf.h" 3 | 4 | fdobuf& operator<<(fdobuf& out, const mystring& str) 5 | { 6 | out.write(str.c_str(), str.length()); 7 | return out; 8 | } 9 | 10 | -------------------------------------------------------------------------------- /lib/mystring/find_first_ch.cc: -------------------------------------------------------------------------------- 1 | #include "mystring.h" 2 | #include 3 | 4 | int mystring::find_first(char ch, size_t offset) const 5 | { 6 | if(offset >= rep->length) 7 | return -1; 8 | char* ptr = strchr(rep->buf+offset, ch); 9 | return ptr ? ptr-rep->buf : -1; 10 | } 11 | 12 | -------------------------------------------------------------------------------- /lib/mystring/find_first_of.cc: -------------------------------------------------------------------------------- 1 | #include "mystring.h" 2 | #include 3 | 4 | int mystring::find_first_of(const char* setstr, size_t setlen, 5 | size_t offset) const 6 | { 7 | for(; offset < rep->length; offset++) { 8 | if(memchr(setstr, rep->buf[offset], setlen)) 9 | return offset; 10 | } 11 | return -1; 12 | } 13 | 14 | int mystring::find_first_of(const char* setstr, size_t offset) const 15 | { 16 | return find_first_of(setstr, strlen(setstr), offset); 17 | } 18 | 19 | int mystring::find_first_of(const mystring& setstr, size_t offset) const 20 | { 21 | return find_first_of(setstr.rep->buf, setstr.rep->length, offset); 22 | } 23 | -------------------------------------------------------------------------------- /lib/mystring/find_last_ch.cc: -------------------------------------------------------------------------------- 1 | #include "mystring.h" 2 | 3 | int mystring::find_last(char ch, size_t offset) const 4 | { 5 | if(offset == (size_t)-1) 6 | offset = rep->length-1; 7 | const char* ptr = rep->buf + offset; 8 | while(ptr >= rep->buf) { 9 | if(*ptr == ch) 10 | return ptr - rep->buf; 11 | --ptr; 12 | } 13 | return -1; 14 | } 15 | -------------------------------------------------------------------------------- /lib/mystring/find_last_of.cc: -------------------------------------------------------------------------------- 1 | #include "mystring.h" 2 | #include 3 | 4 | int mystring::find_last_of(const char* setstr, size_t setlen, 5 | size_t offset) const 6 | { 7 | if(offset == (size_t)-1) 8 | offset = rep->length-1; 9 | for(int i = offset; i >= 0; --i) { 10 | if(memchr(setstr, rep->buf[i], setlen)) 11 | return i; 12 | } 13 | return -1; 14 | } 15 | 16 | int mystring::find_last_of(const char* setstr, size_t offset) const 17 | { 18 | return find_last_of(setstr, strlen(setstr), offset); 19 | } 20 | 21 | int mystring::find_last_of(const mystring& setstr, size_t offset) const 22 | { 23 | return find_last_of(setstr.rep->buf, setstr.rep->length, offset); 24 | } 25 | -------------------------------------------------------------------------------- /lib/mystring/iter.cc: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2018 Bruce Guenter 2 | // 3 | // This program is free software; you can redistribute it and/or modify 4 | // it under the terms of the GNU General Public License as published by 5 | // the Free Software Foundation; either version 2 of the License, or 6 | // (at your option) any later version. 7 | // 8 | // This program is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with this program; if not, write to the Free Software 15 | // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 16 | 17 | #include "mystring.h" 18 | 19 | mystring_iter::mystring_iter(const mystring& s, char e) 20 | : str(s), sep(e), pos(0) 21 | { 22 | advance(); 23 | } 24 | 25 | mystring_iter::~mystring_iter() 26 | { 27 | } 28 | 29 | void mystring_iter::advance() 30 | { 31 | if(pos == -1) 32 | return; 33 | int i = str.find_first(sep, pos); 34 | if(i == -1) { 35 | if(pos >= 0 && pos < (int)str.length()) { 36 | part = str.right(pos); 37 | pos = str.length(); 38 | } 39 | else { 40 | part = ""; 41 | pos = -1; 42 | } 43 | } 44 | else { 45 | part = str.sub(pos, i-pos); 46 | pos = i + 1; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /lib/mystring/iter.h: -------------------------------------------------------------------------------- 1 | /* $Id: iter.h 616 2005-08-19 20:11:01Z bruce $ */ 2 | // Copyright (C) 2018 Bruce Guenter 3 | // 4 | // This program is free software; you can redistribute it and/or modify 5 | // it under the terms of the GNU General Public License as published by 6 | // the Free Software Foundation; either version 2 of the License, or 7 | // (at your option) any later version. 8 | // 9 | // This program is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU General Public License 15 | // along with this program; if not, write to the Free Software 16 | // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 17 | 18 | #ifndef MYSTRING__ITER__H__ 19 | #define MYSTRING__ITER__H__ 20 | 21 | class mystring_iter 22 | { 23 | const mystring str; 24 | const char sep; 25 | int pos; 26 | mystring part; 27 | 28 | void advance(); 29 | public: 30 | mystring_iter(const mystring&, char = '\0'); 31 | ~mystring_iter(); 32 | 33 | operator bool() const { return pos >= 0; } 34 | bool operator!() const { return pos < 0; } 35 | mystring operator*() const { return part; } 36 | void operator++() { advance(); } 37 | }; 38 | 39 | #endif 40 | -------------------------------------------------------------------------------- /lib/mystring/join.cc: -------------------------------------------------------------------------------- 1 | #include "mystring.h" 2 | #include 3 | 4 | // This "join" class relies on one fairly obscure detail in the C++ 5 | // standard: temporaries are destructed only after the entire 6 | // "full-expression" has completed. That is, if the sequence: 7 | // f(f(f(x))) creates three temporary objects, the inner objects are 8 | // destroyed only after the execution has completed. This allows us 9 | // to build a complete linked-list on the stack. Tricky, but efficient! 10 | 11 | struct tmpitem 12 | { 13 | const char* str; 14 | unsigned len; 15 | }; 16 | 17 | mystringrep* mystringjoin::traverse() const 18 | { 19 | // At first glance, a recursive implementation would be the most logical 20 | // way of doing this, but it turned out to be a significant loss. This 21 | // method traverses the pointer chain to first determine the length, and 22 | // then to do the actual copying. 23 | 24 | // Note the use of do/while loops to avoid a test at the head of the loop 25 | // which will always succeed (there is always at least one node or item). 26 | unsigned count = 0; 27 | const mystringjoin* node = this; 28 | do { 29 | ++count; 30 | } while((node = node->prev) != 0); 31 | 32 | // The use of a temporary array avoids re-traversing the pointer 33 | // chain, which is a slight performance win. 34 | tmpitem items[count]; 35 | 36 | unsigned length = 0; 37 | node = this; 38 | tmpitem* item = items; 39 | do { 40 | unsigned l = node->rep ? node->rep->length : strlen(node->str); 41 | length += l; 42 | item->str = node->str; 43 | item->len = l; 44 | ++item; 45 | } while((node = node->prev) != 0); 46 | 47 | // Since the chain is constructed such that the last item is the 48 | // first node, the string gets constructed in reverse order. 49 | mystringrep* rep = mystringrep::alloc(length); 50 | char* buf = rep->buf + length; 51 | item = items; 52 | do { 53 | unsigned l = item->len; 54 | buf -= l; 55 | memcpy(buf, item->str, l); 56 | ++item; 57 | } while(--count != 0); 58 | 59 | rep->buf[length] = 0; 60 | return rep; 61 | } 62 | -------------------------------------------------------------------------------- /lib/mystring/join.h: -------------------------------------------------------------------------------- 1 | /* $Id: join.h 616 2005-08-19 20:11:01Z bruce $ */ 2 | // Copyright (C) 2018 Bruce Guenter 3 | // 4 | // This program is free software; you can redistribute it and/or modify 5 | // it under the terms of the GNU General Public License as published by 6 | // the Free Software Foundation; either version 2 of the License, or 7 | // (at your option) any later version. 8 | // 9 | // This program is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU General Public License 15 | // along with this program; if not, write to the Free Software 16 | // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 17 | 18 | #ifndef MYSTRING__JOIN__H__ 19 | #define MYSTRING__JOIN__H__ 20 | 21 | class mystringjoin 22 | { 23 | private: 24 | const mystringjoin* prev; 25 | mystringrep* rep; 26 | const char* str; 27 | 28 | mystringjoin(); 29 | public: 30 | mystringjoin(const mystringjoin& j) 31 | : prev(j.prev), rep(j.rep), str(j.str) 32 | { 33 | rep->attach(); 34 | } 35 | mystringjoin(const mystring& s) 36 | : prev(0), rep(s.rep), str(s.rep->buf) 37 | { 38 | rep->attach(); 39 | } 40 | mystringjoin(const char* s) 41 | : prev(0), rep(0), str(s) 42 | { 43 | } 44 | mystringjoin(const mystringjoin& p, const mystring& s) 45 | : prev(&p), rep(s.rep), str(s.rep->buf) 46 | { 47 | rep->attach(); 48 | } 49 | mystringjoin(const mystringjoin& p, const char* s) 50 | : prev(&p), rep(0), str(s) 51 | { 52 | } 53 | ~mystringjoin() 54 | { 55 | if(rep) rep->detach(); 56 | } 57 | mystringrep* traverse() const; 58 | }; 59 | 60 | inline mystring::mystring(const mystringjoin& j) 61 | : rep(j.traverse()) 62 | { 63 | rep->attach(); 64 | } 65 | 66 | inline mystringjoin operator+(const mystringjoin& a, const mystring& b) 67 | { 68 | return mystringjoin(a, b); 69 | } 70 | 71 | inline mystringjoin operator+(const mystringjoin& a, const char* b) 72 | { 73 | return mystringjoin(a, b); 74 | } 75 | 76 | #endif 77 | -------------------------------------------------------------------------------- /lib/mystring/lower.cc: -------------------------------------------------------------------------------- 1 | #include "mystring.h" 2 | #include 3 | 4 | mystring mystring::lower() const 5 | { 6 | const unsigned length = rep->length; 7 | char buf[length+1]; 8 | const char* in = rep->buf + length; 9 | bool changed = false; 10 | for(char* out = buf+length; out >= buf; in--, out--) 11 | if(isupper(*in)) 12 | *out = tolower(*in), changed = true; 13 | else 14 | *out = *in; 15 | if(!changed) 16 | return *this; 17 | else 18 | return mystring(buf, length); 19 | } 20 | -------------------------------------------------------------------------------- /lib/mystring/lstrip.cc: -------------------------------------------------------------------------------- 1 | #include "mystring.h" 2 | #include 3 | 4 | mystring mystring::lstrip() const 5 | { 6 | const char* ptr = rep->buf; 7 | while(*ptr && isspace(*ptr)) 8 | ++ptr; 9 | return ptr; 10 | } 11 | -------------------------------------------------------------------------------- /lib/mystring/mystring.cc: -------------------------------------------------------------------------------- 1 | #include "mystring.h" 2 | #include "trace.h" 3 | #include 4 | #include 5 | 6 | #ifdef MYSTRING_TRACE 7 | mystring::~mystring() 8 | { 9 | trace("rep=" << (void*)rep); 10 | rep->detach(); 11 | } 12 | #endif 13 | 14 | int mystring::operator!=(const char* in) const 15 | { 16 | if(rep->buf == in) 17 | return 0; 18 | return strcmp(rep->buf, in); 19 | } 20 | 21 | int mystring::operator!=(const mystring& in) const 22 | { 23 | if(rep->buf == in.rep->buf) 24 | return 0; 25 | return strcmp(rep->buf, in.rep->buf); 26 | } 27 | 28 | const mystring mystring::NUL("", 1); 29 | -------------------------------------------------------------------------------- /lib/mystring/mystring.h: -------------------------------------------------------------------------------- 1 | /* $Id: mystring.h 635 2005-11-02 17:37:50Z bruce $ */ 2 | // Copyright (C) 2018 Bruce Guenter 3 | // 4 | // This program is free software; you can redistribute it and/or modify 5 | // it under the terms of the GNU General Public License as published by 6 | // the Free Software Foundation; either version 2 of the License, or 7 | // (at your option) any later version. 8 | // 9 | // This program is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU General Public License 15 | // along with this program; if not, write to the Free Software 16 | // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 17 | 18 | #ifndef MYSTRING__H__ 19 | #define MYSTRING__H__ 20 | 21 | #include 22 | #include "mystring/rep.h" 23 | 24 | class mystringjoin; 25 | class mystring 26 | { 27 | friend class mystringtmp; 28 | friend class mystringjoin; 29 | private: 30 | mystringrep* rep; 31 | 32 | protected: 33 | void dupnil(); 34 | void dup(const char*, size_t); 35 | void dup(const char*); 36 | void assign(const char*); 37 | void assign(const char*, size_t); 38 | public: 39 | static const mystring NUL; 40 | 41 | mystring() { dupnil(); } 42 | mystring(const char* s) { dup(s); } 43 | mystring(const mystring& s) { dup(s.rep->buf, s.rep->length); } 44 | mystring(const char* str, size_t len) { dup(str, len); } 45 | mystring(const mystringjoin&); 46 | ~mystring(); 47 | 48 | const char* c_str() const { return rep->buf; } 49 | 50 | bool operator!() const { return empty(); } 51 | 52 | char operator[](size_t i) const { return rep->buf[i]; } 53 | 54 | size_t length() const { return rep->length; } 55 | 56 | bool empty() const { return rep->length == 0; } 57 | 58 | int operator!=(const char* in) const; 59 | int operator!=(const mystring& in) const; 60 | bool operator==(const char* in) const 61 | { 62 | return !operator!=(in); 63 | } 64 | bool operator==(const mystring& in) const 65 | { 66 | return !operator!=(in); 67 | } 68 | 69 | void operator=(const char* in) { assign(in); } 70 | void operator=(const mystring& in) { assign(in.rep->buf, in.rep->length); } 71 | void operator=(const mystringjoin& in); 72 | 73 | mystring subst(char from, char to) const; 74 | 75 | mystring lower() const; 76 | mystring upper() const; 77 | 78 | bool starts_with(const mystring&) const; 79 | bool starts_with(const char*) const; 80 | bool starts_with(const char*, size_t) const; 81 | 82 | int find_first(char, size_t = 0) const; 83 | int find_first_of(const mystring&, size_t = 0) const; 84 | int find_first_of(const char*, size_t = 0) const; 85 | int find_first_of(const char*, size_t, size_t) const; 86 | 87 | int find_last(char, size_t = (size_t)-1) const; 88 | int find_last_of(const mystring&, size_t = (size_t)-1) const; 89 | int find_last_of(const char*, size_t = 0) const; 90 | int find_last_of(const char*, size_t, size_t) const; 91 | 92 | mystring left(size_t) const; 93 | mystring right(size_t) const; 94 | mystring sub(size_t, size_t) const; 95 | 96 | mystring lstrip() const; 97 | mystring rstrip() const; 98 | mystring strip() const; 99 | 100 | unsigned count(char ch) const; 101 | 102 | void append(const char*); 103 | void append(const char*, size_t); 104 | 105 | void operator+=(const mystring& str) {append(str.rep->buf, str.rep->length);} 106 | void operator+=(const char* str) { append(str); } 107 | void operator+=(char ch) 108 | { 109 | char str[2] = { ch, 0 }; 110 | append(str, 1); 111 | } 112 | }; 113 | 114 | #ifndef MYSTRING_TRACE 115 | inline mystring::~mystring() 116 | { 117 | rep->detach(); 118 | } 119 | #endif 120 | 121 | #include "mystring/iter.h" 122 | #include "mystring/join.h" 123 | 124 | class fdobuf; 125 | fdobuf& operator<<(fdobuf& out, const mystring& str); 126 | 127 | //istream& operator>>(istream& in, mystring& str); 128 | 129 | typedef mystring string; 130 | 131 | #endif 132 | -------------------------------------------------------------------------------- /lib/mystring/operator_in.cc: -------------------------------------------------------------------------------- 1 | #include "mystring.h" 2 | #include 3 | 4 | istream& operator>>(istream& in, mystring& str) 5 | { 6 | str = ""; 7 | char buf[256]; // buffer this many characters at a time 8 | unsigned i = 0; 9 | in >> ws; // skip leading whitespace 10 | char ch; 11 | while((ch = in.get()) != EOF) { 12 | if(isspace(ch)) { // end the input on whitespace 13 | buf[i] = 0; 14 | str += buf; 15 | i = 0; 16 | break; 17 | } 18 | else { 19 | buf[i++] = ch; 20 | if(i == sizeof(buf)-1) { // append the filled buffer and empty it 21 | buf[i] = 0; 22 | str += buf; 23 | i = 0; 24 | } 25 | } 26 | } 27 | if(i > 0) { // If EOF was reached before whitespace, 28 | buf[i] = 0; // append the buffer to the string. 29 | str += buf; 30 | } else if(str.length() == 0) // Mark failure if no string was read. 31 | in.set(ios::failbit); 32 | return in; 33 | } 34 | -------------------------------------------------------------------------------- /lib/mystring/rep.cc: -------------------------------------------------------------------------------- 1 | #include "mystring.h" 2 | #include "trace.h" 3 | #include 4 | #include 5 | 6 | mystringrep nil = { 0, 1, 1, "" }; 7 | 8 | static const unsigned replength = sizeof(unsigned)*3; 9 | 10 | static const unsigned sizestep = sizeof(unsigned); 11 | static const unsigned slackdiv = 4; 12 | static const unsigned slackmax = 16; 13 | 14 | #ifdef MYSTRINGREP_STATS 15 | 16 | #include "fdbuf.h" 17 | 18 | struct _rep_stats 19 | { 20 | unsigned allocs; 21 | unsigned alloc_size; 22 | unsigned alloc_len; 23 | 24 | unsigned appends; 25 | unsigned appends_dup; 26 | 27 | _rep_stats() 28 | : allocs(0) 29 | { 30 | } 31 | 32 | void stat(const char* name, unsigned value) 33 | { 34 | ferr << "mystringrep: " << name << ": " << value << '\n'; 35 | } 36 | void pcnt(const char* name, unsigned denom, unsigned divis) 37 | { 38 | ferr << "mystringrep: " << name << ": " 39 | << denom << '/' << divis << '='; 40 | if(divis) ferr << denom * 100 / divis << '%'; 41 | else ferr << "N/A"; 42 | ferr << '\n'; 43 | } 44 | 45 | ~_rep_stats() 46 | { 47 | stat(" size step", sizestep); 48 | stat(" slack divisor", slackdiv); 49 | stat(" slack maximum", slackmax); 50 | stat(" allocs", allocs); 51 | stat(" alloc length", alloc_len); 52 | stat(" alloc size", alloc_size); 53 | pcnt(" alloc slack", alloc_size-alloc_len, alloc_len); 54 | stat("alloc overhead", allocs*replength); 55 | pcnt(" appends->dup", appends_dup, appends); 56 | } 57 | }; 58 | 59 | static _rep_stats stats; 60 | 61 | #define ACCOUNT(NAME,VALUE) stats. NAME += VALUE 62 | 63 | #else // MYSTRINGREP_STATS 64 | 65 | #define ACCOUNT(NAME,VALUE) 66 | 67 | #endif // MYSTRINGREP_STATS 68 | 69 | /////////////////////////////////////////////////////////////////////////////// 70 | // class mystringrep 71 | /////////////////////////////////////////////////////////////////////////////// 72 | mystringrep* mystringrep::alloc(unsigned length) 73 | { 74 | ACCOUNT(allocs, 1); 75 | trace_static("length=" << length); 76 | if(length == 0) 77 | return &nil; 78 | 79 | ACCOUNT(alloc_len, length); 80 | unsigned slack = length / slackdiv; 81 | if(slack > slackmax) 82 | slack = slackmax; 83 | unsigned size = length+1 + sizestep-1 + slack; 84 | size = size - size % sizestep; 85 | ACCOUNT(alloc_size, size); 86 | 87 | mystringrep* ptr = (mystringrep*)new char[size+replength]; 88 | ptr->length = length; 89 | ptr->references = 0; 90 | ptr->size = size; 91 | return ptr; 92 | } 93 | 94 | mystringrep* mystringrep::dup(const char* str, unsigned length) 95 | { 96 | trace_static("str=" << (void*)str << " length=" << length); 97 | if(length == 0) 98 | return &nil; 99 | mystringrep* ptr = alloc(length); 100 | memcpy(ptr->buf, str, length); 101 | ptr->buf[length] = 0; 102 | return ptr; 103 | } 104 | 105 | mystringrep* mystringrep::dup(const char* str1, unsigned length1, 106 | const char* str2, unsigned length2) 107 | { 108 | trace_static(""); 109 | if(length1+length2 == 0) 110 | return &nil; 111 | mystringrep* ptr = alloc(length1+length2); 112 | memcpy(ptr->buf, str1, length1); 113 | memcpy(ptr->buf+length1, str2, length2); 114 | ptr->buf[length1+length2] = 0; 115 | return ptr; 116 | } 117 | 118 | mystringrep* mystringrep::append(const char* str, unsigned len) 119 | { 120 | ACCOUNT(appends, 1); 121 | unsigned newlen = length + len; 122 | // If there are more than one references, always make a duplicate 123 | // Also, if this does not have enough space to add the new string, dup it 124 | if(references > 1 || newlen >= size) { 125 | ACCOUNT(appends_dup, 1); 126 | mystringrep* tmp = dup(buf, length, str, len); 127 | tmp->attach(); 128 | detach(); 129 | return tmp; 130 | } 131 | // Otherwise, just add the new string to the end of this 132 | else { 133 | memcpy(buf+length, str, len); 134 | buf[newlen] = 0; 135 | length = newlen; 136 | return this; 137 | } 138 | } 139 | 140 | #ifdef MYSTRING_TRACE 141 | void mystringrep::attach() 142 | { 143 | trace("references=" << references); 144 | ++references; 145 | } 146 | #endif 147 | 148 | void mystringrep::detach() 149 | { 150 | trace("references=" << references); 151 | 152 | --references; 153 | if(!references) { 154 | trace("deleting this"); 155 | delete[] (char*)this; 156 | } 157 | } 158 | -------------------------------------------------------------------------------- /lib/mystring/rep.h: -------------------------------------------------------------------------------- 1 | /* $Id: rep.h 616 2005-08-19 20:11:01Z bruce $ */ 2 | // Copyright (C) 2018 Bruce Guenter 3 | // 4 | // This program is free software; you can redistribute it and/or modify 5 | // it under the terms of the GNU General Public License as published by 6 | // the Free Software Foundation; either version 2 of the License, or 7 | // (at your option) any later version. 8 | // 9 | // This program is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU General Public License 15 | // along with this program; if not, write to the Free Software 16 | // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 17 | 18 | #ifndef MYSTRING__REP__H__ 19 | #define MYSTRING__REP__H__ 20 | 21 | struct mystringrep 22 | { 23 | unsigned length; 24 | unsigned references; 25 | unsigned size; 26 | char buf[1]; 27 | 28 | void attach(); 29 | void detach(); 30 | mystringrep* append(const char*, unsigned); 31 | 32 | static mystringrep* alloc(unsigned); 33 | static mystringrep* dup(const char*, unsigned); 34 | static mystringrep* dup(const char*, unsigned, 35 | const char*, unsigned); 36 | }; 37 | 38 | #ifndef MYSTRING_TRACE 39 | inline void mystringrep::attach() 40 | { 41 | references++; 42 | } 43 | #endif 44 | 45 | extern mystringrep nil; 46 | 47 | #endif 48 | -------------------------------------------------------------------------------- /lib/mystring/rstrip.cc: -------------------------------------------------------------------------------- 1 | #include "mystring.h" 2 | #include 3 | 4 | mystring mystring::rstrip() const 5 | { 6 | const char* ptr = rep->buf + rep->length - 1; 7 | while(ptr >= rep->buf && isspace(*ptr)) 8 | --ptr; 9 | return mystring(rep->buf, ptr-rep->buf+1); 10 | } 11 | -------------------------------------------------------------------------------- /lib/mystring/starts_with.cc: -------------------------------------------------------------------------------- 1 | #include "mystring.h" 2 | #include 3 | 4 | bool mystring::starts_with(const char* that, size_t len) const 5 | { 6 | return len <= rep->length && memcmp(that, rep->buf, len) == 0; 7 | } 8 | 9 | bool mystring::starts_with(const char* that) const 10 | { 11 | return starts_with(that, strlen(that)); 12 | } 13 | 14 | bool mystring::starts_with(const mystring& that) const 15 | { 16 | return starts_with(that.rep->buf, that.rep->length); 17 | } 18 | -------------------------------------------------------------------------------- /lib/mystring/strip.cc: -------------------------------------------------------------------------------- 1 | #include "mystring.h" 2 | #include 3 | 4 | mystring mystring::strip() const 5 | { 6 | const char* start = rep->buf; 7 | while(*start && isspace(*start)) 8 | ++start; 9 | const char* end = rep->buf + rep->length - 1; 10 | while(end >= start && isspace(*end)) 11 | --end; 12 | return mystring(start, end-start+1); 13 | } 14 | -------------------------------------------------------------------------------- /lib/mystring/sub.cc: -------------------------------------------------------------------------------- 1 | #include "mystring.h" 2 | 3 | // return the sub-string ending at 'offset' 4 | mystring mystring::left(size_t offset) const 5 | { 6 | if(offset > rep->length) 7 | return *this; 8 | else 9 | return mystring(rep->buf, offset); 10 | } 11 | 12 | // return the sub-string starting at 'offset' 13 | mystring mystring::right(size_t offset) const 14 | { 15 | if(offset >= rep->length) 16 | return mystring(); 17 | else if(offset == 0) 18 | return *this; 19 | else 20 | return mystring(rep->buf+offset, rep->length-offset); 21 | } 22 | 23 | // return the 'len' characters of the string starting at 'offset' 24 | mystring mystring::sub(size_t offset, size_t len) const 25 | { 26 | // return right(offset).left(len); 27 | if(len == 0) 28 | return mystring(); 29 | else if(offset == 0 && len >= rep->length) 30 | return *this; 31 | else { 32 | if(len+offset >= rep->length) 33 | len = rep->length - offset; 34 | return mystring(rep->buf+offset, len); 35 | } 36 | } 37 | 38 | -------------------------------------------------------------------------------- /lib/mystring/subst.cc: -------------------------------------------------------------------------------- 1 | #include "mystring.h" 2 | 3 | mystring mystring::subst(char from, char to) const 4 | { 5 | const unsigned length = rep->length; 6 | char buf[length+1]; 7 | const char* in = rep->buf + length; 8 | bool changed = true; 9 | for(char* out = buf+length; out >= buf; in--, out--) 10 | if(*in == from) 11 | *out = to, changed = true; 12 | else 13 | *out = *in; 14 | if(!changed) 15 | return *this; 16 | else 17 | return mystring(buf, length); 18 | } 19 | -------------------------------------------------------------------------------- /lib/mystring/trace.h: -------------------------------------------------------------------------------- 1 | /* $Id: trace.h 616 2005-08-19 20:11:01Z bruce $ */ 2 | #include "mystring.h" 3 | 4 | #ifdef MYSTRING_TRACE 5 | ostream& operator<<(ostream& out, const mystringtmp& s); 6 | #define trace(X) cerr << (void*)this << "->" << __PRETTY_FUNCTION__ << X << endl 7 | #define trace_static(X) cerr << __PRETTY_FUNCTION__ << X << endl 8 | #else 9 | #define trace(X) do { } while(0) 10 | #define trace_static(X) do { } while(0) 11 | #endif 12 | -------------------------------------------------------------------------------- /lib/mystring/upper.cc: -------------------------------------------------------------------------------- 1 | #include "mystring.h" 2 | #include 3 | 4 | mystring mystring::upper() const 5 | { 6 | const unsigned length = rep->length; 7 | char buf[length+1]; 8 | const char* in = rep->buf + length; 9 | bool changed = false; 10 | for(char* out = buf+length; out >= buf; in--, out--) 11 | if(islower(*in)) { 12 | *out = toupper(*in); 13 | changed = true; 14 | } 15 | else 16 | *out = *in; 17 | if(!changed) 18 | return *this; 19 | else 20 | return mystring(buf, length); 21 | } 22 | -------------------------------------------------------------------------------- /lib/netstring.cc: -------------------------------------------------------------------------------- 1 | #include "config.h" 2 | #include "netstring.h" 3 | #include "itoa.h" 4 | 5 | mystring str2net(const mystring& s) 6 | { 7 | return mystringjoin(itoa(s.length())) + ":" + s + ","; 8 | } 9 | 10 | mystring strnl2net(const mystring& s) 11 | { 12 | return mystringjoin(itoa(s.length()+1)) + ":" + s + "\012,"; 13 | } 14 | -------------------------------------------------------------------------------- /lib/netstring.h: -------------------------------------------------------------------------------- 1 | #ifndef NULLMAILER__NETSTRING__H__ 2 | #define NULLMAILER__NETSTRING__H__ 3 | 4 | #include "mystring/mystring.h" 5 | mystring str2net(const mystring&); 6 | mystring strnl2net(const mystring&); 7 | 8 | #endif // NULLMAILER__NETSTRING__H__ 9 | -------------------------------------------------------------------------------- /lib/selfpipe.cc: -------------------------------------------------------------------------------- 1 | // nullmailer -- a simple relay-only MTA 2 | // Copyright (C) 2018 Bruce Guenter 3 | // 4 | // This program is free software; you can redistribute it and/or modify 5 | // it under the terms of the GNU General Public License as published by 6 | // the Free Software Foundation; either version 2 of the License, or 7 | // (at your option) any later version. 8 | // 9 | // This program is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU General Public License 15 | // along with this program; if not, write to the Free Software 16 | // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 17 | // 18 | // You can contact me at . There is also a mailing list 19 | // available to discuss this package. To subscribe, send an email to 20 | // . 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include "selfpipe.h" 29 | 30 | static int fds[2] = { -1, -1 }; 31 | 32 | static int fcntl_fl_on(int fd, int flag) 33 | { 34 | int flags; 35 | int newflags; 36 | if ((flags = fcntl(fd, F_GETFL, 0)) == -1) 37 | return 0; 38 | if ((newflags = flags | flag) != flags) 39 | if (fcntl(fd, F_SETFL, newflags)) 40 | return 0; 41 | return 1; 42 | } 43 | 44 | selfpipe::selfpipe() 45 | { 46 | if (fds[0] < 0) { 47 | if (pipe(fds) != -1) { 48 | if (fcntl_fl_on(fds[0], O_NONBLOCK) 49 | && fcntl_fl_on(fds[1], O_NONBLOCK) 50 | && fcntl_fl_on(fds[1], FD_CLOEXEC) 51 | && fcntl_fl_on(fds[1], FD_CLOEXEC)) 52 | return; 53 | 54 | close(fds[0]); 55 | close(fds[1]); 56 | } 57 | fds[0] = fds[1] = -1; 58 | } 59 | } 60 | 61 | selfpipe::operator bool() const 62 | { 63 | return fds[0] >= 0; 64 | } 65 | 66 | static void catcher(int sig) 67 | { 68 | signal(sig, catcher); 69 | write(fds[1], &sig, sizeof sig); 70 | } 71 | 72 | void selfpipe::catchsig(int sig) 73 | { 74 | signal(sig, catcher); 75 | } 76 | 77 | int selfpipe::caught() 78 | { 79 | int buf; 80 | if (read(fds[0], &buf, sizeof buf) == sizeof buf) 81 | return buf; 82 | return 0; 83 | } 84 | 85 | int selfpipe::waitsig(int timeout) 86 | { 87 | if (timeout > 0) { 88 | fd_set fdset; 89 | FD_ZERO(&fdset); 90 | FD_SET(fds[0], &fdset); 91 | struct timeval tv; 92 | tv.tv_sec = timeout; 93 | tv.tv_usec = 0; 94 | int s; 95 | while ((s = select(fds[0] + 1, &fdset, 0, 0, 96 | (timeout <= 0) ? 0 : &tv)) == -1) { 97 | if (errno != EINTR) 98 | return -1; 99 | } 100 | if (s != 1) 101 | return 0; 102 | } 103 | return caught(); 104 | } 105 | -------------------------------------------------------------------------------- /lib/selfpipe.h: -------------------------------------------------------------------------------- 1 | #ifndef NULLMAILER_SELFPIPE__H__ 2 | #define NULLMAILER_SELFPIPE__H__ 3 | 4 | class selfpipe 5 | { 6 | public: 7 | selfpipe(); 8 | 9 | operator bool() const; 10 | 11 | void catchsig(int sig); 12 | int caught(); 13 | int waitsig(int timeout = 0); 14 | }; 15 | 16 | #endif // NULLMAILER_SELFPIPE__H__ 17 | -------------------------------------------------------------------------------- /lib/setenv.cc: -------------------------------------------------------------------------------- 1 | // nullmailer -- a simple relay-only MTA 2 | // Copyright (C) 2018 Bruce Guenter 3 | // 4 | // This program is free software; you can redistribute it and/or modify 5 | // it under the terms of the GNU General Public License as published by 6 | // the Free Software Foundation; either version 2 of the License, or 7 | // (at your option) any later version. 8 | // 9 | // This program is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU General Public License 15 | // along with this program; if not, write to the Free Software 16 | // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 17 | // 18 | // You can contact me at . There is also a mailing list 19 | // available to discuss this package. To subscribe, send an email to 20 | // . 21 | 22 | #include 23 | #include 24 | #include 25 | #include "setenv.h" 26 | 27 | #ifndef HAVE_SETENV 28 | // This is not really a full emulation of setenv, but close enough 29 | int setenv(const char* var, const char* val, int overwrite) 30 | { 31 | size_t varlen = strlen(var); 32 | size_t vallen = strlen(val); 33 | char* str = (char*)malloc(varlen+vallen+2); 34 | if (str == 0) return -1; 35 | memcpy(str, var, varlen); 36 | str[varlen] = '='; 37 | memcpy(str+varlen+1, val, vallen); 38 | str[varlen+vallen+1] = 0; 39 | return putenv(str); 40 | } 41 | #endif 42 | -------------------------------------------------------------------------------- /lib/setenv.h: -------------------------------------------------------------------------------- 1 | #ifndef NULLMAILER__SETENV__H__ 2 | #define NULLMAILER__SETENV__H__ 3 | 4 | extern "C" int setenv(const char*, const char*, int); 5 | 6 | #endif 7 | -------------------------------------------------------------------------------- /lib/tcpconnect.cc: -------------------------------------------------------------------------------- 1 | // nullmailer -- a simple relay-only MTA 2 | // Copyright (C) 2018 Bruce Guenter 3 | // 4 | // This program is free software; you can redistribute it and/or modify 5 | // it under the terms of the GNU General Public License as published by 6 | // the Free Software Foundation; either version 2 of the License, or 7 | // (at your option) any later version. 8 | // 9 | // This program is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU General Public License 15 | // along with this program; if not, write to the Free Software 16 | // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 17 | // 18 | // You can contact me at . There is also a mailing list 19 | // available to discuss this package. To subscribe, send an email to 20 | // . 21 | 22 | #include "config.h" 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include "errcodes.h" 31 | #include "itoa.h" 32 | #include "connect.h" 33 | 34 | static int err_return(int errn, int dflt) 35 | { 36 | if (errn == HOST_NOT_FOUND) 37 | return -ERR_HOST_NOT_FOUND; 38 | if (errn == NO_ADDRESS) 39 | return -ERR_NO_ADDRESS; 40 | if (errn == NO_RECOVERY || errn == EAI_FAIL) 41 | return -ERR_GHBN_FATAL; 42 | if (errn == TRY_AGAIN || errn == EAI_AGAIN) 43 | return -ERR_GHBN_TEMP; 44 | if (errn == EAI_NONAME) 45 | return -ERR_HOST_NOT_FOUND; 46 | if (errn == ECONNREFUSED) 47 | return -ERR_CONN_REFUSED; 48 | if (errn == ETIMEDOUT) 49 | return -ERR_CONN_TIMEDOUT; 50 | if (errn == ENETUNREACH) 51 | return -ERR_CONN_UNREACHABLE; 52 | return -dflt; 53 | } 54 | 55 | #ifdef HAVE_GETADDRINFO 56 | 57 | static int getaddr(const char* hostname, int port, struct addrinfo** result) 58 | { 59 | const char *service = itoa(port, 6); 60 | struct addrinfo req; 61 | memset(&req, 0, sizeof(req)); 62 | req.ai_flags = AI_NUMERICSERV; 63 | req.ai_socktype = SOCK_STREAM; 64 | int e = getaddrinfo(hostname, service, &req, result); 65 | return e ? err_return(e, ERR_GHBN_TEMP) : 0; 66 | } 67 | 68 | static bool canbind(int family, const struct addrinfo* ai) 69 | { 70 | for (; ai; ai = ai->ai_next) 71 | if (ai->ai_family == family) 72 | return true; 73 | return false; 74 | } 75 | 76 | static bool bindit(int fd, int family, const struct addrinfo* ai) 77 | { 78 | for (; ai; ai = ai->ai_next) 79 | if (ai->ai_family == family) 80 | if (bind(fd, ai->ai_addr, ai->ai_addrlen) == 0) 81 | return true; 82 | return false; 83 | } 84 | 85 | int tcpconnect(const char* hostname, int port, const char* source) 86 | { 87 | struct addrinfo* res; 88 | int err = getaddr(hostname, port, &res); 89 | if (err) 90 | return err; 91 | struct addrinfo* source_addr = NULL; 92 | if (source) { 93 | err = getaddr(source, 0, &source_addr); 94 | if (err) 95 | return err; 96 | } 97 | int s = -1; 98 | err = ERR_CONN_FAILED; 99 | struct addrinfo* orig_res = res; 100 | 101 | if (source_addr) 102 | // Check if some address is the same family as the source 103 | for (; res != NULL; res = res->ai_next) 104 | if (canbind(res->ai_family, source_addr)) 105 | break; 106 | if (res == NULL) 107 | return -ERR_BIND_FAILED; 108 | 109 | for (; res != NULL; res = res->ai_next) { 110 | if (!source_addr || canbind(res->ai_family, source_addr)) { 111 | s = socket(res->ai_family, res->ai_socktype, res->ai_protocol); 112 | if(s > 0) { 113 | if(source_addr && !bindit(s, res->ai_family, source_addr)) { 114 | close(s); 115 | err = ERR_BIND_FAILED; 116 | s = -1; 117 | break; 118 | } 119 | if(connect(s, res->ai_addr, res->ai_addrlen) == 0) 120 | break; 121 | close(s); 122 | s = -1; 123 | } 124 | } 125 | } 126 | 127 | freeaddrinfo(orig_res); 128 | if (source_addr) 129 | freeaddrinfo(source_addr); 130 | 131 | if(s < 0) 132 | return err_return(errno, err); 133 | return s; 134 | } 135 | 136 | #else 137 | 138 | static int sethostbyname(const char* hostname, struct sockaddr_in& sa) 139 | { 140 | struct hostent *he = gethostbyname(hostname); 141 | if(!he) 142 | return err_return(h_errno, ERR_GHBN_TEMP); 143 | memcpy(&sa.sin_addr, he->h_addr, he->h_length); 144 | return 0; 145 | } 146 | 147 | int tcpconnect(const char* hostname, int port, const char* source) 148 | { 149 | struct sockaddr_in sa; 150 | memset(&sa, 0, sizeof(sa)); 151 | int e = sethostbyname(hostname, sa); 152 | if(e) return e; 153 | struct sockaddr_in source_sa; 154 | memset(&source_sa, 0, sizeof source_sa); 155 | if(source) { 156 | e = sethostbyname(source, source_sa); 157 | if(e) return e; 158 | } 159 | sa.sin_family = AF_INET; 160 | sa.sin_port = htons(port); 161 | int s = socket(PF_INET, SOCK_STREAM, 0); 162 | if(s == -1) 163 | return -ERR_SOCKET; 164 | if(source && bind(s, (sockaddr*)&source_sa, sizeof source_sa) != 0) { 165 | close(s); 166 | return err_return(errno, ERR_BIND_FAILED); 167 | } 168 | if(connect(s, (sockaddr*)&sa, sizeof(sa)) != 0) { 169 | close(s); 170 | return err_return(errno, ERR_CONN_FAILED); 171 | } 172 | return s; 173 | } 174 | 175 | #endif 176 | -------------------------------------------------------------------------------- /makedist.in: -------------------------------------------------------------------------------- 1 | # Template makedist.in file 2 | 3 | # Set PKGURL to the full base URL for the package web site. 4 | PKGURL=http://untroubled.org/nullmailer/ 5 | LISTURL='http://lists.untroubled.org/?list=nullmailer' 6 | 7 | # If LISTSUB is set, makedist will add a note regarding mailing list 8 | # subscription. 9 | LISTSUB=nullmailer-subscribe@lists.untroubled.org 10 | 11 | # Set MAKERPM to true if the tarball is to be built into a RPM. 12 | MAKERPM=false 13 | NOARCH=false 14 | 15 | # Set DOCS to the list of files that should go into the "docs" directory 16 | # in the destination site. 17 | DOCS= 18 | 19 | # Set EXTRAS to a list of any other extra files that should go into the 20 | # base directory in the destination site. 21 | EXTRAS="ChangeLog ChangeLog.old HOWTO NEWS README TODO" 22 | 23 | # Set EXTRAS_VER to a list of any other extra files that should go into 24 | # the version-numbered directory in the destination site. 25 | EXTRAS_VER="ANNOUNCEMENT" 26 | EXTRAS_DIST= 27 | 28 | VERSION_BASE= 29 | 30 | WEBSITE=untroubled.org 31 | WEBDIR=www/nullmailer 32 | 33 | # Set RPMUPLOAD to a list of additional "hostname/path" destinations to 34 | # which to upload the source and binary RPMs. 35 | #RPMUPLOAD="incoming.redhat.com/libc6" 36 | 37 | # Set LIST to the mailing list(s) to send the announcement to 38 | LIST=nullmailer@lists.untroubled.org 39 | 40 | # Run any extra commands to prepare the source tree (such as making 41 | # documentation) here. 42 | make -C doc 43 | -------------------------------------------------------------------------------- /protocols/Makefile.am: -------------------------------------------------------------------------------- 1 | libexecdir = @libexecdir@/nullmailer 2 | 3 | libexec_PROGRAMS = smtp qmqp 4 | AM_CPPFLAGS = -I$(top_srcdir)/lib 5 | 6 | if TLS 7 | TLS_SOURCES = tls_gnutls.cc 8 | TLS_LDADD = -lgnutls 9 | else 10 | TLS_SOURCES = tls_none.cc 11 | TLS_LDADD = 12 | endif 13 | 14 | smtp_SOURCES = smtp.cc protocol.cc $(TLS_SOURCES) protocol.h 15 | smtp_LDADD = ../lib/cli++/libcli++.a ../lib/libnullmailer.a $(TLS_LDADD) 16 | 17 | qmqp_SOURCES = qmqp.cc protocol.cc $(TLS_SOURCES) protocol.h 18 | qmqp_LDADD = ../lib/cli++/libcli++.a ../lib/libnullmailer.a $(TLS_LDADD) 19 | -------------------------------------------------------------------------------- /protocols/protocol.h: -------------------------------------------------------------------------------- 1 | #ifndef NULLMAILER__PROTOCOL__H__ 2 | #define NULLMAILER__PROTOCOL__H__ 3 | 4 | #include "fdbuf/fdbuf.h" 5 | 6 | #define DEFAULT_CA_FILE "/etc/ssl/certs/ca-certificates.crt" 7 | 8 | extern const int default_port; 9 | extern const int default_tls_port; 10 | extern void protocol_fail(int e, const char* msg); 11 | extern void protocol_succ(const char* msg); 12 | 13 | #define AUTH_DETECT 0 14 | #define AUTH_LOGIN 1 15 | #define AUTH_PLAIN 2 16 | #define AUTH_EXTERNAL 3 17 | extern const char* user; 18 | extern const char* pass; 19 | extern int auth_method; 20 | extern int port; 21 | extern int use_tls; 22 | extern int use_starttls; 23 | 24 | extern void protocol_prep(fdibuf& in); 25 | extern void protocol_send(fdibuf& in, fdibuf& netin, fdobuf& netout); 26 | extern void protocol_starttls(fdibuf& netin, fdobuf& netout); 27 | 28 | extern int tls_insecure; 29 | extern int tls_anon_auth; 30 | extern const char* tls_x509certfile; 31 | extern const char* tls_x509keyfile; 32 | extern const char* tls_x509cafile; 33 | extern const char* tls_x509crlfile; 34 | extern int tls_x509derfmt; 35 | extern void tls_init(const char* remote); 36 | extern void tls_send(fdibuf& in, int fd); 37 | 38 | #endif 39 | -------------------------------------------------------------------------------- /protocols/qmqp.cc: -------------------------------------------------------------------------------- 1 | // nullmailer -- a simple relay-only MTA 2 | // Copyright (C) 2018 Bruce Guenter 3 | // 4 | // This program is free software; you can redistribute it and/or modify 5 | // it under the terms of the GNU General Public License as published by 6 | // the Free Software Foundation; either version 2 of the License, or 7 | // (at your option) any later version. 8 | // 9 | // This program is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU General Public License 15 | // along with this program; if not, write to the Free Software 16 | // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 17 | // 18 | // You can contact me at . There is also a mailing list 19 | // available to discuss this package. To subscribe, send an email to 20 | // . 21 | 22 | #include "config.h" 23 | #include 24 | #include 25 | #include "errcodes.h" 26 | #include "fdbuf/fdbuf.h" 27 | #include "hostname.h" 28 | #include "itoa.h" 29 | #include "mystring/mystring.h" 30 | #include "netstring.h" 31 | #include "protocol.h" 32 | 33 | const int default_port = 628; 34 | const int default_tls_port = -1; // No standard for QMQP over SSL exists 35 | const char* cli_program = "qmqp"; 36 | const char* cli_help_prefix = "Send an email message via QMQP\n"; 37 | 38 | class qmqp 39 | { 40 | fdibuf& in; 41 | fdobuf& out; 42 | public: 43 | qmqp(fdibuf& netin, fdobuf& netout); 44 | ~qmqp(); 45 | void send(fdibuf& msg, unsigned long size, const mystring& env); 46 | }; 47 | 48 | qmqp::qmqp(fdibuf& netin, fdobuf& netout) 49 | : in(netin), out(netout) 50 | { 51 | } 52 | 53 | qmqp::~qmqp() 54 | { 55 | } 56 | 57 | bool skip_envelope(fdibuf& msg) 58 | { 59 | if(!msg.rewind()) 60 | return false; 61 | mystring tmp; 62 | while(msg.getline(tmp)) 63 | if(!tmp) 64 | break; 65 | return msg; 66 | } 67 | 68 | void qmqp::send(fdibuf& msg, unsigned long size, const mystring& env) 69 | { 70 | if(!skip_envelope(msg)) 71 | protocol_fail(ERR_MSG_READ, "Error re-reading message"); 72 | unsigned long fullsize = strlen(itoa(size)) + 1 + size + 1 + env.length(); 73 | out << itoa(fullsize) << ":"; // Start the "outer" netstring 74 | out << itoa(size) << ":"; // Start the message netstring 75 | fdbuf_copy(msg, out, true); // Send out the message 76 | out << "," // End the message netstring 77 | << env // The envelope is already encoded 78 | << ","; // End the "outer" netstring 79 | if(!out.flush()) 80 | protocol_fail(ERR_MSG_WRITE, "Error sending message to remote"); 81 | mystring response; 82 | if(!in.getnetstring(response)) 83 | protocol_fail(ERR_PROTO, "Response from remote was not a netstring"); 84 | switch(response[0]) { 85 | case 'K': protocol_succ(response.c_str()+1); break; 86 | case 'Z': protocol_fail(ERR_MSG_TEMPFAIL, response.c_str()+1); break; 87 | case 'D': protocol_fail(ERR_MSG_PERMFAIL, response.c_str()+1); break; 88 | default: protocol_fail(ERR_PROTO, "Invalid status byte in response"); 89 | } 90 | } 91 | 92 | bool compute_size(fdibuf& msg, unsigned long& size) 93 | { 94 | char buf[4096]; 95 | size = 0; 96 | while(msg.read(buf, 4096)) 97 | size += msg.last_count(); 98 | if(msg.eof()) 99 | size += msg.last_count(); 100 | return size > 0; 101 | } 102 | 103 | bool make_envelope(fdibuf& msg, mystring& env) 104 | { 105 | mystring tmp; 106 | while(msg.getline(tmp)) { 107 | if(!tmp) 108 | return true; 109 | env += str2net(tmp); 110 | } 111 | return false; 112 | } 113 | 114 | bool preload_data(fdibuf& msg, unsigned long& size, mystring& env) 115 | { 116 | return make_envelope(msg, env) && 117 | compute_size(msg, size); 118 | } 119 | 120 | static unsigned long msg_size; 121 | static mystring msg_envelope; 122 | 123 | void protocol_prep(fdibuf& in) 124 | { 125 | if(!preload_data(in, msg_size, msg_envelope)) 126 | protocol_fail(ERR_MSG_READ, "Error reading message"); 127 | } 128 | 129 | void protocol_starttls(fdibuf& netin, fdobuf& netout) 130 | { 131 | protocol_fail(ERR_USAGE, "QMQP does not support STARTTLS"); 132 | (void)netin; 133 | (void)netout; 134 | } 135 | 136 | void protocol_send(fdibuf& in, fdibuf& netin, fdobuf& netout) 137 | { 138 | alarm(60*60); // Connection must close after an hour 139 | qmqp conn(netin, netout); 140 | conn.send(in, msg_size, msg_envelope); 141 | } 142 | -------------------------------------------------------------------------------- /protocols/tls_none.cc: -------------------------------------------------------------------------------- 1 | // nullmailer -- a simple relay-only MTA 2 | // Copyright (C) 2018 Bruce Guenter 3 | // 4 | // This program is free software; you can redistribute it and/or modify 5 | // it under the terms of the GNU General Public License as published by 6 | // the Free Software Foundation; either version 2 of the License, or 7 | // (at your option) any later version. 8 | // 9 | // This program is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU General Public License 15 | // along with this program; if not, write to the Free Software 16 | // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 17 | // 18 | // You can contact me at . There is also a mailing list 19 | // available to discuss this package. To subscribe, send an email to 20 | // . 21 | 22 | #include "errcodes.h" 23 | #include "protocol.h" 24 | 25 | void tls_init(const char* remote) 26 | { 27 | protocol_fail(ERR_USAGE, "SSL/TLS not supported in this build"); 28 | (void)remote; 29 | } 30 | 31 | void tls_send(fdibuf& in, int fd) 32 | { 33 | protocol_fail(ERR_USAGE, "SSL/TLS not supported in this build"); 34 | (void)in; 35 | (void)fd; 36 | } 37 | -------------------------------------------------------------------------------- /scripts/nullmailer-log.run: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | exec setuidgid nullmail multilog t /var/log/nullmailer 3 | -------------------------------------------------------------------------------- /scripts/nullmailer.run: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | exec 2>&1 3 | exec setuidgid nullmail /usr/sbin/nullmailer-send 4 | -------------------------------------------------------------------------------- /scripts/nullmailer.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Nullmailer relay-only MTA 3 | Requires=network.target 4 | After=local-fs.target 5 | ConditionPathExists=/var/spool/nullmailer/queue 6 | ConditionPathExists=/etc/nullmailer/defaultdomain 7 | ConditionPathExists=/etc/nullmailer/me 8 | 9 | [Service] 10 | WorkingDirectory=/var/spool/nullmailer 11 | ExecStart=/usr/sbin/nullmailer-send 12 | User=nullmail 13 | Group=nullmail 14 | Restart=always 15 | 16 | [Install] 17 | WantedBy=multi-user.target 18 | -------------------------------------------------------------------------------- /spec: -------------------------------------------------------------------------------- 1 | Name: nullmailer 2 | Summary: Simple relay-only mail transport agent 3 | Version: @VERSION@ 4 | Release: 1 5 | License: GPL 6 | Group: Networking/Daemons 7 | Source: http://untroubled.org/nullmailer/archive/%{version}/nullmailer-%{version}.tar.gz 8 | BuildRoot: /tmp/nullmailer-root 9 | URL: http://untroubled.org/nullmailer/ 10 | Packager: Bruce Guenter 11 | Provides: smtpdaemon 12 | Conflicts: sendmail 13 | Conflicts: qmail 14 | Requires: supervise-scripts >= 3.2 15 | Requires: gnutls 16 | BuildRequires: gnutls-devel 17 | Requires(pre,preun): shadow-utils 18 | 19 | %description 20 | Nullmailer is a mail transport agent designed to only relay all its 21 | messages through a fixed set of "upstream" hosts. It is also designed 22 | to be secure. 23 | 24 | %prep 25 | %setup 26 | 27 | %build 28 | CFLAGS="$RPM_OPT_FLAGS" CXXFLAGS="$RPM_OPT_FLAGS" \ 29 | ./configure --prefix=/usr --sysconfdir=/etc --localstatedir=/var --enable-tls 30 | 31 | make 32 | 33 | %install 34 | rm -fr $RPM_BUILD_ROOT 35 | mkdir -p $RPM_BUILD_ROOT/etc 36 | mkdir -p $RPM_BUILD_ROOT/usr/lib 37 | mkdir -p $RPM_BUILD_ROOT/var/service/nullmailer/log 38 | mkdir -p $RPM_BUILD_ROOT/var/log/nullmailer 39 | 40 | make DESTDIR=$RPM_BUILD_ROOT install-strip 41 | ln -s ../sbin/sendmail $RPM_BUILD_ROOT/usr/lib/sendmail 42 | install scripts/nullmailer.run $RPM_BUILD_ROOT/var/service/nullmailer/run 43 | install scripts/nullmailer-log.run $RPM_BUILD_ROOT/var/service/nullmailer/log/run 44 | 45 | %clean 46 | rm -rf $RPM_BUILD_ROOT 47 | 48 | %pre 49 | PATH="/sbin:/usr/sbin:$PATH" export PATH 50 | if [ "$1" = 1 ]; then 51 | # pre-install instructions 52 | grep ^nullmail: /etc/group >/dev/null || groupadd -r nullmail 53 | grep ^nullmail: /etc/passwd >/dev/null || useradd -d /var/lock/svc/nullmailer -g nullmail -M -r -s /bin/true nullmail 54 | fi 55 | 56 | %post 57 | if ! [ -L /service/nullmailer ]; then 58 | svc-add /var/service/nullmailer 59 | fi 60 | if ! [ -s /etc/nullmailer/me ]; then 61 | /bin/hostname --fqdn >/etc/nullmailer/me 62 | fi 63 | if ! [ -s /etc/nullmailer/defaultdomain ]; then 64 | /bin/hostname --domain >/etc/nullmailer/defaultdomain 65 | fi 66 | 67 | %preun 68 | if [ "$1" = 0 ]; then 69 | svc-remove nullmailer 70 | fi 71 | 72 | %postun 73 | if [ "$1" = 0 ]; then 74 | # post-erase instructions 75 | /usr/sbin/userdel nullmail 76 | /usr/sbin/groupdel nullmail 77 | fi 78 | 79 | %files 80 | %defattr(-,nullmail,nullmail) 81 | %doc AUTHORS BUGS ChangeLog COPYING INSTALL NEWS README TODO doc/DIAGRAM 82 | %dir /etc/nullmailer 83 | %attr(04711,nullmail,nullmail) /usr/bin/mailq 84 | /usr/bin/nullmailer-inject 85 | /usr/bin/nullmailer-smtpd 86 | /usr/lib/sendmail 87 | %dir /usr/libexec/nullmailer 88 | /usr/libexec/nullmailer/* 89 | %{_mandir}/*/* 90 | %attr(04711,nullmail,nullmail) /usr/sbin/nullmailer-queue 91 | /usr/sbin/nullmailer-send 92 | /usr/sbin/sendmail 93 | %dir /var/log/nullmailer 94 | /var/service/nullmailer 95 | /var/spool/nullmailer 96 | -------------------------------------------------------------------------------- /src/Makefile.am: -------------------------------------------------------------------------------- 1 | bin_PROGRAMS = \ 2 | mailq \ 3 | nullmailer-dsn \ 4 | nullmailer-inject \ 5 | nullmailer-smtpd 6 | sbin_PROGRAMS = \ 7 | nullmailer-queue \ 8 | nullmailer-send \ 9 | sendmail 10 | 11 | #noinst_PROGRAMS = address 12 | 13 | AM_CPPFLAGS = -I$(top_srcdir)/lib 14 | 15 | mailq_SOURCES = mailq.cc 16 | mailq_LDADD = ../lib/libnullmailer.a 17 | 18 | nullmailer_dsn_SOURCES = dsn.cc 19 | nullmailer_dsn_LDADD = ../lib/libnullmailer.a ../lib/cli++/libcli++.a 20 | 21 | nullmailer_inject_SOURCES = inject.cc 22 | nullmailer_inject_LDADD = ../lib/libnullmailer.a ../lib/cli++/libcli++.a 23 | 24 | nullmailer_queue_SOURCES = queue.cc 25 | nullmailer_queue_LDADD = ../lib/libnullmailer.a ../lib/cli++/libcli++.a 26 | 27 | nullmailer_send_SOURCES = send.cc 28 | nullmailer_send_LDADD = ../lib/libnullmailer.a ../lib/cli++/libcli++.a 29 | 30 | nullmailer_smtpd_SOURCES = smtpd.cc 31 | nullmailer_smtpd_LDADD = ../lib/libnullmailer.a 32 | 33 | sendmail_SOURCES = sendmail.cc 34 | sendmail_LDADD = ../lib/cli++/libcli++.a ../lib/libnullmailer.a 35 | 36 | -------------------------------------------------------------------------------- /src/address-main.cc: -------------------------------------------------------------------------------- 1 | #include "config.h" 2 | #include "mystring.h" 3 | #include "fdbuf.h" 4 | #include "address.h" 5 | 6 | int main(int argc, char* argv[]) 7 | { 8 | for(int i = 1; i < argc; i++) { 9 | mystring s(argv[i]); 10 | mystring l; 11 | if(!parse_addresses(s, l)) { 12 | fout.writeln("Parsing failed."); 13 | } 14 | else { 15 | fout.write(l); 16 | fout.write("To: "); 17 | fout.writeln(s); 18 | } 19 | } 20 | return 0; 21 | } 22 | -------------------------------------------------------------------------------- /src/mailq.cc: -------------------------------------------------------------------------------- 1 | // nullmailer -- a simple relay-only MTA 2 | // Copyright (C) 2018 Bruce Guenter 3 | // 4 | // This program is free software; you can redistribute it and/or modify 5 | // it under the terms of the GNU General Public License as published by 6 | // the Free Software Foundation; either version 2 of the License, or 7 | // (at your option) any later version. 8 | // 9 | // This program is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU General Public License 15 | // along with this program; if not, write to the Free Software 16 | // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 17 | // 18 | // You can contact me at . There is also a mailing list 19 | // available to discuss this package. To subscribe, send an email to 20 | // . 21 | 22 | #include "config.h" 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include "configio.h" 29 | #include "defines.h" 30 | #include "fdbuf/fdbuf.h" 31 | #include "itoa.h" 32 | #include "mystring/mystring.h" 33 | 34 | #define fail(X) do{ fout << X << endl; return 1; }while(0) 35 | 36 | int main(int, char*[]) 37 | { 38 | mystring line; 39 | 40 | mystring msg_dir = CONFIG_PATH(QUEUE, NULL, "queue"); 41 | if(chdir(msg_dir.c_str())) 42 | fail("Cannot change directory to queue."); 43 | DIR* dir = opendir("."); 44 | if(!dir) 45 | fail("Cannot open queue directory."); 46 | struct dirent* entry; 47 | while((entry = readdir(dir)) != 0) { 48 | const char* name = entry->d_name; 49 | if(name[0] == '.') 50 | continue; 51 | time_t time = atoi(name); 52 | char timebuf[100]; 53 | strftime(timebuf, 100, "%Y-%m-%d %H:%M:%S ", localtime(&time)); 54 | fout << timebuf; 55 | struct stat statbuf; 56 | if(stat(name, &statbuf) == -1) 57 | fout << "?????"; 58 | else 59 | fout << itoa(statbuf.st_size); 60 | fout << " bytes"; 61 | fdibuf in(name); 62 | if (in.getline(line)) { 63 | fout << " from <" << line << '>'; 64 | while (in.getline(line) && !!line) 65 | fout << "\n to <" << line << '>'; 66 | } 67 | fout << endl; 68 | } 69 | closedir(dir); 70 | return 0; 71 | } 72 | -------------------------------------------------------------------------------- /test/Makefile.am: -------------------------------------------------------------------------------- 1 | noinst_PROGRAMS = address-test argparse-test clitest0 clitest1 2 | EXTRA_DIST = address-trace.cc clitest.cc clitest.sh functions.in runtests \ 3 | accept-qmqp.sh accept-smtp.sh authtest-smtp.sh 4 | noinst_SCRIPTS = functions 5 | CLEANFILES = functions 6 | 7 | AM_CPPFLAGS = -I$(top_srcdir)/lib 8 | 9 | address_test_SOURCES = address-test.cc # address-trace.cc 10 | address_test_LDADD = ../lib/libnullmailer.a 11 | 12 | argparse_test_SOURCES = argparse-test.cc 13 | argparse_test_LDADD = ../lib/libnullmailer.a 14 | 15 | clitest0_CPPFLAGS = $(AM_CPPFLAGS) -DCLI_ONLY_LONG=false 16 | clitest0_SOURCES = clitest.cc 17 | clitest0_LDADD = ../lib/libnullmailer.a ../lib/cli++/libcli++.a 18 | 19 | clitest1_CPPFLAGS = $(AM_CPPFLAGS) -DCLI_ONLY_LONG=true 20 | clitest1_SOURCES = clitest.cc 21 | clitest1_LDADD = ../lib/libnullmailer.a ../lib/cli++/libcli++.a 22 | 23 | functions: functions.in Makefile 24 | sed -e 's,[@]SRCDIR[@],$(abs_top_srcdir),g; s,[@]BUILDDIR[@],$(abs_top_builddir),g;' < $< > $@ 25 | 26 | # The following makes sure that we can't produce a package without the 27 | # tests executing properly 28 | dist-hook: 29 | cp -r $(srcdir)/tests $(distdir) 30 | 31 | check: all 32 | ./address-test 33 | ./argparse-test 34 | sh $(srcdir)/clitest.sh 35 | $(srcdir)/runtests `find $(abs_srcdir)/tests -type f -not -name '.*'` 36 | -------------------------------------------------------------------------------- /test/accept-qmqp.sh: -------------------------------------------------------------------------------- 1 | echo '3:KOK,' 2 | -------------------------------------------------------------------------------- /test/accept-smtp.sh: -------------------------------------------------------------------------------- 1 | echo 250-domain.com 2 | echo 250-8BITMIME 3 | echo 250-ENHANCEDSTATUSCODES 4 | echo 250-SIZE 36700160 5 | echo 250-DSN 6 | echo 250-AUTH PLAIN LOGIN 7 | echo 250 OK 8 | echo 250 OK 9 | echo 250 OK 10 | echo 250 OK 11 | echo 351 OK 12 | echo 220 OK 13 | sleep 1 14 | -------------------------------------------------------------------------------- /test/address-trace.cc: -------------------------------------------------------------------------------- 1 | #define TRACE 2 | #include "address.cc" 3 | -------------------------------------------------------------------------------- /test/argparse-test.cc: -------------------------------------------------------------------------------- 1 | #include "argparse.h" 2 | 3 | #include "fdbuf/fdbuf.h" 4 | #include "itoa.h" 5 | 6 | static bool doit(const char* teststr, unsigned count, const char** result) 7 | { 8 | arglist args; 9 | unsigned c = parse_args(args, teststr); 10 | if (c != count) { 11 | fout << "Parsing of \"" << teststr << "\" failed, wrong count, was: " << c << " should be: " << count << endl; 12 | return false; 13 | } 14 | arglist::const_iter iter(args); 15 | for (unsigned i = 0; i < c; i++, iter++) { 16 | if (*iter != result[i]) { 17 | fout << "Parsing of \"" << teststr << "\" failed, wrong string, was:\n" 18 | << *iter << " should be:\n" << result[i] << endl; 19 | return false; 20 | } 21 | } 22 | return true; 23 | } 24 | 25 | #define TEST(X,Y,...) do{ const char* result[] = {__VA_ARGS__}; ++count; if (!doit(X, Y, result)) ++failed; } while(0) 26 | 27 | int main() 28 | { 29 | int count = 0; 30 | int failed = 0; 31 | 32 | TEST("", 0); 33 | 34 | TEST("one", 1, "one"); // Simple case 35 | TEST(" two", 1, "two"); // Leading space 36 | TEST("three ", 1, "three"); // Trailing space 37 | TEST("one\\ two", 1, "one two"); // Escaped internal space 38 | TEST(" one two three ", 3, "one","two","three"); // Spaces between args 39 | TEST("'one'", 1, "one"); // Simple single quoted 40 | TEST("'one two'", 1, "one two"); // Single quoted with space 41 | TEST("'one\\'two", 1, "one\\two"); // Single quoted with backslash 42 | TEST("\"one two\"", 1, "one two"); // Double quoted 43 | TEST(" one \"two\"three four", 3, "one", "twothree", "four"); // Mixed quoted and unquoted 44 | TEST("\"one\\\" two\"", 1, "one\" two"); // Double quoted with escaped quote 45 | TEST("one='two three' four", 2, "one=two three", "four"); // Single quotes within an arg with multiple args 46 | TEST("one=\"two three\" four", 2, "one=two three", "four"); // Double quotes within an arg with multiple args 47 | 48 | fout << itoa(count) << " tests run, "; 49 | fout << itoa(failed) << " failed." << endl; 50 | return failed; 51 | } 52 | -------------------------------------------------------------------------------- /test/authtest-smtp.sh: -------------------------------------------------------------------------------- 1 | echo 250 ME 2 | read line 3 | echo 250-ME 4 | echo 250-AUTH PLAIN 5 | echo 250 OK 6 | read line 7 | cat $1 8 | read line 9 | echo 250 OK 10 | read line 11 | echo 250 OK 12 | read line 13 | echo 351 OK 14 | read line 15 | echo 220 OK 16 | -------------------------------------------------------------------------------- /test/clitest.cc: -------------------------------------------------------------------------------- 1 | #include "cli++/cli++.h" 2 | #include "fdbuf/fdbuf.h" 3 | 4 | const char* cli_program = "clitest"; 5 | const char* cli_help_prefix = "Nullmailer CLI test harness\n"; 6 | const char* cli_help_suffix = 0; 7 | const char* cli_args_usage = "[args]"; 8 | const int cli_args_min = 0; 9 | const int cli_args_max = -1; 10 | const bool cli_only_long = CLI_ONLY_LONG; 11 | 12 | static int a = 0; 13 | static int b = 0; 14 | static const char* c = 0; 15 | static const char* d = 0; 16 | 17 | cli_option cli_options[] = { 18 | { 'a', 0, cli_option::flag, 1, &a, "Test flag", 0 }, 19 | { 0, "bb", cli_option::flag, 1, &b, "Test flag", 0 }, 20 | { 'c', 0, cli_option::string, 1, &c, "Test string", 0 }, 21 | { 0, "dd", cli_option::string, 1, &d, "Test string", 0 }, 22 | CLI_OPTION_END 23 | }; 24 | 25 | static void showcstr(const char* name, const char* value) 26 | { 27 | fout << ' ' << name; 28 | if (value == NULL) 29 | fout << "=NULL"; 30 | else 31 | fout << "=\"" << value << "\""; 32 | } 33 | 34 | int cli_main(int argc, char* argv[]) 35 | { 36 | fout << "argc=" << argc 37 | << " a=" << a 38 | << " b=" << b; 39 | showcstr("c", c); 40 | showcstr("d", d); 41 | fout << endl; 42 | for (int i = 0; i < argc; i++) 43 | fout << "argv[" << i << "]=\"" << argv[i] << "\"" << endl; 44 | return 0; 45 | } 46 | -------------------------------------------------------------------------------- /test/clitest.sh: -------------------------------------------------------------------------------- 1 | tmp=$PWD/clitest.tmp 2 | trap 'rm -f ${tmp}*' EXIT 3 | 4 | testit() { 5 | test "x$1" = 'x!' && isfailed='-eq 0' || isfailed='-ne 0' 6 | "$@" >$tmp.out 2>/dev/null 7 | if [ $? $isfailed ] 8 | then 9 | echo "Test \"$*\" failed!" 10 | exit 1 11 | fi 12 | if [ "x$1" != 'x!' ] 13 | then 14 | cat >$tmp.exp 15 | if ! diff -u $tmp.exp $tmp.out 16 | then 17 | echo "Test \"$*\" failed!" 18 | exit 1 19 | fi 20 | fi 21 | } 22 | 23 | testit ./clitest0 < /dev/null 2>&1 || : 21 | # Remove all temporary files on success 22 | if [ $exit -eq 0 ] 23 | then 24 | wait 25 | rm -rf $tmpdir 26 | fi 27 | } 28 | trap exit_cleanup EXIT 29 | 30 | fail() { 31 | echo "$*" 32 | exit 1 33 | } 34 | start() { 35 | local name=$1 36 | shift 37 | mkdir -p $tmpdir/service/$name 38 | { 39 | echo '#!/bin/sh' 40 | echo exec "$@" 41 | } >$tmpdir/service/$name/run 42 | chmod +x $tmpdir/service/$name/run 43 | supervise $tmpdir/service/$name $tmpdir/service/${name}-log 2>&1 & 44 | sleep 1 45 | } 46 | stop() { 47 | for name in $*; do 48 | svc -dx $tmpdir/service/$name >/dev/null 2>&1 49 | wait 50 | done 51 | } 52 | catch-port() { 53 | local name=$1 54 | port= 55 | while [ -z "$port" ] 56 | do 57 | port=$( head -n 1 $tmpdir/service/${name}-log ) 58 | done 59 | } 60 | 61 | #not() { if "$@"; then return 1; else return 0; fi } 62 | not() { ! safe "$@"; } 63 | safe() { set +e; "$@"; result=$?; set -e; return $result; } 64 | error() { 65 | local code=$1 66 | shift 67 | if "$@"; then 68 | echo "Result was 0, should be $code." 69 | return 1 70 | else 71 | result=$? 72 | if test $result -eq $code; then 73 | return 0 74 | else 75 | echo "Result was $result, should be $code." 76 | return 1 77 | fi 78 | fi 79 | } 80 | 81 | inject() { ../src/nullmailer-inject "$@"; } 82 | mailq() { ../src/mailq "$@"; } 83 | smtpd() { ../src/nullmailer-smtpd "$@"; } 84 | queue() { 85 | ( 86 | echo "$1" 87 | shift 88 | while [ x"$1" != x ] 89 | do 90 | echo "$1" 91 | shift 92 | done 93 | echo 94 | echo "Subject: test" 95 | ) | ../src/nullmailer-queue > $tmpdir/queue-out 2> $tmpdir/queue-err 96 | } 97 | injectlines() { 98 | for line in "$@"; do 99 | echo "$line" 100 | done | inject -n 101 | return $? 102 | } 103 | injectfield() { 104 | local field=$1 105 | shift 106 | injectlines "$@" | grep -i "^$field:" | cut -d: -f2- 107 | } 108 | protocol() { 109 | local p=$1 110 | local opts="" 111 | shift 112 | while [ $# -gt 0 -a x"$1" != x-- ] 113 | do 114 | opts="$opts $1" 115 | shift 116 | done 117 | shift || : 118 | for line in "$@" 119 | do 120 | echo "$line" 121 | done > $tmpdir/protocol-in 122 | ../protocols/$p $opts < $tmpdir/protocol-in > $tmpdir/protocol-log 2>&1 123 | } 124 | 125 | # Split an input on blank lines 126 | splitblank() { 127 | local fn=$1 128 | local n=1 129 | while read line 130 | do 131 | if [ x"$line" = x ] 132 | then 133 | n=$(( $n + 1 )) 134 | else 135 | echo "$line" >> ${fn}.$n 136 | fi 137 | done 138 | } 139 | 140 | make-testmail() { 141 | testmail=$tmpdir/testmail 142 | rm -f $testmail 143 | cat >$testmail < 148 | To: 149 | Subject: Nullmailer automated test message 150 | 151 | Just testing, please ignore 152 | EOF 153 | } 154 | 155 | export PATH=/bin:/usr/bin:/usr/local/bin 156 | rm -f $SYSCONFDIR/* 157 | echo f.q.d.n >$SYSCONFDIR/me 158 | echo q.d.n >$SYSCONFDIR/defaultdomain 159 | set -e 160 | -------------------------------------------------------------------------------- /test/runtests: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | failed=0 5 | for test in $* 6 | do 7 | echo Running test $test... 8 | if env - bash $test 9 | then 10 | echo 'Done.' 11 | else 12 | echo '******************************Failed!******************************' 13 | failed=$(( $failed + 1 )) 14 | fi 15 | done 2>&1 16 | echo "$# tests run, $failed failed" 17 | exit $failed 18 | -------------------------------------------------------------------------------- /test/tests/dsn: -------------------------------------------------------------------------------- 1 | . functions 2 | 3 | msgid=message.id.$$.$RANDOM@f.q.d.n 4 | dsn() { 5 | $builddir/src/nullmailer-dsn "$@" << EOF 6 | sender@example.com 7 | recip1@example.net 8 | recip2@example.org 9 | 10 | Subject: test message 11 | Message-Id: <$msgid> 12 | From: 13 | To: 14 | Cc: 15 | 16 | This is a test. 17 | line 2 18 | line 3 19 | line 4 20 | EOF 21 | } 22 | 23 | fn=$tmpdir/dsn-tmp 24 | dsn 5.2.9 | splitblank $fn 25 | 26 | echo -n Testing envelope: sender 27 | not test -x $fn.1 28 | echo -n , recipient 29 | grep -qx sender@example.com $fn.2 30 | echo -n , end 31 | test $( wc -l < $fn.2 ) = 1 32 | echo 33 | 34 | date='..., [ 0-9][0-9] ... [0-9]{4} [0-9][0-9]:[0-9][0-9]:[0-9][0-9] [+-][0-9]{4}' 35 | echo -n Testing header: from 36 | grep -qx 'From: Message Delivery Subsystem ' $fn.3 37 | echo -n , to 38 | grep -qx 'To: ' $fn.3 39 | echo -n , subject 40 | grep -qx 'Subject: Returned mail: Could not send message' $fn.3 41 | echo -n , date 42 | egrep -q "^Date: $date$" $fn.3 43 | echo -n , message-id 44 | grep -q '^Message-Id: <.*.nullmailer@f.q.d.n>$' $fn.3 45 | echo -n , mime 46 | grep -qx 'MIME-Version: 1.0' $fn.3 47 | echo -n , content-type 48 | grep -q '^Content-Type: multipart/report; report-type=delivery-status;' $fn.3 49 | echo 50 | 51 | echo -n Testing report header: reporting-mta 52 | grep -qx "Reporting-MTA: x-local-hostname; f.q.d.n" $fn.8 53 | echo -n , date 54 | egrep -q "^Arrival-Date: $date$" $fn.8 55 | echo 56 | 57 | echo -n Testing recipient report 1: final-recipient 58 | grep -qx 'Final-Recipient: rfc822; recip1@example.net' $fn.9 59 | echo -n , action 60 | grep -qx 'Action: failed' $fn.9 61 | echo -n , status 62 | grep -qx 'Status: 5.2.9' $fn.9 63 | echo -n , date 64 | egrep -q "^Last-Attempt-Date: $date$" $fn.9 65 | echo 66 | 67 | echo -n Testing recipient report 2: final-recipient 68 | grep -qx 'Final-Recipient: rfc822; recip2@example.org' $fn.10 69 | echo -n , action 70 | grep -qx 'Action: failed' $fn.10 71 | echo -n , status 72 | grep -qx 'Status: 5.2.9' $fn.10 73 | echo -n , date 74 | egrep -q "^Last-Attempt-Date: $date$" $fn.10 75 | echo 76 | 77 | echo Testing quoted message pre-header 78 | grep -qx 'Content-Type: message/rfc822' $fn.11 79 | 80 | echo Testing quoted message header 81 | grep -qx "Message-Id: <$msgid>" $fn.12 82 | 83 | echo Testing quoted message body 84 | not grep -qx "This is a test." $fn.13 85 | 86 | echo Testing fixed bounce address 87 | echo admin@example.com > $SYSCONFDIR/bounceto 88 | dsn 5.2.9 | splitblank $fn 89 | grep -qx admin@example.com $fn.2 90 | 91 | echo -n Testing max-lines: 0 92 | dsn --max-lines=0 5.2.9 | not grep -qx "This is a test." 93 | echo -n , 1 94 | dsn --max-lines=1 5.2.9 | grep -qx "This is a test." 95 | dsn --max-lines=1 5.2.9 | not grep -qx "line 2" 96 | echo -n , 2 97 | dsn --max-lines=2 5.2.9 | grep -qx "line 2" 98 | dsn --max-lines=2 5.2.9 | not grep -qx "line 3" 99 | echo -n , bouncelines=1 100 | echo 1 > $SYSCONFDIR/bouncelines 101 | dsn 5.2.9 | grep -qx "This is a test." 102 | dsn 5.2.9 | not grep -qx "line 2" 103 | echo -n , override 104 | dsn --max-lines=2 5.2.9 | grep -qx "line 2" 105 | dsn --max-lines=2 5.2.9 | not grep -qx "line 3" 106 | echo 107 | 108 | rm -f $fn.* $SYSCONFDIR/bounceto $SYSCONFDIR/bouncelines 109 | -------------------------------------------------------------------------------- /test/tests/inject/bad-headers: -------------------------------------------------------------------------------- 1 | . functions 2 | 3 | injtest() { echo "$@" | not inject >/dev/null; } 4 | 5 | echo "Checking that inject rejects leading continuation lines." 6 | injtest " foo..." 7 | 8 | echo "Checking that inject rejects malformed RFC headers." 9 | injtest "foo : bar" 10 | injtest ":foo bar" 11 | injtest "foo" 12 | 13 | echo "Checking that inject rejects bad addresses." 14 | injtest "from: foo/dev/null 10 | 11 | echo "Checking that inject does not add more date lines." 12 | test 1 -eq `inj "date: foo" | wc -l` 13 | -------------------------------------------------------------------------------- /test/tests/inject/from: -------------------------------------------------------------------------------- 1 | . functions 2 | 3 | inj() { injectfield from 'to: nobody' "$@"; } 4 | testvar() { 5 | set -e 6 | echo "Checking that inject obeys $1." 7 | export $1="$2" 8 | inj | grep -q "$3" 9 | } 10 | 11 | echo "Checking that inject inserts a from line." 12 | test -n "`inj`" 13 | 14 | echo "Checking that inject preserves an existing from line." 15 | inj "from: a@b.c" | grep -q '^ a@b\.c$' 16 | 17 | echo "Checking that inject does not add more from lines." 18 | test 1 -eq `inj "from: a@b.c" | wc -l` 19 | 20 | echo "Checking that inject will strip from lines." 21 | export NULLMAILER_FLAGS=f 22 | inj "from: a@b.c" | not grep -q '^ a@b\.c$' 23 | unset NULLMAILER_FLAGS 24 | 25 | echo "Checking that inject obeys me" 26 | rm -f $SYSCONFDIR/default* 27 | inj | grep -q "@f.q.d.n>$" 28 | 29 | echo "Checking that inject obeys config/defaulthost" 30 | echo test.org >$SYSCONFDIR/defaulthost 31 | inj | grep -q '@test.org>$' 32 | 33 | echo "Checking that inject obeys config/defaultdomain" 34 | echo test >$SYSCONFDIR/defaulthost 35 | echo domain.org >$SYSCONFDIR/defaultdomain 36 | inj | grep -q '@test.domain.org>$' 37 | 38 | echo "Checking that inject ignores config/defaultdomain for localhost" 39 | echo localhost >$SYSCONFDIR/defaulthost 40 | inj | grep -q '@localhost>$' 41 | 42 | testvar HOSTNAME test1.org '@test1.org>$' 43 | 44 | testvar MAILHOST test2.org '@test2.org>$' 45 | 46 | testvar NULLMAILER_HOST test3.org '@test3.org>$' 47 | 48 | echo "Checking that inject uses getpwnam" 49 | inj | grep -q " <`id -un`@" 50 | 51 | testvar LOGNAME name1 ' ' 6 | do 7 | echo "Checking that inject ignores a leading \"${lead}From \" line" 8 | injectfrom "$lead" | egrep -qv '^>?From ' 9 | injectfrom "$lead" | head -n 1 | grep -q '^Subject:' 10 | done 11 | -------------------------------------------------------------------------------- /test/tests/inject/message-id: -------------------------------------------------------------------------------- 1 | . functions 2 | 3 | inj() { injectfield message-id 'to: n' "$@"; } 4 | 5 | echo "Checking that inject inserts a message-id." 6 | test -n "`inj`" 7 | 8 | echo "Checking that inject preserves an existing message-id." 9 | inj "Message-Id: " | grep -q '^ $' 10 | 11 | echo "Checking that inject does not add more message-ids." 12 | test 1 -eq `inj "Message-Id: " | wc -l` 13 | 14 | echo "Checking that inject will ignore an existing message-id." 15 | export NULLMAILER_FLAGS=i 16 | inj "Message-Id: " | not grep -q '^ $' 17 | 18 | echo "Checking that inject obeys me" 19 | inj | grep -q "@f.q.d.n>$" 20 | 21 | echo "Checking that inject obeys config/idhost" 22 | echo test1.org >$SYSCONFDIR/idhost 23 | inj | grep -q '@test1.org>$' 24 | -------------------------------------------------------------------------------- /test/tests/inject/queue: -------------------------------------------------------------------------------- 1 | . functions 2 | 3 | echo 'Testing that inject queues messages properly.' 4 | 5 | echo 'To: nobody' | inject 6 | test $( ls $QUEUEDIR/queue | wc -l ) = 1 7 | egrep -i '^to: *nobody' $QUEUEDIR/queue/* >/dev/null 8 | 9 | echo 'Testing that inject honors $NULLMAILER_QUEUE.' 10 | 11 | rm -f $QUEUEDIR/queue/* 12 | export NULLMAILER_QUEUE=/bin/cat 13 | echo 'To: nobody' | inject >/dev/null 14 | test $( ls $QUEUEDIR/queue | wc -l ) = 0 15 | unset NULLMAILER_QUEUE 16 | -------------------------------------------------------------------------------- /test/tests/inject/recips: -------------------------------------------------------------------------------- 1 | . functions 2 | 3 | inj() { inject -n -v "$@" | tail -n +2 | sed '/^$/,$d'; } 4 | inj-find() { echo to: a@b.c | inj "$1" d@e.f | grep -q "$2"; } 5 | inj-notfind() { echo to: a@b.c | inj "$1" d@e.f | not grep -q "$2"; } 6 | 7 | hdrline='^a@b.c$' 8 | cmdline='^d@e.f$' 9 | 10 | echo "Checking that inject uses command line with -a." 11 | inj-find -a $cmdline 12 | 13 | echo "Checking that inject ignores header lines with -a." 14 | inj-notfind -a $hdrline 15 | 16 | echo "Checking that inject uses command line with -b." 17 | inj-find -b $cmdline 18 | 19 | echo "Checking that inject uses header lines with -b." 20 | inj-find -b $hdrline 21 | 22 | echo "Checking that inject ignores command line with -h." 23 | inj-notfind -h $cmdline 24 | 25 | echo "Checking that inject uses header lines with -h." 26 | inj-find -h $hdrline 27 | 28 | echo "Checking that inject uses command line with -e and no header." 29 | echo | inj -e d@e.f | grep -q $cmdline 30 | 31 | echo "Checking that inject uses command line with -e and header." 32 | inj-find -e $cmdline 33 | 34 | echo "Checking that inject uses header with -e and no command line." 35 | echo to: a@b.c | inj -e | grep -q $hdrline 36 | 37 | echo "Checking that inject ignores header with -e and command line." 38 | inj-notfind -e $hdrline 39 | 40 | echo "Checking that inject uses command line with no header by default." 41 | echo | inj -e d@e.f | grep -q $cmdline 42 | 43 | echo "Checking that inject uses command line with header by default." 44 | inj-find -e $cmdline 45 | 46 | echo "Checking that inject uses header with no command line by default." 47 | echo to: a@b.c | inj -e | grep -q $hdrline 48 | 49 | echo "Checking that inject ignores header with command line by default." 50 | inj-notfind -e $hdrline 51 | -------------------------------------------------------------------------------- /test/tests/inject/return-path: -------------------------------------------------------------------------------- 1 | . functions 2 | 3 | inj() { injectfield return-path 'to: n' "$@"; } 4 | 5 | echo "Checking that inject does not inserts a return-path." 6 | test -z "`inj`" 7 | 8 | echo "Checking that inject strips return-paths." 9 | test -z "`inj return-path: blah`" 10 | -------------------------------------------------------------------------------- /test/tests/inject/sender: -------------------------------------------------------------------------------- 1 | . functions 2 | 3 | inj() { inject -n -v "$@" a /dev/null | head -n 1; } 4 | testvar() { 5 | set -e 6 | echo "Checking that inject obeys $1." 7 | export $1="$2" 8 | inj | grep -q "$3" 9 | } 10 | testcanon() { 11 | set -e 12 | echo "Checking that inject canonicalizes $1." 13 | export $1="$2" 14 | inj | grep -q "$3" 15 | } 16 | testhdr() { 17 | set -e 18 | echo "Checking that inject $1 $2:" 19 | echo $2: $3 | inject -n -v a 2>/dev/null | head -n 1 | grep -q "$4" 20 | } 21 | testign() { testhdr ignores "$@"; } 22 | testset() { testhdr uses "$@"; } 23 | testfrom() { 24 | echo "Checking that inject honors -f '$1'" 25 | inj -f "$1" | grep -qx "$2" 26 | } 27 | testfrom2() { 28 | testfrom "$1" "$2" 29 | testfrom "<$1>" "$2" 30 | } 31 | 32 | echo "Checking that inject obeys me" 33 | rm -f $SYSCONFDIR/default* 34 | inj | grep -q "@f.q.d.n$" 35 | 36 | echo "Checking that inject obeys config/defaulthost" 37 | echo test.org >$SYSCONFDIR/defaulthost 38 | inj | grep -q '@test.org$' 39 | 40 | echo "Checking that inject obeys config/defaultdomain" 41 | echo test >$SYSCONFDIR/defaulthost 42 | echo domain.org >$SYSCONFDIR/defaultdomain 43 | inj | grep -q '@test.domain.org$' 44 | 45 | echo "Checking that inject ignores config/defaultdomain for localhost" 46 | echo localhost >$SYSCONFDIR/defaulthost 47 | inj | grep -q '@localhost$' 48 | 49 | testvar HOSTNAME test1.org '@test1.org$' 50 | 51 | testcanon HOSTNAME test1 '@test1.domain.org$' 52 | 53 | testvar MAILHOST test2.org '@test2.org$' 54 | 55 | testcanon MAILHOST test2 '@test2.domain.org$' 56 | 57 | testvar NULLMAILER_HOST test3.org '@test3.org$' 58 | 59 | testcanon NULLMAILER_HOST test3 '@test3.domain.org$' 60 | 61 | echo "Checking that inject uses getpwnam" 62 | inj | grep -q "^`id -un`@" 63 | 64 | testvar LOGNAME name1 '^name1@' 65 | 66 | testvar USER name2 '^name2@' 67 | 68 | testvar MAILUSER name3 '^name3@' 69 | 70 | testvar NULLMAILER_USER name4 '^name4@' 71 | 72 | testign Errors-To a@b.c '^name4@test3' 73 | testign From a@b.c '^name4@test3' 74 | testign Reply-To a@b.c '^name4@test3' 75 | testign Resent-From a@b.c '^name4@test3' 76 | testign Resent-Reply-To a@b.c '^name4@test3' 77 | testign Resent-Sender a@b.c '^name4@test3' 78 | testign Return-Receipt-To a@b.c '^name4@test3' 79 | testign Sender a@b.c '^name4@test3' 80 | testset Return-Path name0@host0.org '^name0@host0.org$' 81 | export NULLMAILER_FLAGS=s 82 | testign Return-Path name0@host0.org '^name4@test3' 83 | 84 | testfrom2 '' '' 85 | testfrom2 'a@b.c' 'a@b.c' 86 | testfrom2 'a@b' 'a@b.domain.org' 87 | -------------------------------------------------------------------------------- /test/tests/mailq: -------------------------------------------------------------------------------- 1 | . functions 2 | 3 | echo "Testing the output of the mailq command" 4 | echo To: nobody@nowhere | inject 5 | 6 | $builddir/src/mailq | head -n 1 | egrep -q '^[0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2} [0-9]+ bytes from <.*>$' \ 7 | || { echo "Header is misformatted."; exit 1; } 8 | 9 | $builddir/src/mailq | tail -n 1 | egrep -q '^ to $' \ 10 | || { echo "Recipient is misformatted."; exit 1; } 11 | -------------------------------------------------------------------------------- /test/tests/protocols: -------------------------------------------------------------------------------- 1 | . functions 2 | export HELOHOST=f.q.d.n 3 | 4 | make-testmail 5 | 6 | for p in smtp qmqp 7 | do 8 | start server "tcpserver -1 ::0 0 sh $srcdir/test/accept-$p.sh" 9 | catch-port server 10 | 11 | echo "Testing protocol success with $p (command-line)" 12 | protocol $p --host=localhost --port=$port 3<$testmail 13 | 14 | echo "Testing protocol success with $p (stdin)" 15 | protocol $p -- host=localhost port=$port 3<$testmail 16 | 17 | echo "Testing protocol success with $p (stdin, IPv6)" 18 | protocol $p -- host=::1 port=$port 3<$testmail 19 | 20 | echo "Testing protocol success with $p (source addr)" 21 | protocol $p -- host=::1 port=$port source=::1 3<$testmail 22 | 23 | echo "Testing protocol failure with $p (bad source addr 1)" 24 | error 19 protocol $p -- host=::1 port=$port source=127.0.0.1 3<$testmail 25 | 26 | echo "Testing protocol failure with $p (bad source addr 2)" 27 | error 19 protocol $p -- host=127.0.0.1 port=$port source=::1 3<$testmail 28 | 29 | stop server 30 | 31 | echo "Testing host not found error with $p." 32 | error 2 protocol $p --host=this.host.can.not.exist 3<$testmail 33 | 34 | echo "Testing connection refused error with $p." 35 | error 7 protocol $p -p $port --host=localhost 3<$testmail 36 | 37 | echo "Testing connection refused error with $p (IPv6)." 38 | error 7 protocol $p -p $port --host=::1 3<$testmail 39 | 40 | echo "Testing usage error with $p (zero args)." 41 | error 1 protocol $p 3<$testmail 42 | 43 | echo "Testing usage error with $p (two args)." 44 | error 1 protocol $p --host=localhost foobar 3<$testmail 45 | 46 | echo "Testing usage error with $p (unknown option)." 47 | error 1 protocol $p -x --host=localhost 3<$testmail 48 | 49 | echo "Testing usage error with $p (invalid integer)." 50 | error 1 protocol $p -p foo --host=localhost 3<$testmail 51 | 52 | start server tcpserver -1 0 0 date 53 | port=$( head -n 1 $tmpdir/service/server-log ) 54 | echo "Testing protocol failure with $p." 55 | error 11 protocol $p -p $port --host=localhost 3<$testmail 56 | stop server 57 | done 58 | 59 | stop server 60 | -------------------------------------------------------------------------------- /test/tests/queue/rewrite: -------------------------------------------------------------------------------- 1 | . functions 2 | 3 | que() { 4 | set -e 5 | ../src/nullmailer-queue && \ 6 | cat $QUEUEDIR/queue/* && \ 7 | rm -f $QUEUEDIR/queue/* 8 | } 9 | que-recip() { 10 | set -e 11 | que | sed -e '2,/^$/!d' | grep -q "$@" 12 | } 13 | que-sender() { 14 | set -e 15 | que | head -n 1 | grep -q "$@" 16 | } 17 | 18 | echo admin@remote >$SYSCONFDIR/adminaddr 19 | 20 | echo "Checking that queue rewrites user@localhost to adminaddr." 21 | que-recip '^admin@remote$' <$SYSCONFDIR/adminaddr 51 | 52 | echo "Checking that queue rewrites to multiple adminaddr." 53 | que-recip '^admin2@remote2$' <$SYSCONFDIR/allmailfrom 74 | que-sender '^sender@remote3$' < to <$2>" 8 | queue "$@" 9 | } 10 | 11 | queue-good() { 12 | if ! queue-test "$@" 13 | then 14 | fail "nullmailer-queue failed to accept good envelope" 15 | fi 16 | qc=$( find $queuedir -type f | wc -l ) 17 | if [ $qc -eq 0 ] 18 | then 19 | fail "nullmailer-queue did not queue a message despite succeeding" 20 | fi 21 | if [ $qc -gt 1 ] 22 | then 23 | fail "nullmailer-queue queued multiple messages ?!?" 24 | fi 25 | qm=$queuedir/* 26 | sed -e '1,/^$/d' $qm | grep -q '^Subject:' || fail "queued message did not contain body" 27 | } 28 | 29 | check-sender() { 30 | qm=$queuedir/* 31 | head -n 1 $qm | grep -q "^$1$" || fail "queued message contained wrong sender" 32 | } 33 | 34 | check-recipient() { 35 | qm=$queuedir/* 36 | sed -e '1d;q' $qm | grep -q "^$1$" || fail "queued message contained wrong sender" 37 | } 38 | 39 | queue-bad() { 40 | if queue-test "$@" 41 | then 42 | fail "nullmailer-queue failed to reject bad envelope" 43 | fi 44 | qc=$( find $queuedir -type f | wc -l ) 45 | if [ $qc -ne 0 ] 46 | then 47 | fail "nullmailer-queue queued a message after rejecting it" 48 | fi 49 | } 50 | 51 | queue-good "a@b.c" "d@e.f" 52 | queue-bad "@b.c" "d@e.f" 53 | queue-bad "a@b.c" "@e.f" 54 | queue-bad "a@b" "d@e.f" 55 | queue-bad "a@b.c" "d@e" 56 | queue-good "" "d@e.f" 57 | -------------------------------------------------------------------------------- /test/tests/send: -------------------------------------------------------------------------------- 1 | . functions 2 | 3 | cat <$tmpdir/protocols/dummy 4 | #!/bin/sh 5 | set -e 6 | read opts 7 | read code 8 | echo "\$opts" | grep -q '^host=' 9 | echo "code=\$code (#5.2.1) \$opts" 10 | exit \$code 11 | EOF 12 | chmod +x $tmpdir/protocols/dummy 13 | 14 | echo 127.0.0.1 smtp >$SYSCONFDIR/remotes 15 | 16 | # Start up the servers 17 | start send $builddir/src/nullmailer-send 18 | 19 | make_message() { 20 | local sender=$1 21 | local recip=$2 22 | msgid=$(date +%s).$$.me 23 | cat <$QUEUEDIR/tmp/$msgid 24 | $sender 25 | $recip 26 | 27 | Subject: test 28 | Message-Id: <$msgid> 29 | 30 | This is just a test. 31 | EOF 32 | mv -f $QUEUEDIR/tmp/$msgid $QUEUEDIR/queue/$msgid 33 | } 34 | 35 | send_message() { 36 | local sender=$1 37 | local recip=$2 38 | shift 2 39 | echo 127.0.0.1 dummy $@ >$SYSCONFDIR/remotes 40 | make_message "$sender" "$recip" 41 | svc -a $tmpdir/service/send 42 | sleep 2 43 | not test -e $QUEUEDIR/queue/$msgid 44 | } 45 | 46 | echo 'Testing sending with a succeeding protocol' 47 | send_message me@example.com me@example.net 0 2.0.0 48 | 49 | echo 'Testing sending with a failing protocol' 50 | not send_message me@example.com me@example.net 1 5.2.2 51 | rm -f $QUEUEDIR/queue/$msgid 52 | 53 | echo 'Testing handling of a permanent failure' 54 | send_message me@example.com me@example.net 33 5.2.2 55 | 56 | echo 'Checking failed message was moved out of the queue' 57 | test -e $QUEUEDIR/failed/$msgid 58 | rm -f $QUEUEDIR/failed/$msgid 59 | 60 | echo 'Checking for a generated bounce message' 61 | # It will also bounce, so look at failed messages 62 | msgid2=$( ls $QUEUEDIR/failed ) 63 | test $( wc -w <<< $msgid2 ) = 1 64 | fn=$QUEUEDIR/failed/$msgid2 65 | 66 | echo 'Checking bounce sender' 67 | head -n 1 $fn | grep -qx '' 68 | 69 | echo 'Checking bounce recipient' 70 | sed -e '1d;q' $fn | grep -qx 'me@example.com' 71 | sed -e '1,2d;q' $fn | grep -qx '' 72 | 73 | echo 'Checking bounce contents' 74 | sed -e '1,3d; /^$/q' $fn | grep -qx 'To: ' 75 | sed -e '1,3d; 1,/^$/d' $fn | grep -qx 'Reporting-MTA: x-local-hostname; f.q.d.n' 76 | sed -e '1,3d; 1,/^$/d' $fn | grep -qx 'Final-Recipient: rfc822; me@example.net' 77 | sed -e '1,3d; 1,/^$/d' $fn | grep -qx 'Action: failed' 78 | sed -e '1,3d; 1,/^$/d' $fn | grep -qx 'Status: 5.2.1' 79 | sed -e '1,3d; 1,/^$/d' $fn | grep -qx 'Diagnostic-Code: DUMMY; code=33 (#5.2.1) host=127.0.0.1' 80 | sed -e '1,3d; 1,/^$/d' $fn | grep -qx 'Subject: test' 81 | sed -e '1,3d; 1,/^$/d' $fn | not grep -qx 'This is just a test.' 82 | rm -f $fn 83 | 84 | echo 'Checking log outputs' 85 | log=$tmpdir/service/send-log 86 | grep -qx "^Starting delivery: host: 127.0.0.1 protocol: dummy file: $msgid" $log 87 | grep -qx 'From: to: ' $log 88 | grep -qx 'Sending failed: Unspecified temporary error' $log 89 | grep -qx "Message-Id: <$msgid>" $log 90 | 91 | echo 'Testing handling of rejected bounces' 92 | send_message '' me@example.com 33 5.2.2 93 | 94 | echo 'Checking failed message was moved out of the queue' 95 | test -e $QUEUEDIR/failed/$msgid 96 | rm -f $QUEUEDIR/failed/$msgid 97 | 98 | echo 'Checking for no generated bounce message' 99 | msgid2=$( ls $QUEUEDIR/failed ) 100 | test $( wc -w <<< $msgid2 ) = 0 101 | 102 | echo 'Checking log outputs' 103 | log=$tmpdir/service/send-log 104 | grep -qx "^Starting delivery: host: 127.0.0.1 protocol: dummy file: $msgid" $log 105 | grep -qx 'From: <> to: ' $log 106 | grep -qx 'Sending failed: Unspecified temporary error' $log 107 | grep -qx "Message-Id: <$msgid>" $log 108 | grep -qx "Not generating double bounce for $msgid" $log 109 | -------------------------------------------------------------------------------- /test/tests/smtp-auth: -------------------------------------------------------------------------------- 1 | . functions 2 | export HELOHOST=f.q.d.n 3 | 4 | make-testmail 5 | 6 | start server "tcpserver -1 ::0 0 sh $srcdir/test/authtest-smtp.sh $tmpdir/smtp-result" 7 | catch-port server 8 | 9 | echo 'Testing auth success with smtp' 10 | echo '250 OK' > $tmpdir/smtp-result 11 | protocol smtp --host=localhost --port=$port --user=example --pass=example 3<$testmail 12 | 13 | echo 'Testing auth login success with smtp' 14 | echo $'350 Go ahead\n250 AUTH' > $tmpdir/smtp-result 15 | protocol smtp --host=localhost --port=$port --user=example --pass=example --auth-login 3<$testmail 16 | 17 | echo 'Testing auth temporary failure with smtp' 18 | echo '450 No' > $tmpdir/smtp-result 19 | error 16 protocol smtp --host=localhost --port $port --user=example --pass=example 3<$testmail 20 | 21 | echo 'Testing auth permanent failure with smtp' 22 | echo '550 No' > $tmpdir/smtp-result 23 | error 20 protocol smtp --host=localhost --port $port --user=example --pass=example 3<$testmail 24 | 25 | rm -f $tmpdir/smtp-result 26 | 27 | stop server 28 | -------------------------------------------------------------------------------- /test/tests/smtpd: -------------------------------------------------------------------------------- 1 | . functions 2 | 3 | echo "Testing the nullmailer-smtpd program" 4 | 5 | out=$tmpdir/smtpd.out.$$ 6 | 7 | smtpd <&1 | cat -v >$out 8 | HELO 9 | HELO somebody 10 | EHLO 11 | EHLO somebody 12 | HELP 13 | HELP something 14 | DATA 15 | RCPT 16 | RCPT TO: 17 | MAIL 18 | MAIL FROM: 19 | RCPT 20 | RCPT TO: 21 | RSET 22 | RCPT TO: 23 | MAIL FROM:<> 24 | RSET 25 | MAIL FROM: 26 | RCPT TO: 27 | mail from f@example.com 28 | rcpt to r@example.com 29 | rcpt <@example.org:r2@example.com> 30 | DATA 31 | Subject: test 32 | 33 | testing SMTP 34 | .line 2 35 | ..line 3 36 | . 37 | QUIT 38 | QUIT 39 | EOF 40 | 41 | diff -u - $out <