From: Wietse Venema Date: Sun, 24 Jul 2022 05:00:00 +0000 (-0500) Subject: postfix-3.8-20220724-nonprod X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=8d42d2bc8913a10287d7be596c3831a110925bb4;p=thirdparty%2Fpostfix.git postfix-3.8-20220724-nonprod --- diff --git a/postfix/.indent.pro b/postfix/.indent.pro index 1f8431f0d..7a382f767 100644 --- a/postfix/.indent.pro +++ b/postfix/.indent.pro @@ -1,4 +1,5 @@ -TABOUNCE_STATE +-Taddrinfo -TADDR_MATCH_LIST -TADDR_PATTERN -TALIAS_TOKEN @@ -241,6 +242,11 @@ -TMKMAP_OPEN_FN -TMKMAP_OPEN_INFO -TMKMAP_SDBM +-TMOCK_APPL +-TMOCK_APPL_SIG +-TMOCK_APPL_STATUS +-TMOCK_EXPECT +-TMSG_OUTPUT_INFO -TMSG_STATS -TMULTI_SERVER -TMVECT @@ -289,6 +295,8 @@ -TPSC_SMTPD_COMMAND -TPSC_STARTTLS -TPSC_STATE +-TPTEST_CASE +-TPTEST_CTX -TQMGR_ENTRY -TQMGR_FEEDBACK -TQMGR_JOB @@ -375,7 +383,7 @@ -TSTRING_LIST -TSTRING_TABLE -TSYS_EXITS_DETAIL --TTEST_CASE +-TTEST_JMP_BUF -Ttime_t -Ttlsa_filter -TTLS_APPL_STATE diff --git a/postfix/HISTORY b/postfix/HISTORY index 443cb2ab2..aca90aee1 100644 --- a/postfix/HISTORY +++ b/postfix/HISTORY @@ -26539,16 +26539,43 @@ Apologies for any names omitted. Documentation: Cyrus SASL configuration file location. Viktor Dukhovni. File: proto/SASL_README.html. +20220605 + + Cleanup: migrate biff_notify() from gethostbyname() and + getservbyname() to myaddrinfo. File: local/biff_notify.c. + +20220609 + + Cleanup: new find_inet_service(3) module that returns errors, + and deprecate find_inet(3) where all errors are fatal. File: + util/find_inet_service.c. + 20220617 Cleanup: missing include was causing a warning on some platform. posttls-finger/posttls-finger.c. +20220618 + + Cleanup: migrate the MySQL client from find_inet(3) to + find_inet_service(3). Files: src/global/dict_mysql.c. + 20220620 Documentation: inet_interfaces and proxy_interfaces descriptions. File: proto/postconf.proto. +20220624 + + Cleanup: renamed global/test_main.[hc] to + global/test_server_main.[hc]. Files: global/test_server_main.[hc], + bounce/bounce_tester.c, Makefiles. + +20220626 + + Feature: when the NAME_MASK_NULL flag is present, the + str_name_mask_opt() function will output a string "0" when the + input mask is empty. Files: util/name_mask.[hc]. 20220719 @@ -26562,3 +26589,27 @@ Apologies for any names omitted. regression that broke TLS handshakes. It is rarely useful. Report by Spil Oss, fix by Viktor Dukhovni. File: tls/tls_server.c. + + Feature: 'ptest' infrastructure for unit tests, and 'pmock' + infrastructure to make tests independent of host configuration, + network configuration, or DNS. ptest looks like Go test, + while pmock implements a few ideas from Google gmock. + + This changes the Postfix file footprint as follows: + + 1) This adds source files named 'foo_test.c' that test code + in 'foo.c' and 'foo.h'; and this adds source files mock_foo.c + and mock_foo.h with functions that produce prepared outputs + for expected inputs. For example, mock_getaddrinfo.c + implements getaddrinfo() and getnameinfo() functions that + return prepared outputs without doing host or DNS lookups. + More files will be added as tests are converted to the + ptest/pmock infrastructure. + + 2) This removes obsolete test data files ('file.in*', + 'file.ref*'). More files will be removed as tests are + converted to the ptest/pmock infrastructure. + + 3) This renames old files whose names end in test.c: + dict_test.c is now dict_cli.c, and stream_test.c is now + test_sunos5_stream.c. diff --git a/postfix/Makefile.in b/postfix/Makefile.in index 5bc7a7cb1..72e99a913 100644 --- a/postfix/Makefile.in +++ b/postfix/Makefile.in @@ -12,7 +12,7 @@ DIRS = src/util src/global src/dns src/tls src/xsasl src/master src/milter \ src/postsuper src/qmqpd src/spawn src/flush src/verify \ src/virtual src/proxymap src/anvil src/scache src/discard src/tlsmgr \ src/postmulti src/postscreen src/dnsblog src/tlsproxy \ - src/posttls-finger src/postlogd + src/posttls-finger src/postlogd src/ptest src/testing MANDIRS = proto man html LIBEXEC = libexec/post-install libexec/postfix-script libexec/postfix-wrapper \ libexec/postmulti-script libexec/postfix-tls-script diff --git a/postfix/TODO b/postfix/TODO new file mode 100644 index 000000000..cc67947e1 --- /dev/null +++ b/postfix/TODO @@ -0,0 +1,295 @@ +TODO write a TEST_README that summarizes how to write simple tests +that don't need custom PTEST_CASE fields (use the example in +ptest_main.h), how to report test errors and how to require them, +how to require msg(3) logging, how to write subtests when test data +does not fit in custom PTEST_CASE fields (use example in test_run.c), +and how to mock out dependencies (use example in pmock_expect_test.c). + +TODO document NO_MOCK_WRAPPERS in makedefs. + +TODO move PCRE tests to src/global, and either make them skippable +(#ifndef USE_DYNAMIC_MAPS) or support dynamic loading in tests. + +DONE Need a way to SKIP tests, and report those in the summmary. + + NORETURN ptest_skip(TEST_CTX *t) + TEST_CTX has a 'skip' counter + main() reports skipped tests + +DONE PTEST_CASE.name should be PTEST_CASE.testname + +DONE Port hfrom_format.c to use PTEST. + +TODO Port haproxy_srvr.c, to use PTEST. + +TODO Test that eq_sockaddr() and eq_addrinfo compare all fields for equality. + +============== + +DONE Subtests. What would the API look like? + +With callback: +test_run(TEST_CTX *t, const char *name, void (*cb)(void *), void *context) + +This is clumsy because the callback has to be implemented outside +the test. Apart from that, the approach is robust and can temporarily +redirect long jumps. + +With inline code: + new_ctx = test_start(TEST_CTX *t, const char *name); + ... do stuff with new_ctx context... + end_test(new_ctx); // merges pass/fail stats into parent. + +This keeps the code inside the test. The problem that this still +exposes internal machinery, such as redircting long jumps and +logging. + +With inline code, again: + START_SUB_TEST(new_ctx, name) { + ...do actual test... + } + END_SUB_TEST(new_ctx); + +That hides the internal machinery inside macros, but is still too clumsy. + +In-line code, single-macro alternative: + + RUN_TEST(t, name, { + /* do actual test */ + }); + +Now we're talking! + +Both Linux gcc 11.3.1 and FreeBSD clang version 11.0.1 pre-process {} +inside () without problems. + +indent complains about "Unbalanced parens" before the '(' and "Extra +)" before the ')', but formats the code correctly. + +clang-format 12.0.1 handles {} inside (), but needs comments with /* +clang-format off */ and /* clang-format on */ to avoid messing up the +macro definition (it removes the '\' at the end of the lines). + +TODO: make sure that a subtest within a subtest propagates +the number of subtests passed/failed. + +Assume that there always is a parent (the root is special). + +#define RUN_TEST(t, name, body_in_braces) \ + MSG_JMP_BUF new_buf; \ + TEST_CTX *parent = t; + t = test_ctx_create(name, &new_buf); \ + test_info(t, "RUN %s", t->name); \ + test_error_setup(t, VSTREAM_ERR); \ + test_log_setup(t); \ + msg_vstream_enable(0); \ + if (msg_setjmp(&new_buf) == 0) { \ + body_in_braces \ + } \ + msg_jmp_bufp = parent->jbuf; \ + msg_vstream_enable(1); \ + test_log_wrapup(t); \ + mock_expect_wrapup(t); \ + if (test_error_wrapup(t) == 0) { \ + test_info(t, "PASS %s", t->name); \ + parent->pass += 1; + } else { \ + test_info(t, "FAIL %s", t->name); \ + parent->fail += 1; \ + } \ + if (t->defer_fn) \ + t->defer_fn(t->defer_ctx); \ + test_ctx_free(t); + t = parent; + +This invites an archiecture where main() creates its own context with +pass/fail counts and error handling, main() runs top-level tests in their +own child context, subtests run in a child of a top-level test, and so on. + +Remaining questions + +- When would we NOT want the test framework to capture msg(3) +logging? Perhaps in the main test context, where all logging is the +result of a test framework failure, therefore no events should be +suppressed with expectations. + +- When would we NOT want the test framework to catch panic/fatal +errors? In a main context they indicate a test framework failure. +Elsewhere, continuing after such a failure may still be desirable +even if the result will be less reliable. mymalloc_test.c relies +on this. + +================ + +DONE: implemented with test_defer(TEST_CTX *t, callback, context) which +is called from within a test, and which runs deferred code after a test +is finished. + +Some tests just need do some pre setup post cleanup. Actually, +they need to clean up after a fatal or paninc error. Would this be solved +with an application callback for example expect_exception(callback, context)? +Or should we just fork() the program that is being tested? + + +================ + +mock_expect_apply() should be a void function (now that errors are +propagated through the test context. + +================ + +DONE: Migrate test from argv.c, dict_stream.c + +Implement proper tests for vstream.c + +================ + +DONE Add msg_output test that a popped handler accepts no further logging. + +===================== + +DONE: msg_output_pop(context) also pops all handlers that were +registered later. This makes tests more robust and relaxes some +awkward requirements in msg_output_test.c. + +====================== + +Should we include all test helpers in ? Otherwis +test code would have to include +separate files for matchers, to_string converters, +with and without networking. + +No, testing.h should not pull in network-related code. + +============ + +The *test.o files need to depend on ../../conf/makedefs.out + +============= + +wrap system library functions, so that we can mock the wrappers. + +And don't use the MOCKABLE (weak symbol) annotation, it breaks +non-test code! + +================= + +make_sockaddr() should take an address family argument. + +================= + +Add a 'type' to the format strings that *print* support. + +vbuf_print_register(int char, callback) + +void callback(VBUF *bp, int width, int prec, va_list ap) + +But would the compiler's format-string checks complain, +if the new 'type' appears amidst known types? + +================== + +Should we mock out getaddrinfo() and getnameinfo(), like we mocked +out getservbyname? It would simplify a few tests, with aome risk of +less realism. It could also provide a test bed for workarounds. + +=================== + +Cleaning up the dns_lookup/dns_get_h_errno mess. + +Ideally, dns_get_h_errno() is removed from the API and all dns_lookup() +functions return the last h_errno value. But deleting a function +is bad. + +Maybe the mock set_dns_h_errno() can set an expectation for the next +dns_get_h_errno() calls, then, dns_get_h_errno() would return that. + +Problem: dns_lookup_v() and dns_lookup_l() make multiple dns_lookup_x() +calls, and not every call is followed by a dns_get_h_errno() call. +So we would need to be able to control the order of mock calls. + +Solution: expect_dns_lookup_x() takes an expected h_errno value. +Calling dns_lookup_x() will update a global variable that is shared with +get_dns_h_errno() and set_dns_h_errno(). + +================ + +Would be nice to shut up msg_vstream() output whle a test runs so that +logging goes only to the test_log_event() msg_output handler. + +Solution: add a msg_vstream_enable(int yesno) function. + +================ + +Really would like to specify a logging expectation inside a test, +instead of being required to put that outside a test function. + +Could it be folded into the test_error() infrastructure? Maybe generalize +TEST_ERR_INFO interface (and rename it). + +Strawman: + + test_log_setup(t) + create VSTRING memory stream buffer + push test_log_event() msg_output handler, with 't' + as callback context + expect_log(t, "blah blah") + append text to expected ll be made whose formatted text + test_log_wrapup(t) + pop msg_output handler + close memory stream + report mismatches between expected argv and actual logging. + delete the expected argv + delete VSTRING memory stream buffer + +One limitation is that code being tested should not push an output +handler if it may triger a test_fatal() or msg_{fatal,panic}() call +because their output handlers would persist. + +============== + +Perhaps add test_info(), test_error(), and test_fatal() for things +that could be reported directly instead of through logging diffs. +Question is where to keep the error state. In an object that is +passed by reference, like other test system do. + +The test_xxx() would have to be callable from mock helpers so they +can't be declared in mock_main.h. The would have to be declared in +a different header file. For example, testing.h. + +Then we no longer rely on result values from test actions. + +Will this still work with testing the test system itself, for +example, to test that eq_dns_rr() really reports all the expected +differences? + +One could add an 'expect this error' method. At test wrap-up time, +raise a new error when the expected error did not happen. + +How does the mock infrastructure know where to report errors? There +can be only one test main active at a time, It could call a mock_setup +function that storea a local pointer. + +Passing the test object pointer during call expectation setting +like __FILE__ and __LINE__? That does not work for a mock call that +has no expectation. This test object pointer would then be an +eq_mumble argument. + +===================== + +Replace gethostby* with myaddrinfo calls. + +$ grep -l gethostby ../*/*c +../local/biff_notify.c DONE +../util/find_inet.c deprecate + +$ grep -l getservby ../*/*c +../local/biff_notify.c DONE +../posttls-finger/posttls-finger.c +../smtp/smtp_connect.c +../util/find_inet.c USE SURROGATE getservbyname + +Or deprecate find_inet.c. It's used only in: +grep -l find_inet.h ../*/*c +../global/dict_mysql.c + diff --git a/postfix/makedefs b/postfix/makedefs index 2839f3a8a..09ee561bf 100644 --- a/postfix/makedefs +++ b/postfix/makedefs @@ -958,7 +958,7 @@ CCARGS="$CCARGS -DSNAPSHOT" # Non-production: needs thorough testing, or major changes are still # needed before the code stabilizes. -#CCARGS="$CCARGS -DNONPROD" +CCARGS="$CCARGS -DNONPROD" # Workaround: prepend Postfix include files before other include files. CCARGS="-I. -I../../include $CCARGS" diff --git a/postfix/proto/stop b/postfix/proto/stop index d8ae00828..9354b5f76 100644 --- a/postfix/proto/stop +++ b/postfix/proto/stop @@ -1565,3 +1565,5 @@ raf fnvz hc mkmap +hc +mkmap diff --git a/postfix/proto/stop.double-cc b/postfix/proto/stop.double-cc index ceddb7f5c..41c5ffa0f 100644 --- a/postfix/proto/stop.double-cc +++ b/postfix/proto/stop.double-cc @@ -330,3 +330,15 @@ XXX XXX USE_FNV_32BIT USE_FNV_32BIT void void cleanup_milter_receive state count struct DICT open const char int int dict_xx_open +void void msg_printf level format +application application specific +void void dns_set_h_errno int herrval +application application sans mock_ prefix +int int eq_enum +int int eq_str +void void defer_ctx + To undo a test_defer call call the function with a + static const struct mumble mumble + Null name or name name +copy_addrinfo copy_addrinfo expectation helper + To undo a ptest_defer call call the function with a diff --git a/postfix/proto/stop.spell-cc b/postfix/proto/stop.spell-cc index ea6ad0038..550d998d0 100644 --- a/postfix/proto/stop.spell-cc +++ b/postfix/proto/stop.spell-cc @@ -1786,3 +1786,55 @@ deinit reinit COMPAR deduplicate +Deduplicate +JMP +MATCHERS +MOCKABLE +Matcher +abc +addrinfos +clearjmp +endservent +errval +expcted +getservbyname +getservent +herrno +herrval +notexist +servent +setservent +sockaddrinfos +stayopen +testcases +unallocated +wrapup +yesno +Aargh +getservbyport +gotlen +hostlen +ipprotocol +lbrary +mockable +onlinepubs +opengroup +servlen +sockadddr +sockaddrinfo +sockaddrs +wantlen +xrat +xsh +Matchers +matchers +resetjmp +Subtests +subtest +PTEST +pmock +ptest +ptestcase +ptestcases +subtests +case's diff --git a/postfix/proto/stop.spell-history b/postfix/proto/stop.spell-history index 16c43bfd2..e54670f50 100644 --- a/postfix/proto/stop.spell-history +++ b/postfix/proto/stop.spell-history @@ -19,3 +19,10 @@ libs segfaulting srctoman systemd +Amma +Jesper +Pau +gmock +sunos +Oss +Spil diff --git a/postfix/proto/stop.spell-proto-html b/postfix/proto/stop.spell-proto-html index a4ad7c594..616fb118e 100644 --- a/postfix/proto/stop.spell-proto-html +++ b/postfix/proto/stop.spell-proto-html @@ -348,3 +348,4 @@ wiki Jà ng rsyslogd +ptest diff --git a/postfix/src/bounce/Makefile.in b/postfix/src/bounce/Makefile.in index 969413a00..9a7a69257 100644 --- a/postfix/src/bounce/Makefile.in +++ b/postfix/src/bounce/Makefile.in @@ -504,7 +504,7 @@ bounce_notify_util_tester.o: ../../include/rec_type.h bounce_notify_util_tester.o: ../../include/recipient_list.h bounce_notify_util_tester.o: ../../include/record.h bounce_notify_util_tester.o: ../../include/sys_defs.h -bounce_notify_util_tester.o: ../../include/test_main.h +bounce_notify_util_tester.o: ../../include/test_server_main.h bounce_notify_util_tester.o: ../../include/vbuf.h bounce_notify_util_tester.o: ../../include/vstream.h bounce_notify_util_tester.o: ../../include/vstring.h diff --git a/postfix/src/bounce/bounce_notify_util_tester.c b/postfix/src/bounce/bounce_notify_util_tester.c index da13f4705..91406f4cd 100644 --- a/postfix/src/bounce/bounce_notify_util_tester.c +++ b/postfix/src/bounce/bounce_notify_util_tester.c @@ -30,7 +30,7 @@ /* * Testing library. */ -#include +#include #define TEST_ENCODING "7bit" #define NO_SMTPUTF8 (0) @@ -156,7 +156,7 @@ int main(int argc, char **argv) 0, }; - test_main(argc, argv, test_driver, + test_server_main(argc, argv, test_driver, CA_TEST_MAIN_INT_TABLE(int_table), CA_TEST_MAIN_STR_TABLE(str_table), CA_TEST_MAIN_TIME_TABLE(time_table), diff --git a/postfix/src/dns/Makefile.in b/postfix/src/dns/Makefile.in index 795f9ba2a..fcbe4bb5f 100644 --- a/postfix/src/dns/Makefile.in +++ b/postfix/src/dns/Makefile.in @@ -1,19 +1,22 @@ SHELL = /bin/sh SRCS = dns_lookup.c dns_rr.c dns_strerror.c dns_strtype.c dns_rr_to_pa.c \ dns_sa_to_rr.c dns_rr_eq_sa.c dns_rr_to_sa.c dns_strrecord.c \ - dns_rr_filter.c dns_str_resflags.c dns_sec.c + dns_rr_filter.c dns_str_resflags.c dns_sec.c dns_lookup_types.c OBJS = dns_lookup.o dns_rr.o dns_strerror.o dns_strtype.o dns_rr_to_pa.o \ dns_sa_to_rr.o dns_rr_eq_sa.o dns_rr_to_sa.o dns_strrecord.o \ - dns_rr_filter.o dns_str_resflags.o dns_sec.o + dns_rr_filter.o dns_str_resflags.o dns_sec.o dns_lookup_types.o +TEST_OBJ = dns_lookup_types_test.o HDRS = dns.h TESTSRC = test_dns_lookup.c test_alias_token.c DEFS = -I. -I$(INC_DIR) -D$(SYSTYPE) CFLAGS = $(DEBUG) $(OPT) $(DEFS) INCL = LIB = lib$(LIB_PREFIX)dns$(LIB_SUFFIX) -TESTPROG= test_dns_lookup dns_rr_to_pa dns_rr_to_sa dns_sa_to_rr dns_rr_eq_sa +TESTPROG= test_dns_lookup dns_rr_to_pa dns_rr_to_sa dns_sa_to_rr dns_rr_eq_sa \ + dns_lookup_types_test LIBS = ../../lib/lib$(LIB_PREFIX)global$(LIB_SUFFIX) \ ../../lib/lib$(LIB_PREFIX)util$(LIB_SUFFIX) +TEST_LIB= ../../lib/libtesting.a ../../lib/libptest.a LIB_DIR = ../../lib INC_DIR = ../../include @@ -21,7 +24,7 @@ INC_DIR = ../../include all: $(LIB) -$(OBJS): ../../conf/makedefs.out +$(OBJS) $(TEST_OBJ): ../../conf/makedefs.out Makefile: Makefile.in cat ../../conf/makedefs.out $? >$@ @@ -31,7 +34,7 @@ test: $(TESTPROG) tests: test dns_rr_to_pa_test dns_rr_to_sa_test dns_sa_to_rr_test \ dns_rr_eq_sa_test no-a-test no-aaaa-test no-mx-test \ error-filter-test nullmx_test nxdomain_test mxonly_test \ - dnsbl_tests + dnsbl_tests test_dns_lookup_types dnsbl_tests: \ dnsbl_ttl_127.0.0.2_bind_plain_test \ @@ -98,7 +101,7 @@ dns_rr_eq_sa: $(LIB) $(LIBS) mv $@.o junk $(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(LIBS) $(SYSLIBS) mv junk $@.o - + dns_rr_to_pa_test: dns_rr_to_pa dns_rr_to_pa.in dns_rr_to_pa.ref $(SHLIB_ENV) ./dns_rr_to_pa `cat dns_rr_to_pa.in` >dns_rr_to_pa.tmp diff dns_rr_to_pa.ref dns_rr_to_pa.tmp @@ -160,6 +163,14 @@ mxonly_test: test_dns_lookup mxonly_test.ref diff mxonly_test.ref mxonly_test.tmp rm -f mxonly_test.tmp +dns_lookup_types_test: update dns_lookup_types_test.o \ + $(LIB_DIR)/mock_dns_lookup.o $(TEST_LIB) $(LIB) $(LIBS) + $(CC) $(CFLAGS) -o $@ $@.o $(LIB_DIR)/mock_dns_lookup.o \ + $(TEST_LIB) $(LIB) $(LIBS) $(SYSLIBS) + +test_dns_lookup_types: dns_lookup_types_test + $(SHLIB_ENV) $(VALGRIND) ./dns_lookup_types_test + # Non-existent record, libbind API, RFC 2308 disabled. dnsbl_ttl_127.0.0.1_bind_plain_test: test_dns_lookup dnsbl_ttl_127.0.0.1_bind_plain.ref @@ -283,8 +294,43 @@ dns_lookup.o: ../../include/valid_hostname.h dns_lookup.o: ../../include/vbuf.h dns_lookup.o: ../../include/vstream.h dns_lookup.o: ../../include/vstring.h +dns_lookup.o: ../../include/wrap_netdb.h dns_lookup.o: dns.h dns_lookup.o: dns_lookup.c +dns_lookup_types.o: ../../include/argv.h +dns_lookup_types.o: ../../include/check_arg.h +dns_lookup_types.o: ../../include/dict.h +dns_lookup_types.o: ../../include/maps.h +dns_lookup_types.o: ../../include/msg.h +dns_lookup_types.o: ../../include/myaddrinfo.h +dns_lookup_types.o: ../../include/myflock.h +dns_lookup_types.o: ../../include/sock_addr.h +dns_lookup_types.o: ../../include/sys_defs.h +dns_lookup_types.o: ../../include/vbuf.h +dns_lookup_types.o: ../../include/vstream.h +dns_lookup_types.o: ../../include/vstring.h +dns_lookup_types.o: ../../include/wrap_netdb.h +dns_lookup_types.o: dns.h +dns_lookup_types.o: dns_lookup_types.c +dns_lookup_types_test.o: ../../include/argv.h +dns_lookup_types_test.o: ../../include/check_arg.h +dns_lookup_types_test.o: ../../include/mock_dns.h +dns_lookup_types_test.o: ../../include/msg.h +dns_lookup_types_test.o: ../../include/msg_output.h +dns_lookup_types_test.o: ../../include/msg_vstream.h +dns_lookup_types_test.o: ../../include/myaddrinfo.h +dns_lookup_types_test.o: ../../include/pmock_expect.h +dns_lookup_types_test.o: ../../include/ptest.h +dns_lookup_types_test.o: ../../include/ptest_main.h +dns_lookup_types_test.o: ../../include/sock_addr.h +dns_lookup_types_test.o: ../../include/stringops.h +dns_lookup_types_test.o: ../../include/sys_defs.h +dns_lookup_types_test.o: ../../include/vbuf.h +dns_lookup_types_test.o: ../../include/vstream.h +dns_lookup_types_test.o: ../../include/vstring.h +dns_lookup_types_test.o: ../../include/wrap_netdb.h +dns_lookup_types_test.o: dns.h +dns_lookup_types_test.o: dns_lookup_types_test.c dns_rr.o: ../../include/check_arg.h dns_rr.o: ../../include/msg.h dns_rr.o: ../../include/myaddrinfo.h @@ -294,6 +340,7 @@ dns_rr.o: ../../include/sock_addr.h dns_rr.o: ../../include/sys_defs.h dns_rr.o: ../../include/vbuf.h dns_rr.o: ../../include/vstring.h +dns_rr.o: ../../include/wrap_netdb.h dns_rr.o: dns.h dns_rr.o: dns_rr.c dns_rr_eq_sa.o: ../../include/check_arg.h @@ -303,6 +350,7 @@ dns_rr_eq_sa.o: ../../include/sock_addr.h dns_rr_eq_sa.o: ../../include/sys_defs.h dns_rr_eq_sa.o: ../../include/vbuf.h dns_rr_eq_sa.o: ../../include/vstring.h +dns_rr_eq_sa.o: ../../include/wrap_netdb.h dns_rr_eq_sa.o: dns.h dns_rr_eq_sa.o: dns_rr_eq_sa.c dns_rr_filter.o: ../../include/argv.h @@ -317,6 +365,7 @@ dns_rr_filter.o: ../../include/sys_defs.h dns_rr_filter.o: ../../include/vbuf.h dns_rr_filter.o: ../../include/vstream.h dns_rr_filter.o: ../../include/vstring.h +dns_rr_filter.o: ../../include/wrap_netdb.h dns_rr_filter.o: dns.h dns_rr_filter.o: dns_rr_filter.c dns_rr_to_pa.o: ../../include/check_arg.h @@ -326,6 +375,7 @@ dns_rr_to_pa.o: ../../include/sock_addr.h dns_rr_to_pa.o: ../../include/sys_defs.h dns_rr_to_pa.o: ../../include/vbuf.h dns_rr_to_pa.o: ../../include/vstring.h +dns_rr_to_pa.o: ../../include/wrap_netdb.h dns_rr_to_pa.o: dns.h dns_rr_to_pa.o: dns_rr_to_pa.c dns_rr_to_sa.o: ../../include/check_arg.h @@ -335,6 +385,7 @@ dns_rr_to_sa.o: ../../include/sock_addr.h dns_rr_to_sa.o: ../../include/sys_defs.h dns_rr_to_sa.o: ../../include/vbuf.h dns_rr_to_sa.o: ../../include/vstring.h +dns_rr_to_sa.o: ../../include/wrap_netdb.h dns_rr_to_sa.o: dns.h dns_rr_to_sa.o: dns_rr_to_sa.c dns_sa_to_rr.o: ../../include/check_arg.h @@ -344,6 +395,7 @@ dns_sa_to_rr.o: ../../include/sock_addr.h dns_sa_to_rr.o: ../../include/sys_defs.h dns_sa_to_rr.o: ../../include/vbuf.h dns_sa_to_rr.o: ../../include/vstring.h +dns_sa_to_rr.o: ../../include/wrap_netdb.h dns_sa_to_rr.o: dns.h dns_sa_to_rr.o: dns_sa_to_rr.c dns_sec.o: ../../include/check_arg.h @@ -356,6 +408,7 @@ dns_sec.o: ../../include/split_at.h dns_sec.o: ../../include/sys_defs.h dns_sec.o: ../../include/vbuf.h dns_sec.o: ../../include/vstring.h +dns_sec.o: ../../include/wrap_netdb.h dns_sec.o: dns.h dns_sec.o: dns_sec.c dns_str_resflags.o: ../../include/check_arg.h @@ -365,6 +418,7 @@ dns_str_resflags.o: ../../include/sock_addr.h dns_str_resflags.o: ../../include/sys_defs.h dns_str_resflags.o: ../../include/vbuf.h dns_str_resflags.o: ../../include/vstring.h +dns_str_resflags.o: ../../include/wrap_netdb.h dns_str_resflags.o: dns.h dns_str_resflags.o: dns_str_resflags.c dns_strerror.o: ../../include/check_arg.h @@ -373,6 +427,7 @@ dns_strerror.o: ../../include/sock_addr.h dns_strerror.o: ../../include/sys_defs.h dns_strerror.o: ../../include/vbuf.h dns_strerror.o: ../../include/vstring.h +dns_strerror.o: ../../include/wrap_netdb.h dns_strerror.o: dns.h dns_strerror.o: dns_strerror.c dns_strrecord.o: ../../include/check_arg.h @@ -382,6 +437,7 @@ dns_strrecord.o: ../../include/sock_addr.h dns_strrecord.o: ../../include/sys_defs.h dns_strrecord.o: ../../include/vbuf.h dns_strrecord.o: ../../include/vstring.h +dns_strrecord.o: ../../include/wrap_netdb.h dns_strrecord.o: dns.h dns_strrecord.o: dns_strrecord.c dns_strtype.o: ../../include/check_arg.h @@ -390,6 +446,7 @@ dns_strtype.o: ../../include/sock_addr.h dns_strtype.o: ../../include/sys_defs.h dns_strtype.o: ../../include/vbuf.h dns_strtype.o: ../../include/vstring.h +dns_strtype.o: ../../include/wrap_netdb.h dns_strtype.o: dns.h dns_strtype.o: dns_strtype.c test_dns_lookup.o: ../../include/argv.h @@ -404,5 +461,6 @@ test_dns_lookup.o: ../../include/sys_defs.h test_dns_lookup.o: ../../include/vbuf.h test_dns_lookup.o: ../../include/vstream.h test_dns_lookup.o: ../../include/vstring.h +test_dns_lookup.o: ../../include/wrap_netdb.h test_dns_lookup.o: dns.h test_dns_lookup.o: test_dns_lookup.c diff --git a/postfix/src/dns/dns.h b/postfix/src/dns/dns.h index 5f53dbc8f..43f4a8e32 100644 --- a/postfix/src/dns/dns.h +++ b/postfix/src/dns/dns.h @@ -239,8 +239,9 @@ extern int dns_lookup_x(const char *, unsigned, unsigned, DNS_RR **, extern int dns_lookup_rl(const char *, unsigned, DNS_RR **, VSTRING *, VSTRING *, int *, int,...); extern int dns_lookup_rv(const char *, unsigned, DNS_RR **, VSTRING *, - VSTRING *, int *, int, unsigned *); + VSTRING *, int *, int, const unsigned *); extern int dns_get_h_errno(void); +extern void dns_set_h_errno(int); #define dns_lookup(name, type, rflags, list, fqdn, why) \ dns_lookup_x((name), (type), (rflags), (list), (fqdn), (why), (int *) 0, \ diff --git a/postfix/src/dns/dns_lookup.c b/postfix/src/dns/dns_lookup.c index 1c12a889d..748c1a69a 100644 --- a/postfix/src/dns/dns_lookup.c +++ b/postfix/src/dns/dns_lookup.c @@ -33,6 +33,9 @@ /* unsigned *ltype; /* /* int dns_get_h_errno() +/* +/* void dns_set_h_errno( +/* int errval) /* AUXILIARY FUNCTIONS /* extern int var_dns_ncache_ttl_fix; /* @@ -85,9 +88,9 @@ /* an invalid name is reported as a DNS_INVAL result, while /* malformed replies are reported as transient errors. /* -/* dns_get_h_errno() returns the last error. This deprecates -/* usage of the global h_errno variable. We should not rely -/* on that being updated. +/* dns_get_h_errno() returns the last error, and dns_set_h_errno() +/* sets it. This deprecates usage of the global h_errno variable. +/* We should not rely on that being updated. /* /* dns_lookup_l() and dns_lookup_v() allow the user to specify /* a list of resource types. @@ -295,8 +298,8 @@ typedef struct DNS_REPLY { #define INET6_ADDR_LEN 16 /* XXX */ /* - * Use the threadsafe resolver API if available, not because it is theadsafe, - * but because it has more functionality. + * Use the threadsafe resolver API if available, not because it is + * theadsafe, but because it has more functionality. */ #ifdef USE_RES_NCALLS static struct __res_state dns_res_state; @@ -1137,136 +1140,16 @@ int dns_lookup_x(const char *name, unsigned type, unsigned flags, return (DNS_NOTFOUND); } -/* dns_lookup_rl - DNS lookup interface with types list */ - -int dns_lookup_rl(const char *name, unsigned flags, DNS_RR **rrlist, - VSTRING *fqdn, VSTRING *why, int *rcode, - int lflags,...) -{ - va_list ap; - unsigned type, next; - int status = DNS_NOTFOUND; - int hpref_status = INT_MIN; - VSTRING *hpref_rtext = 0; - int hpref_rcode; - int hpref_h_errno; - DNS_RR *rr; - - /* Save intermediate highest-priority result. */ -#define SAVE_HPREF_STATUS() do { \ - hpref_status = status; \ - if (rcode) \ - hpref_rcode = *rcode; \ - if (why && status != DNS_OK) \ - vstring_strcpy(hpref_rtext ? hpref_rtext : \ - (hpref_rtext = vstring_alloc(VSTRING_LEN(why))), \ - vstring_str(why)); \ - hpref_h_errno = DNS_GET_H_ERRNO(&dns_res_state); \ - } while (0) - - /* Restore intermediate highest-priority result. */ -#define RESTORE_HPREF_STATUS() do { \ - status = hpref_status; \ - if (rcode) \ - *rcode = hpref_rcode; \ - if (why && status != DNS_OK) \ - vstring_strcpy(why, vstring_str(hpref_rtext)); \ - DNS_SET_H_ERRNO(&dns_res_state, hpref_h_errno); \ - } while (0) - - if (rrlist) - *rrlist = 0; - va_start(ap, lflags); - for (type = va_arg(ap, unsigned); type != 0; type = next) { - next = va_arg(ap, unsigned); - if (msg_verbose) - msg_info("lookup %s type %s flags %s", - name, dns_strtype(type), dns_str_resflags(flags)); - status = dns_lookup_x(name, type, flags, rrlist ? &rr : (DNS_RR **) 0, - fqdn, why, rcode, lflags); - if (rrlist && rr) - *rrlist = dns_rr_append(*rrlist, rr); - if (status == DNS_OK) { - if (lflags & DNS_REQ_FLAG_STOP_OK) - break; - } else if (status == DNS_INVAL) { - if (lflags & DNS_REQ_FLAG_STOP_INVAL) - break; - } else if (status == DNS_POLICY) { - if (type == T_MX && (lflags & DNS_REQ_FLAG_STOP_MX_POLICY)) - break; - } else if (status == DNS_NULLMX) { - if (lflags & DNS_REQ_FLAG_STOP_NULLMX) - break; - } - /* XXX Stop after NXDOMAIN error. */ - if (next == 0) - break; - if (status >= hpref_status) - SAVE_HPREF_STATUS(); /* save last info */ - } - va_end(ap); - if (status < hpref_status) - RESTORE_HPREF_STATUS(); /* else report last info */ - if (hpref_rtext) - vstring_free(hpref_rtext); - return (status); -} - -/* dns_lookup_rv - DNS lookup interface with types vector */ +/* dns_get_h_errno - get the last lookup status */ -int dns_lookup_rv(const char *name, unsigned flags, DNS_RR **rrlist, - VSTRING *fqdn, VSTRING *why, int *rcode, - int lflags, unsigned *types) +int dns_get_h_errno(void) { - unsigned type, next; - int status = DNS_NOTFOUND; - int hpref_status = INT_MIN; - VSTRING *hpref_rtext = 0; - int hpref_rcode; - int hpref_h_errno; - DNS_RR *rr; - - if (rrlist) - *rrlist = 0; - for (type = *types++; type != 0; type = next) { - next = *types++; - if (msg_verbose) - msg_info("lookup %s type %s flags %s", - name, dns_strtype(type), dns_str_resflags(flags)); - status = dns_lookup_x(name, type, flags, rrlist ? &rr : (DNS_RR **) 0, - fqdn, why, rcode, lflags); - if (rrlist && rr) - *rrlist = dns_rr_append(*rrlist, rr); - if (status == DNS_OK) { - if (lflags & DNS_REQ_FLAG_STOP_OK) - break; - } else if (status == DNS_INVAL) { - if (lflags & DNS_REQ_FLAG_STOP_INVAL) - break; - } else if (status == DNS_POLICY) { - if (type == T_MX && (lflags & DNS_REQ_FLAG_STOP_MX_POLICY)) - break; - } else if (status == DNS_NULLMX) { - if (lflags & DNS_REQ_FLAG_STOP_NULLMX) - break; - } - /* XXX Stop after NXDOMAIN error. */ - if (next == 0) - break; - if (status >= hpref_status) - SAVE_HPREF_STATUS(); /* save last info */ - } - if (status < hpref_status) - RESTORE_HPREF_STATUS(); /* else report last info */ - if (hpref_rtext) - vstring_free(hpref_rtext); - return (status); + return (DNS_GET_H_ERRNO(&dns_res_state)); } -/* dns_get_h_errno - get the last lookup status */ +/* dns_set_h_errno - set the last lookup status */ -int dns_get_h_errno(void) +void dns_set_h_errno(int errval) { - return (DNS_GET_H_ERRNO(&dns_res_state)); + DNS_SET_H_ERRNO(&dns_res_state, errval); } diff --git a/postfix/src/dns/dns_lookup_types.c b/postfix/src/dns/dns_lookup_types.c new file mode 100644 index 000000000..de89ca681 --- /dev/null +++ b/postfix/src/dns/dns_lookup_types.c @@ -0,0 +1,183 @@ +/*++ +/* NAME +/* dns_lookup_types 3 +/* SUMMARY +/* domain name service lookup for multiple types +/* SYNOPSIS +/* #include +/* +/* int dns_lookup_l(name, rflags, list, fqdn, why, lflags, ...) +/* const char *name; +/* unsigned rflags; +/* DNS_RR **list; +/* VSTRING *fqdn; +/* VSTRING *why; +/* int lflags; +/* +/* int dns_lookup_v(name, rflags, list, fqdn, why, lflags, ltype) +/* const char *name; +/* unsigned rflags; +/* DNS_RR **list; +/* VSTRING *fqdn; +/* VSTRING *why; +/* int lflags; +/* unsigned *ltype; +/* AUXILIARY FUNCTIONS +/* int dns_lookup_rl(name, rflags, list, fqdn, why, rcode, lflags, ...) +/* const char *name; +/* unsigned rflags; +/* DNS_RR **list; +/* VSTRING *fqdn; +/* VSTRING *why; +/* int *rcode; +/* int lflags; +/* +/* int dns_lookup_rv(name, rflags, list, fqdn, why, rcode, lflags, +/* ltype) +/* const char *name; +/* unsigned rflags; +/* DNS_RR **list; +/* VSTRING *fqdn; +/* VSTRING *why; +/* int *rcode; +/* int lflags; +/* unsigned *ltype; +/* DESCRIPTION +/* These functions iterate over a sequence of unsigned resource +/* types, call dns_lookup_x() for each type, and carefully +/* aggregate the resulting error and non-error results. +/* +/* dns_lookup_rl() and dns_lookup_l() iterate over a variadic +/* list of query types, while dns_lookup_rv() and dns_lookup_v() +/* iterate over a vector of query types. +/* DIAGNOSTICS +/* SEE ALSO +/* dns_lookup(3), domain name service lookup +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/* +/* Wietse Venema +/* Google, Inc. +/* 111 8th Avenue +/* New York, NY 10011, USA +/*--*/ + +/* System library. */ + +#include + +/* Utility library. */ + +#include +#include + +/* DNS library. */ + +#define LIBDNS_INTERNAL +#include + + /* + * KISS memory management. + */ +#define MAX_TYPE 10 + +/* dns_lookup_rl - DNS lookup interface with types list */ + +int dns_lookup_rl(const char *name, unsigned flags, DNS_RR **rrlist, + VSTRING *fqdn, VSTRING *why, int *rcode, + int lflags,...) +{ + va_list ap; + unsigned type, types[MAX_TYPE]; + int count = 0; + + va_start(ap, lflags); + for (type = va_arg(ap, unsigned); type != 0; type = va_arg(ap, unsigned)) { + if (count >= MAX_TYPE - 1) + msg_panic("dns_lookup_rl: too many types"); + types[count++] = type; + } + types[count] = 0; + va_end(ap); + return (dns_lookup_rv(name, flags, rrlist, fqdn, why, rcode, lflags, types)); +} + +/* dns_lookup_rv - DNS lookup interface with types vector */ + +int dns_lookup_rv(const char *name, unsigned flags, DNS_RR **rrlist, + VSTRING *fqdn, VSTRING *why, int *rcode, + int lflags, const unsigned *types) +{ + unsigned type, next; + int status = DNS_NOTFOUND; + int hpref_status = INT_MIN; + VSTRING *hpref_rtext = 0; + int hpref_rcode; + int hpref_h_errno; + DNS_RR *rr; + + /* Save intermediate highest-priority result. */ +#define SAVE_HPREF_STATUS() do { \ + hpref_status = status; \ + if (rcode) \ + hpref_rcode = *rcode; \ + if (why && status != DNS_OK) \ + vstring_strcpy(hpref_rtext ? hpref_rtext : \ + (hpref_rtext = vstring_alloc(VSTRING_LEN(why))), \ + vstring_str(why)); \ + hpref_h_errno = dns_get_h_errno(); \ + } while (0) + + /* Restore intermediate highest-priority result. */ +#define RESTORE_HPREF_STATUS() do { \ + status = hpref_status; \ + if (rcode) \ + *rcode = hpref_rcode; \ + if (why && status != DNS_OK) \ + vstring_strcpy(why, vstring_str(hpref_rtext)); \ + dns_set_h_errno(hpref_h_errno); \ + } while (0) + + if (rrlist) + *rrlist = 0; + for (type = *types++; type != 0; type = next) { + next = *types++; + if (msg_verbose) + msg_info("lookup %s type %s flags %s", + name, dns_strtype(type), dns_str_resflags(flags)); + status = dns_lookup_x(name, type, flags, rrlist ? &rr : (DNS_RR **) 0, + fqdn, why, rcode, lflags); + if (rrlist && rr) + *rrlist = dns_rr_append(*rrlist, rr); + if (status == DNS_OK) { + if (lflags & DNS_REQ_FLAG_STOP_OK) + break; + } else if (status == DNS_INVAL) { + if (lflags & DNS_REQ_FLAG_STOP_INVAL) + break; + } else if (status == DNS_POLICY) { + if (type == T_MX && (lflags & DNS_REQ_FLAG_STOP_MX_POLICY)) + break; + } else if (status == DNS_NULLMX) { + if (lflags & DNS_REQ_FLAG_STOP_NULLMX) + break; + } + /* XXX Stop after NXDOMAIN error. */ + if (next == 0) + break; + if (status >= hpref_status) + SAVE_HPREF_STATUS(); /* save last info */ + } + if (status < hpref_status) + RESTORE_HPREF_STATUS(); /* else report last info */ + if (hpref_rtext) + vstring_free(hpref_rtext); + return (status); +} diff --git a/postfix/src/dns/dns_lookup_types_test.c b/postfix/src/dns/dns_lookup_types_test.c new file mode 100644 index 000000000..78d60cf74 --- /dev/null +++ b/postfix/src/dns/dns_lookup_types_test.c @@ -0,0 +1,200 @@ + /* + * Test program to mocks including logging. See pmock_expect_test.c and + * ptest_main.h for a documented example. + */ + + /* + * System library. + */ +#include + + /* + * Utility library. + */ +#include +#include + + /* + * DNS library. + */ +#include + + /* + * Test library. + */ +#include +#include + +typedef struct PTEST_CASE { + const char *testname; /* Human-readable description */ + void (*action) (PTEST_CTX *, const struct PTEST_CASE *); +} PTEST_CASE; + + /* + * dns_lookup_rl() forwards all calls to dns_lookup_rv(), therefore most + * tests will focus on dns_lookup_rl(). + */ +#define NO_RFLAGS 0 +#define NO_LFLAGS 0 + +static void test_dns_lookup_rl_success(PTEST_CTX *t, const PTEST_CASE *unused) +{ + int got_st, want_st = DNS_OK; + DNS_RR *got_rr = 0, *want_rr; + int got_rcode, want_rcode = NOERROR; + int got_herrval, want_herrval = 0; + + /* + * Set up expectations and prepared responses. + */ + want_rr = make_dns_rr("example.com", "example.com", T_MX, C_IN, + 5, 0, 10, "m1.example.com", 14); + expect_dns_lookup_x(1, want_herrval, DNS_OK, "example.com", T_MX, + NO_RFLAGS, want_rr, (VSTRING *) 0, (VSTRING *) 0, + NOERROR, NO_LFLAGS); + + got_st = dns_lookup_rl("example.com", NO_RFLAGS, &got_rr, (VSTRING *) 0, + (VSTRING *) 0, &got_rcode, NO_LFLAGS, T_MX, 0); + if (got_st != want_st) { + ptest_error(t, "dns_lookup_rl: got result %d, want %d", + got_st, want_st); + } else if (got_rcode != want_rcode) { + ptest_error(t, "dns_lookup_rl: got rcode %d, want %d", + got_rcode, want_rcode); + } else { + (void) eq_dns_rr(t, "dns_lookup_rl", got_rr, want_rr); + } + + got_herrval = dns_get_h_errno(); + if (got_herrval != want_herrval) + ptest_error(t, "dns_get_h_errno: got %d, want %d", + got_herrval, want_herrval); + + /* + * Cleanup. + */ + dns_rr_free(want_rr); + if (got_rr) + dns_rr_free(got_rr); +} + +static void test_dns_lookup_rv_success(PTEST_CTX *t, const PTEST_CASE *unused) +{ + int got_st, want_st = DNS_OK; + DNS_RR *got_rr = 0, *want_rr; + int got_rcode, want_rcode = NOERROR; + int got_herrval, want_herrval = 0; + static const unsigned rr_types[2] = {T_MX, 0}; + + /* + * Set up expectations and prepared responses, + */ + want_rr = make_dns_rr("example.com", "example.com", T_MX, C_IN, + 5, 0, 10, "m1.example.com", 14); + expect_dns_lookup_x(1, want_herrval, DNS_OK, "example.com", T_MX, + NO_RFLAGS, want_rr, (VSTRING *) 0, (VSTRING *) 0, + NOERROR, NO_LFLAGS); + + got_st = dns_lookup_rv("example.com", NO_RFLAGS, &got_rr, (VSTRING *) 0, + (VSTRING *) 0, &got_rcode, NO_LFLAGS, rr_types); + if (got_st != want_st) { + ptest_error(t, "dns_lookup_rv: got result %d, want %d", + got_st, want_st); + } else if (got_rcode != want_rcode) { + ptest_error(t, "dns_lookup_rv: got rcode %d, want %d", + got_rcode, want_rcode); + } else { + (void) eq_dns_rr(t, "dns_lookup_rv", got_rr, want_rr); + } + + got_herrval = dns_get_h_errno(); + if (got_herrval != want_herrval) + ptest_error(t, "dns_get_h_errno: got %d, want %d", + got_herrval, want_herrval); + + /* + * Cleanup. + */ + dns_rr_free(want_rr); + if (got_rr) + dns_rr_free(got_rr); +} + +static void test_dns_lookup_rv_error_ladder(PTEST_CTX *t, + const PTEST_CASE *unused) +{ + int got_st; + int got_herrval; + struct step { + int want_st; + int want_herrval; + }; + struct step ladder[] = { + DNS_OK, 0, + DNS_POLICY, 0, + DNS_RETRY, TRY_AGAIN, + DNS_INVAL, 0, + DNS_FAIL, NO_RECOVERY, + DNS_NULLMX, 0, + DNS_NOTFOUND, NO_DATA, + }; + struct step *lp; + VSTRING *label = vstring_alloc(100); + +#define LADDER_SIZE (sizeof(ladder)/sizeof(*ladder)) + + for (lp = ladder; lp < ladder + LADDER_SIZE - 1; lp++) { + + vstring_sprintf(label, "%s precedence over %s", + dns_status_to_string(lp->want_st), + dns_status_to_string(lp[1].want_st)); + + PTEST_RUN(t, vstring_str(label), { + + /* + * Set up expectations and prepared responses. + */ + expect_dns_lookup_x(1, lp->want_herrval, lp->want_st, + "example.com", T_MX, NO_RFLAGS, (DNS_RR *) 0, + (VSTRING *) 0, (VSTRING *) 0, NOERROR, NO_LFLAGS); + expect_dns_lookup_x(1, lp[1].want_herrval, lp[1].want_st, + "example.com", T_A, NO_RFLAGS, (DNS_RR *) 0, + (VSTRING *) 0, (VSTRING *) 0, NOERROR, NO_LFLAGS); + + /* + * Call the mock and verify the results. + */ + got_st = dns_lookup_rl("example.com", NO_RFLAGS, (DNS_RR **) 0, + (VSTRING *) 0, (VSTRING *) 0, (int *) 0, + NO_LFLAGS, T_MX, T_A, 0); + if (got_st != lp->want_st) { + ptest_error(t, "dns_lookup_rv: got result %d, want %d", + got_st, lp->want_st); + } + got_herrval = dns_get_h_errno(); + if (got_herrval != lp->want_herrval) + ptest_error(t, "dns_get_h_errno: got %d, want %d", + got_herrval, lp->want_herrval); + }); + } + vstring_free(label); +} + + /* + * Test cases. The "success" tests exercise the expectation match and apply + * helpers, and "failure" tests exercise the print helpers. All tests + * exercise the expectation free helpers. + */ +const PTEST_CASE ptestcases[] = { + { + "test_dns_lookup_rl success", test_dns_lookup_rl_success, + }, + { + "test_dns_lookup_rv success", test_dns_lookup_rv_success, + }, + { + "test_dns_lookup_rv error ladder", test_dns_lookup_rv_error_ladder, + }, +}; + +#include diff --git a/postfix/src/dnsblog/Makefile.in b/postfix/src/dnsblog/Makefile.in index c2ed84881..042e40c13 100644 --- a/postfix/src/dnsblog/Makefile.in +++ b/postfix/src/dnsblog/Makefile.in @@ -81,4 +81,5 @@ dnsblog.o: ../../include/valid_hostname.h dnsblog.o: ../../include/vbuf.h dnsblog.o: ../../include/vstream.h dnsblog.o: ../../include/vstring.h +dnsblog.o: ../../include/wrap_netdb.h dnsblog.o: dnsblog.c diff --git a/postfix/src/global/Makefile.in b/postfix/src/global/Makefile.in index 86390ede2..a8c7ccd4c 100644 --- a/postfix/src/global/Makefile.in +++ b/postfix/src/global/Makefile.in @@ -36,7 +36,7 @@ SRCS = abounce.c anvil_clnt.c been_here.c bounce.c bounce_log.c \ mail_addr_form.c quote_flags.c maillog_client.c \ normalize_mailhost_addr.c map_search.c reject_deliver_request.c \ info_log_addr_form.c sasl_mech_filter.c login_sender_match.c \ - test_main.c compat_level.c config_known_tcp_ports.c \ + test_server_main.c compat_level.c config_known_tcp_ports.c \ hfrom_format.c OBJS = abounce.o anvil_clnt.o been_here.o bounce.o bounce_log.o \ canon_addr.o cfg_parser.o cleanup_strerror.o cleanup_strflags.o \ @@ -75,13 +75,15 @@ OBJS = abounce.o anvil_clnt.o been_here.o bounce.o bounce_log.o \ $(NON_PLUGIN_MAP_OBJ) mail_addr_form.o quote_flags.o maillog_client.o \ normalize_mailhost_addr.o map_search.o reject_deliver_request.o \ info_log_addr_form.o sasl_mech_filter.o login_sender_match.o \ - test_main.o compat_level.o config_known_tcp_ports.o \ + test_server_main.o compat_level.o config_known_tcp_ports.o \ hfrom_format.o # MAP_OBJ is for maps that may be dynamically loaded with dynamicmaps.cf. # When hard-linking these maps, makedefs sets NON_PLUGIN_MAP_OBJ=$(MAP_OBJ), # otherwise it sets the PLUGIN_* macros. MAP_OBJ = dict_ldap.o dict_mysql.o dict_pgsql.o dict_sqlite.o - +TEST_OBJ = normalize_mailhost_addr_test.o smtp_reply_footer_test.o \ + login_sender_match_test.o map_search_test.o delivered_hdr_test.o \ + config_known_tcp_ports_test.o hfrom_format_test.o HDRS = abounce.h anvil_clnt.h been_here.h bounce.h bounce_log.h \ canon_addr.h cfg_parser.h cleanup_user.h clnt_stream.h config.h \ conv_time.h db_common.h debug_peer.h debug_process.h defer.h \ @@ -112,9 +114,11 @@ HDRS = abounce.h anvil_clnt.h been_here.h bounce.h bounce_log.h \ attr_override.h mail_parm_split.h midna_adomain.h mail_addr_form.h \ maillog_client.h normalize_mailhost_addr.h map_search.h \ info_log_addr_form.h sasl_mech_filter.h login_sender_match.h \ - test_main.h compat_level.h config_known_tcp_ports.h \ + test_server_main.h compat_level.h config_known_tcp_ports.h \ hfrom_format.h -TESTSRC = rec2stream.c stream2rec.c recdump.c +TESTSRC = rec2stream.c stream2rec.c recdump.c login_sender_match_test.c \ + normalize_mailhost_addr_test.c smtp_reply_footer_test.c \ + map_search_test.c DEFS = -I. -I$(INC_DIR) -D$(SYSTYPE) CFLAGS = $(DEBUG) $(OPT) $(DEFS) INCL = @@ -128,11 +132,13 @@ TESTPROG= domain_list dot_lockfile mail_addr_crunch mail_addr_find \ valid_mailhost_addr own_inet_addr header_body_checks \ data_redirect addr_match_list safe_ultostr verify_sender_addr \ mail_version mail_dict server_acl uxtext mail_parm_split \ - fold_addr smtp_reply_footer mail_addr_map normalize_mailhost_addr \ - haproxy_srvr map_search delivered_hdr login_sender_match \ - compat_level config_known_tcp_ports hfrom_format + fold_addr smtp_reply_footer_test mail_addr_map \ + normalize_mailhost_addr_test haproxy_srvr map_search_test \ + delivered_hdr_test login_sender_match_test \ + compat_level config_known_tcp_ports_test hfrom_format_test LIBS = ../../lib/lib$(LIB_PREFIX)util$(LIB_SUFFIX) +TEST_LIB= ../../lib/libtesting.a ../../lib/libptest.a LIB_DIR = ../../lib INC_DIR = ../../include PLUGIN_MAP_SO = $(LIB_PREFIX)ldap$(LIB_SUFFIX) $(LIB_PREFIX)mysql$(LIB_SUFFIX) \ @@ -143,7 +149,7 @@ MAKES = all: $(LIB) $(PLUGIN_MAP_SO_MAKE) $(PLUGIN_MAP_OBJ) -$(OBJS) $(PLUGIN_MAP_OBJ): ../../conf/makedefs.out +$(OBJS) $(PLUGIN_MAP_OBJ) $(TEST_OBJ): ../../conf/makedefs.out Makefile: Makefile.in cat ../../conf/makedefs.out $? >$@ @@ -371,43 +377,22 @@ mail_parm_split: mail_parm_split.c $(LIB) $(LIBS) fold_addr: fold_addr.c $(LIB) $(LIBS) $(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(LIBS) $(SYSLIBS) -smtp_reply_footer: smtp_reply_footer.c $(LIB) $(LIBS) - $(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(LIBS) $(SYSLIBS) - -normalize_mailhost_addr: normalize_mailhost_addr.c $(LIB) $(LIBS) - $(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(LIBS) $(SYSLIBS) - haproxy_srvr: haproxy_srvr.c $(LIB) $(LIBS) $(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(LIBS) $(SYSLIBS) -map_search: map_search.c $(LIB) $(LIBS) - $(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(LIBS) $(SYSLIBS) - -delivered_hdr: delivered_hdr.c $(LIB) $(LIBS) - $(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(LIBS) $(SYSLIBS) - -login_sender_match: login_sender_match.c $(LIB) $(LIBS) - $(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(LIBS) $(SYSLIBS) - compat_level: compat_level.c $(LIB) $(LIBS) $(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(LIBS) $(SYSLIBS) -hfrom_format: hfrom_format.c $(LIB) $(LIBS) - $(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(LIBS) $(SYSLIBS) - -config_known_tcp_ports: config_known_tcp_ports.c $(LIB) $(LIBS) - $(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(LIBS) $(SYSLIBS) - tests: tok822_test mime_tests strip_addr_test tok822_limit_test \ xtext_test scache_multi_test ehlo_mask_test \ namadr_list_test mail_conf_time_test header_body_checks_tests \ mail_version_test server_acl_test resolve_local_test maps_test \ safe_ultostr_test mail_parm_split_test fold_addr_test \ - smtp_reply_footer_test off_cvt_test mail_addr_crunch_test \ + test_smtp_reply_footer off_cvt_test mail_addr_crunch_test \ mail_addr_find_test mail_addr_map_test quote_822_local_test \ - normalize_mailhost_addr_test haproxy_srvr_test map_search_test \ - delivered_hdr_test login_sender_match_test compat_level_test \ - config_known_tcp_ports_test hfrom_format_test + test_normalize_mailhost_addr haproxy_srvr_test test_map_search \ + delivered_hdr_test test_login_sender_match compat_level_test \ + test_config_known_tcp_ports test_hfrom_format mime_tests: mime_test mime_nest mime_8bit mime_dom mime_trunc mime_cvt \ mime_cvt2 mime_cvt3 mime_garb1 mime_garb2 mime_garb3 mime_garb4 @@ -689,10 +674,11 @@ fold_addr_test: fold_addr fold_addr_test.in fold_addr_test.ref diff fold_addr_test.ref fold_addr_test.tmp rm -f fold_addr_test.tmp -smtp_reply_footer_test: smtp_reply_footer smtp_reply_footer.ref - $(SHLIB_ENV) $(VALGRIND) ./smtp_reply_footer >smtp_reply_footer.tmp 2>&1 - diff smtp_reply_footer.ref smtp_reply_footer.tmp - rm -f smtp_reply_footer.tmp +smtp_reply_footer_test: smtp_reply_footer_test.o $(TEST_LIB) $(LIB) $(LIBS) + $(CC) $(CFLAGS) -o $@ $@.o $(TEST_LIB) $(LIB) $(LIBS) $(SYSLIBS) + +test_smtp_reply_footer: smtp_reply_footer_test + $(SHLIB_ENV) $(VALGRIND) ./smtp_reply_footer_test off_cvt_test: off_cvt off_cvt.in off_cvt.ref $(SHLIB_ENV) $(VALGRIND) ./off_cvt off_cvt.tmp 2>&1 @@ -720,30 +706,36 @@ quote_822_local_test: update quote_822_local quote_822_local.in quote_822_local. diff quote_822_local.ref quote_822_local.tmp rm -f quote_822_local.tmp -normalize_mailhost_addr_test: update normalize_mailhost_addr - -$(SHLIB_ENV) $(VALGRIND) ./normalize_mailhost_addr >normalize_mailhost_addr.tmp 2>&1 - diff /dev/null normalize_mailhost_addr.tmp - rm -f normalize_mailhost_addr.tmp +normalize_mailhost_addr_test: normalize_mailhost_addr_test.o \ + $(TEST_LIB) $(LIB) $(LIBS) + $(CC) $(CFLAGS) -o $@ $@.o $(TEST_LIB) $(LIB) $(LIBS) $(SYSLIBS) + +test_normalize_mailhost_addr: update normalize_mailhost_addr_test + $(SHLIB_ENV) $(VALGRIND) ./normalize_mailhost_addr_test haproxy_srvr_test: update haproxy_srvr -$(SHLIB_ENV) $(VALGRIND) ./haproxy_srvr >haproxy_srvr.tmp 2>&1 diff /dev/null haproxy_srvr.tmp rm -f haproxy_srvr.tmp -map_search_test: update map_search map_search.ref - -$(SHLIB_ENV) $(VALGRIND) ./map_search >map_search.tmp 2>&1 - diff map_search.ref map_search.tmp - rm -f map_search.tmp +map_search_test: map_search_test.o $(TEST_LIB) $(LIB) $(LIBS) + $(CC) $(CFLAGS) -o $@ $@.o $(TEST_LIB) $(LIB) $(LIBS) $(SYSLIBS) -delivered_hdr_test: update delivered_hdr delivered_hdr.ref - -$(SHLIB_ENV) $(VALGRIND) ./delivered_hdr >delivered_hdr.tmp 2>&1 - diff delivered_hdr.ref delivered_hdr.tmp - rm -f delivered_hdr.tmp +test_map_search: update map_search_test + $(SHLIB_ENV) $(VALGRIND) ./map_search_test -login_sender_match_test: update login_sender_match login_sender_match.ref - -$(SHLIB_ENV) $(VALGRIND) ./login_sender_match >login_sender_match.tmp 2>&1 - diff login_sender_match.ref login_sender_match.tmp - rm -f login_sender_match.tmp +delivered_hdr_test: delivered_hdr_test.o $(TEST_LIB) $(LIB) $(LIBS) + $(CC) $(CFLAGS) -o $@ $@.o $(TEST_LIB) $(LIB) $(LIBS) $(SYSLIBS) + +test_delivered_hdr: update delivered_hdr_test + $(SHLIB_ENV) $(VALGRIND) ./delivered_hdr_test + +login_sender_match_test: login_sender_match_test.o \ + $(TEST_LIB) $(LIB) $(LIBS) + $(CC) $(CFLAGS) -o $@ $@.o $(TEST_LIB) $(LIB) $(LIBS) $(SYSLIBS) + +test_login_sender_match: update login_sender_match_test + $(SHLIB_ENV) $(VALGRIND) ./login_sender_match_test compat_level_test: compat_level_expand_test compat_level_convert_test @@ -759,19 +751,17 @@ compat_level_convert_test: update compat_level compat_level_convert.in \ diff compat_level_convert.ref compat_level_convert.tmp rm -f compat_level_convert.tmp -config_known_tcp_ports_test: update config_known_tcp_ports \ - config_known_tcp_ports.ref - -$(SHLIB_ENV) $(VALGRIND) ./config_known_tcp_ports \ - >config_known_tcp_ports.tmp 2>&1 - diff config_known_tcp_ports.ref config_known_tcp_ports.tmp - rm -f config_known_tcp_ports.tmp +config_known_tcp_ports_test: config_known_tcp_ports_test.o $(TEST_LIB) $(LIB) $(LIBS) + $(CC) $(CFLAGS) -o $@ $@.o $(TEST_LIB) $(LIB) $(LIBS) $(SYSLIBS) + +test_config_known_tcp_ports: update config_known_tcp_ports_test + $(SHLIB_ENV) $(VALGRIND) ./config_known_tcp_ports_test + +hfrom_format_test: hfrom_format_test.o $(TEST_LIB) $(LIB) $(LIBS) + $(CC) $(CFLAGS) -o $@ $@.o $(TEST_LIB) $(LIB) $(LIBS) $(SYSLIBS) -hfrom_format_test: update hfrom_format \ - hfrom_format.ref - -$(SHLIB_ENV) $(VALGRIND) ./hfrom_format \ - >hfrom_format.tmp 2>&1 - diff hfrom_format.ref hfrom_format.tmp - rm -f hfrom_format.tmp +test_hfrom_format: update hfrom_format_test + $(SHLIB_ENV) $(VALGRIND) ./hfrom_format_test printfck: $(OBJS) $(PROG) rm -rf printfck @@ -996,6 +986,22 @@ config_known_tcp_ports.o: ../../include/vbuf.h config_known_tcp_ports.o: ../../include/vstring.h config_known_tcp_ports.o: config_known_tcp_ports.c config_known_tcp_ports.o: config_known_tcp_ports.h +config_known_tcp_ports_test.o: ../../include/argv.h +config_known_tcp_ports_test.o: ../../include/check_arg.h +config_known_tcp_ports_test.o: ../../include/known_tcp_ports.h +config_known_tcp_ports_test.o: ../../include/msg.h +config_known_tcp_ports_test.o: ../../include/msg_output.h +config_known_tcp_ports_test.o: ../../include/msg_vstream.h +config_known_tcp_ports_test.o: ../../include/pmock_expect.h +config_known_tcp_ports_test.o: ../../include/ptest.h +config_known_tcp_ports_test.o: ../../include/ptest_main.h +config_known_tcp_ports_test.o: ../../include/stringops.h +config_known_tcp_ports_test.o: ../../include/sys_defs.h +config_known_tcp_ports_test.o: ../../include/vbuf.h +config_known_tcp_ports_test.o: ../../include/vstream.h +config_known_tcp_ports_test.o: ../../include/vstring.h +config_known_tcp_ports_test.o: config_known_tcp_ports.h +config_known_tcp_ports_test.o: config_known_tcp_ports_test.c conv_time.o: ../../include/msg.h conv_time.o: ../../include/sys_defs.h conv_time.o: conv_time.c @@ -1171,6 +1177,26 @@ delivered_hdr.o: quote_822_local.h delivered_hdr.o: quote_flags.h delivered_hdr.o: rec_type.h delivered_hdr.o: record.h +delivered_hdr_test.o: ../../include/argv.h +delivered_hdr_test.o: ../../include/check_arg.h +delivered_hdr_test.o: ../../include/msg.h +delivered_hdr_test.o: ../../include/msg_output.h +delivered_hdr_test.o: ../../include/msg_vstream.h +delivered_hdr_test.o: ../../include/mymalloc.h +delivered_hdr_test.o: ../../include/pmock_expect.h +delivered_hdr_test.o: ../../include/ptest.h +delivered_hdr_test.o: ../../include/ptest_main.h +delivered_hdr_test.o: ../../include/stringops.h +delivered_hdr_test.o: ../../include/sys_defs.h +delivered_hdr_test.o: ../../include/vbuf.h +delivered_hdr_test.o: ../../include/vstream.h +delivered_hdr_test.o: ../../include/vstring.h +delivered_hdr_test.o: delivered_hdr.h +delivered_hdr_test.o: delivered_hdr_test.c +delivered_hdr_test.o: fold_addr.h +delivered_hdr_test.o: mail_params.h +delivered_hdr_test.o: rec_type.h +delivered_hdr_test.o: record.h dict_ldap.o: ../../include/argv.h dict_ldap.o: ../../include/binhash.h dict_ldap.o: ../../include/check_arg.h @@ -1477,6 +1503,7 @@ haproxy_srvr.o: ../../include/sys_defs.h haproxy_srvr.o: ../../include/valid_hostname.h haproxy_srvr.o: ../../include/vbuf.h haproxy_srvr.o: ../../include/vstring.h +haproxy_srvr.o: ../../include/wrap_netdb.h haproxy_srvr.o: haproxy_srvr.c haproxy_srvr.o: haproxy_srvr.h header_body_checks.o: ../../include/argv.h @@ -1524,6 +1551,22 @@ hfrom_format.o: ../../include/sys_defs.h hfrom_format.o: hfrom_format.c hfrom_format.o: hfrom_format.h hfrom_format.o: mail_params.h +hfrom_format_test.o: ../../include/argv.h +hfrom_format_test.o: ../../include/check_arg.h +hfrom_format_test.o: ../../include/msg.h +hfrom_format_test.o: ../../include/msg_output.h +hfrom_format_test.o: ../../include/msg_vstream.h +hfrom_format_test.o: ../../include/pmock_expect.h +hfrom_format_test.o: ../../include/ptest.h +hfrom_format_test.o: ../../include/ptest_main.h +hfrom_format_test.o: ../../include/stringops.h +hfrom_format_test.o: ../../include/sys_defs.h +hfrom_format_test.o: ../../include/vbuf.h +hfrom_format_test.o: ../../include/vstream.h +hfrom_format_test.o: ../../include/vstring.h +hfrom_format_test.o: hfrom_format.h +hfrom_format_test.o: hfrom_format_test.c +hfrom_format_test.o: mail_params.h info_log_addr_form.o: ../../include/check_arg.h info_log_addr_form.o: ../../include/msg.h info_log_addr_form.o: ../../include/name_code.h @@ -1603,6 +1646,24 @@ login_sender_match.o: maps.h login_sender_match.o: quote_822_local.h login_sender_match.o: quote_flags.h login_sender_match.o: strip_addr.h +login_sender_match_test.o: ../../include/argv.h +login_sender_match_test.o: ../../include/check_arg.h +login_sender_match_test.o: ../../include/dict.h +login_sender_match_test.o: ../../include/msg.h +login_sender_match_test.o: ../../include/msg_output.h +login_sender_match_test.o: ../../include/msg_vstream.h +login_sender_match_test.o: ../../include/myflock.h +login_sender_match_test.o: ../../include/pmock_expect.h +login_sender_match_test.o: ../../include/ptest.h +login_sender_match_test.o: ../../include/ptest_main.h +login_sender_match_test.o: ../../include/stringops.h +login_sender_match_test.o: ../../include/sys_defs.h +login_sender_match_test.o: ../../include/vbuf.h +login_sender_match_test.o: ../../include/vstream.h +login_sender_match_test.o: ../../include/vstring.h +login_sender_match_test.o: login_sender_match.h +login_sender_match_test.o: login_sender_match_test.c +login_sender_match_test.o: mail_params.h mail_addr.o: ../../include/check_arg.h mail_addr.o: ../../include/stringops.h mail_addr.o: ../../include/sys_defs.h @@ -1928,6 +1989,7 @@ mail_params.o: ../../include/vbuf.h mail_params.o: ../../include/vstream.h mail_params.o: ../../include/vstring.h mail_params.o: ../../include/vstring_vstream.h +mail_params.o: ../../include/wrap_netdb.h mail_params.o: compat_level.h mail_params.o: config_known_tcp_ports.h mail_params.o: mail_conf.h @@ -2085,6 +2147,22 @@ map_search.o: ../../include/vbuf.h map_search.o: ../../include/vstring.h map_search.o: map_search.c map_search.o: map_search.h +map_search_test.o: ../../include/argv.h +map_search_test.o: ../../include/check_arg.h +map_search_test.o: ../../include/msg.h +map_search_test.o: ../../include/msg_output.h +map_search_test.o: ../../include/msg_vstream.h +map_search_test.o: ../../include/name_code.h +map_search_test.o: ../../include/pmock_expect.h +map_search_test.o: ../../include/ptest.h +map_search_test.o: ../../include/ptest_main.h +map_search_test.o: ../../include/stringops.h +map_search_test.o: ../../include/sys_defs.h +map_search_test.o: ../../include/vbuf.h +map_search_test.o: ../../include/vstream.h +map_search_test.o: ../../include/vstring.h +map_search_test.o: map_search.h +map_search_test.o: map_search_test.c maps.o: ../../include/argv.h maps.o: ../../include/check_arg.h maps.o: ../../include/dict.h @@ -2251,6 +2329,7 @@ mynetworks.o: ../../include/sock_addr.h mynetworks.o: ../../include/sys_defs.h mynetworks.o: ../../include/vbuf.h mynetworks.o: ../../include/vstring.h +mynetworks.o: ../../include/wrap_netdb.h mynetworks.o: been_here.h mynetworks.o: mail_params.h mynetworks.o: mynetworks.c @@ -2281,9 +2360,27 @@ normalize_mailhost_addr.o: ../../include/sys_defs.h normalize_mailhost_addr.o: ../../include/valid_hostname.h normalize_mailhost_addr.o: ../../include/vbuf.h normalize_mailhost_addr.o: ../../include/vstring.h +normalize_mailhost_addr.o: ../../include/wrap_netdb.h normalize_mailhost_addr.o: normalize_mailhost_addr.c normalize_mailhost_addr.o: normalize_mailhost_addr.h normalize_mailhost_addr.o: valid_mailhost_addr.h +normalize_mailhost_addr_test.o: ../../include/argv.h +normalize_mailhost_addr_test.o: ../../include/check_arg.h +normalize_mailhost_addr_test.o: ../../include/inet_proto.h +normalize_mailhost_addr_test.o: ../../include/msg.h +normalize_mailhost_addr_test.o: ../../include/msg_output.h +normalize_mailhost_addr_test.o: ../../include/msg_vstream.h +normalize_mailhost_addr_test.o: ../../include/mymalloc.h +normalize_mailhost_addr_test.o: ../../include/pmock_expect.h +normalize_mailhost_addr_test.o: ../../include/ptest.h +normalize_mailhost_addr_test.o: ../../include/ptest_main.h +normalize_mailhost_addr_test.o: ../../include/stringops.h +normalize_mailhost_addr_test.o: ../../include/sys_defs.h +normalize_mailhost_addr_test.o: ../../include/vbuf.h +normalize_mailhost_addr_test.o: ../../include/vstream.h +normalize_mailhost_addr_test.o: ../../include/vstring.h +normalize_mailhost_addr_test.o: normalize_mailhost_addr.h +normalize_mailhost_addr_test.o: normalize_mailhost_addr_test.c off_cvt.o: ../../include/check_arg.h off_cvt.o: ../../include/msg.h off_cvt.o: ../../include/sys_defs.h @@ -2312,6 +2409,7 @@ own_inet_addr.o: ../../include/stringops.h own_inet_addr.o: ../../include/sys_defs.h own_inet_addr.o: ../../include/vbuf.h own_inet_addr.o: ../../include/vstring.h +own_inet_addr.o: ../../include/wrap_netdb.h own_inet_addr.o: mail_params.h own_inet_addr.o: own_inet_addr.c own_inet_addr.o: own_inet_addr.h @@ -2529,6 +2627,7 @@ resolve_local.o: ../../include/valid_hostname.h resolve_local.o: ../../include/vbuf.h resolve_local.o: ../../include/vstream.h resolve_local.o: ../../include/vstring.h +resolve_local.o: ../../include/wrap_netdb.h resolve_local.o: mail_params.h resolve_local.o: own_inet_addr.h resolve_local.o: resolve_local.c @@ -2678,6 +2777,24 @@ smtp_reply_footer.o: ../../include/vstring.h smtp_reply_footer.o: dsn_util.h smtp_reply_footer.o: smtp_reply_footer.c smtp_reply_footer.o: smtp_reply_footer.h +smtp_reply_footer_test.o: ../../include/argv.h +smtp_reply_footer_test.o: ../../include/check_arg.h +smtp_reply_footer_test.o: ../../include/mac_expand.h +smtp_reply_footer_test.o: ../../include/mac_parse.h +smtp_reply_footer_test.o: ../../include/msg.h +smtp_reply_footer_test.o: ../../include/msg_output.h +smtp_reply_footer_test.o: ../../include/msg_vstream.h +smtp_reply_footer_test.o: ../../include/pmock_expect.h +smtp_reply_footer_test.o: ../../include/ptest.h +smtp_reply_footer_test.o: ../../include/ptest_main.h +smtp_reply_footer_test.o: ../../include/stringops.h +smtp_reply_footer_test.o: ../../include/sys_defs.h +smtp_reply_footer_test.o: ../../include/vbuf.h +smtp_reply_footer_test.o: ../../include/vstream.h +smtp_reply_footer_test.o: ../../include/vstring.h +smtp_reply_footer_test.o: dsn_util.h +smtp_reply_footer_test.o: smtp_reply_footer.h +smtp_reply_footer_test.o: smtp_reply_footer_test.c smtp_stream.o: ../../include/check_arg.h smtp_stream.o: ../../include/iostuff.h smtp_stream.o: ../../include/msg.h @@ -2744,25 +2861,25 @@ sys_exits.o: ../../include/vbuf.h sys_exits.o: ../../include/vstring.h sys_exits.o: sys_exits.c sys_exits.o: sys_exits.h -test_main.o: ../../include/argv.h -test_main.o: ../../include/check_arg.h -test_main.o: ../../include/dict.h -test_main.o: ../../include/msg.h -test_main.o: ../../include/msg_vstream.h -test_main.o: ../../include/myflock.h -test_main.o: ../../include/mymalloc.h -test_main.o: ../../include/stringops.h -test_main.o: ../../include/sys_defs.h -test_main.o: ../../include/vbuf.h -test_main.o: ../../include/vstream.h -test_main.o: ../../include/vstring.h -test_main.o: mail_conf.h -test_main.o: mail_dict.h -test_main.o: mail_params.h -test_main.o: mail_task.h -test_main.o: mail_version.h -test_main.o: test_main.c -test_main.o: test_main.h +test_server_main.o: ../../include/argv.h +test_server_main.o: ../../include/check_arg.h +test_server_main.o: ../../include/dict.h +test_server_main.o: ../../include/msg.h +test_server_main.o: ../../include/msg_vstream.h +test_server_main.o: ../../include/myflock.h +test_server_main.o: ../../include/mymalloc.h +test_server_main.o: ../../include/stringops.h +test_server_main.o: ../../include/sys_defs.h +test_server_main.o: ../../include/vbuf.h +test_server_main.o: ../../include/vstream.h +test_server_main.o: ../../include/vstring.h +test_server_main.o: mail_conf.h +test_server_main.o: mail_dict.h +test_server_main.o: mail_params.h +test_server_main.o: mail_task.h +test_server_main.o: mail_version.h +test_server_main.o: test_server_main.c +test_server_main.o: test_server_main.h timed_ipc.o: ../../include/check_arg.h timed_ipc.o: ../../include/msg.h timed_ipc.o: ../../include/sys_defs.h @@ -2879,6 +2996,7 @@ valid_mailhost_addr.o: ../../include/msg.h valid_mailhost_addr.o: ../../include/myaddrinfo.h valid_mailhost_addr.o: ../../include/sys_defs.h valid_mailhost_addr.o: ../../include/valid_hostname.h +valid_mailhost_addr.o: ../../include/wrap_netdb.h valid_mailhost_addr.o: valid_mailhost_addr.c valid_mailhost_addr.o: valid_mailhost_addr.h verify.o: ../../include/attr.h @@ -2955,6 +3073,7 @@ wildcard_inet_addr.o: ../../include/inet_addr_list.h wildcard_inet_addr.o: ../../include/msg.h wildcard_inet_addr.o: ../../include/myaddrinfo.h wildcard_inet_addr.o: ../../include/sys_defs.h +wildcard_inet_addr.o: ../../include/wrap_netdb.h wildcard_inet_addr.o: wildcard_inet_addr.c wildcard_inet_addr.o: wildcard_inet_addr.h xtext.o: ../../include/check_arg.h diff --git a/postfix/src/global/config_known_tcp_ports.c b/postfix/src/global/config_known_tcp_ports.c index 563bbd356..4a0d43300 100644 --- a/postfix/src/global/config_known_tcp_ports.c +++ b/postfix/src/global/config_known_tcp_ports.c @@ -123,135 +123,3 @@ void config_known_tcp_ports(const char *source, const char *settings) } argv_free(associations); } - -#ifdef TEST - -#include -#include -#include - -#define STR(x) vstring_str(x) - - /* TODO(wietse) make this a proper VSTREAM interface */ - -/* vstream_swap - kludge to capture output for testing */ - -static void vstream_swap(VSTREAM *one, VSTREAM *two) -{ - VSTREAM save; - - save = *one; - *one = *two; - *two = save; -} - -struct test_case { - const char *label; /* identifies test case */ - const char *config; /* configuration under test */ - const char *exp_warning; /* expected warning or null */ - const char *exp_export; /* expected export or null */ -}; - -static struct test_case test_cases[] = { - {"good", - /* config */ "smtp = 25, smtps = submissions = 465, lmtp = 24", - /* warning */ "", - /* export */ "lmtp=24 smtp=25 smtps=465 submissions=465" - }, - {"equal-equal", - /* config */ "smtp = 25, smtps == submissions = 465, lmtp = 24", - /* warning */ "config_known_tcp_ports: warning: equal-equal: " - "in \" smtps == submissions = 465\": missing service name before " - "\"=\"\n", - /* export */ "lmtp=24 smtp=25 smtps=465 submissions=465" - }, - {"port test 1", - /* config */ "smtps = submission =", - /* warning */ "config_known_tcp_ports: warning: port test 1: " - "in \"smtps = submission =\": missing port value after \"=\"\n", - /* export */ "" - }, - {"port test 2", - /* config */ "smtps = submission = 4 65", - /* warning */ "config_known_tcp_ports: warning: port test 2: " - "in \"smtps = submission = 4 65\": whitespace in port number\n", - /* export */ "" - }, - {"port test 3", - /* config */ "lmtp = 24, smtps = submission = foo", - /* warning */ "config_known_tcp_ports: warning: port test 3: " - "in \" smtps = submission = foo\": non-numerical service port\n", - /* export */ "lmtp=24" - }, - {"service name test 1", - /* config */ "smtps = sub mission = 465", - /* warning */ "config_known_tcp_ports: warning: service name test 1: " - "in \"smtps = sub mission = 465\": whitespace in service name\n", - /* export */ "smtps=465" - }, - {"service name test 2", - /* config */ "lmtp = 24, smtps = 1234 = submissions = 465", - /* warning */ "config_known_tcp_ports: warning: service name test 2: " - "in \" smtps = 1234 = submissions = 465\": numerical service name\n", - /* export */ "lmtp=24 smtps=465 submissions=465" - }, - 0, -}; - -int main(int argc, char **argv) -{ - VSTRING *export_buf; - struct test_case *tp; - int pass = 0; - int fail = 0; - int test_failed; - const char *export; - VSTRING *msg_buf; - VSTREAM *memory_stream; - -#define STRING_OR_NULL(s) ((s) ? (s) : "(null)") - - msg_vstream_init("config_known_tcp_ports", VSTREAM_ERR); - - export_buf = vstring_alloc(100); - msg_buf = vstring_alloc(100); - for (tp = test_cases; tp->label != 0; tp++) { - test_failed = 0; - if ((memory_stream = vstream_memopen(msg_buf, O_WRONLY)) == 0) - msg_fatal("open memory stream: %m"); - vstream_swap(VSTREAM_ERR, memory_stream); - config_known_tcp_ports(tp->label, tp->config); - vstream_swap(memory_stream, VSTREAM_ERR); - if (vstream_fclose(memory_stream)) - msg_fatal("close memory stream: %m"); - if (strcmp(STR(msg_buf), tp->exp_warning) != 0) { - msg_warn("test case %s: got error: \"%s\", want: \"%s\"", - tp->label, STR(msg_buf), - STRING_OR_NULL(tp->exp_warning)); - test_failed = 1; - } else { - export = export_known_tcp_ports(export_buf); - if (strcmp(export, tp->exp_export) != 0) { - msg_warn("test case %s: got export: \"%s\", want: \"%s\"", - tp->label, export, tp->exp_export); - test_failed = 1; - } - clear_known_tcp_ports(); - VSTRING_RESET(msg_buf); - VSTRING_TERMINATE(msg_buf); - } - if (test_failed) { - msg_info("%s: FAIL", tp->label); - fail++; - } else { - msg_info("%s: PASS", tp->label); - pass++; - } - } - msg_info("PASS=%d FAIL=%d", pass, fail); - vstring_free(msg_buf); - vstring_free(export_buf); - exit(fail != 0); -} - -#endif diff --git a/postfix/src/global/config_known_tcp_ports_test.c b/postfix/src/global/config_known_tcp_ports_test.c new file mode 100644 index 000000000..e1c331e67 --- /dev/null +++ b/postfix/src/global/config_known_tcp_ports_test.c @@ -0,0 +1,97 @@ + /* + * Test program to exercise config_known_tcp_ports.c. See ptest_main.h for a + * documented example. + */ + + /* + * System library. + */ +#include +#include + + /* + * Utility library. + */ +#include +#include + + /* + * Global library. + */ +#include + + /* + * Test library. + */ +#include + +typedef struct PTEST_CASE { + const char *testname; /* identifies test case */ + void (*action) (PTEST_CTX *, const struct PTEST_CASE *); + const char *config; /* configuration under test */ + const char *want_warning; /* expected warning or null */ + const char *want_export; /* expected export or null */ +} PTEST_CASE; + +static void test_config_known_tcp_ports(PTEST_CTX *t, const PTEST_CASE *tp) +{ + VSTRING *export_buf; + const char *got_export; + + export_buf = vstring_alloc(100); + if (*tp->want_warning) + expect_ptest_error(t, tp->want_warning); + config_known_tcp_ports(tp->testname, tp->config); + got_export = export_known_tcp_ports(export_buf); + if (strcmp(got_export, tp->want_export) != 0) + ptest_error(t, "got export \"%s\", want \"%s\"", + got_export, tp->want_export); + clear_known_tcp_ports(); + vstring_free(export_buf); +} + +static const PTEST_CASE ptestcases[] = { + {"good", test_config_known_tcp_ports, + /* config */ "smtp = 25, smtps = submissions = 465, lmtp = 24", + /* warning */ "", + /* export */ "lmtp=24 smtp=25 smtps=465 submissions=465" + }, + {"equal-equal", test_config_known_tcp_ports, + /* config */ "smtp = 25, smtps == submissions = 465, lmtp = 24", + /* warning */ "equal-equal: in \" smtps == submissions = 465\": " + "missing service name before \"=\"", + /* export */ "lmtp=24 smtp=25 smtps=465 submissions=465" + }, + {"port test 1", test_config_known_tcp_ports, + /* config */ "smtps = submission =", + /* warning */ "port test 1: in \"smtps = submission =\": " + "missing port value after \"=\"", + /* export */ "" + }, + {"port test 2", test_config_known_tcp_ports, + /* config */ "smtps = submission = 4 65", + /* warning */ "port test 2: in \"smtps = submission = 4 65\": " + "whitespace in port number", + /* export */ "" + }, + {"port test 3", test_config_known_tcp_ports, + /* config */ "lmtp = 24, smtps = submission = foo", + /* warning */ "port test 3: in \" smtps = submission = foo\": " + "non-numerical service port", + /* export */ "lmtp=24" + }, + {"service name test 1", test_config_known_tcp_ports, + /* config */ "smtps = sub mission = 465", + /* warning */ "service name test 1: in \"smtps = sub mission = 465\": " + "whitespace in service name", + /* export */ "smtps=465" + }, + {"service name test 2", test_config_known_tcp_ports, + /* config */ "lmtp = 24, smtps = 1234 = submissions = 465", + /* warning */ "service name test 2: in \" smtps = 1234 = submissions " + "= 465\": numerical service name", + /* export */ "lmtp=24 smtps=465 submissions=465" + }, +}; + +#include diff --git a/postfix/src/global/delivered_hdr.c b/postfix/src/global/delivered_hdr.c index 0aea1cc1d..1b20b3856 100644 --- a/postfix/src/global/delivered_hdr.c +++ b/postfix/src/global/delivered_hdr.c @@ -198,69 +198,3 @@ void delivered_hdr_free(DELIVERED_HDR_INFO *info) htable_free(info->table, (void (*) (void *)) 0); myfree((void *) info); } - -#ifdef TEST - -#include -#include - -char *var_drop_hdrs; - -int main(int arc, char **argv) -{ - - /* - * We write test records to a VSTRING, then read with delivered_hdr_init. - */ - VSTRING *mem_bp; - VSTREAM *mem_fp; - DELIVERED_HDR_INFO *dp; - struct test_case { - int rec_type; - const char *content; - int expect_find; - }; - const struct test_case test_cases[] = { - REC_TYPE_CONT, "Delivered-To: one", 1, - REC_TYPE_NORM, "Delivered-To: two", 0, - REC_TYPE_NORM, "Delivered-To: three", 1, - 0, - }; - const struct test_case *tp; - int actual_find; - int errors; - - msg_vstream_init(argv[0], VSTREAM_ERR); - - var_drop_hdrs = mystrdup(DEF_DROP_HDRS); - - mem_bp = vstring_alloc(VSTREAM_BUFSIZE); - if ((mem_fp = vstream_memopen(mem_bp, O_WRONLY)) == 0) - msg_panic("vstream_memopen O_WRONLY failed: %m"); - -#define REC_PUT_LIT(fp, type, lit) rec_put((fp), (type), (lit), strlen(lit)) - - for (tp = test_cases; tp->content != 0; tp++) - REC_PUT_LIT(mem_fp, tp->rec_type, tp->content); - - if (vstream_fclose(mem_fp)) - msg_panic("vstream_fclose fail: %m"); - - if ((mem_fp = vstream_memopen(mem_bp, O_RDONLY)) == 0) - msg_panic("vstream_memopen O_RDONLY failed: %m"); - - dp = delivered_hdr_init(mem_fp, 0, FOLD_ADDR_ALL); - - for (errors = 0, tp = test_cases; tp->content != 0; tp++) { - actual_find = - delivered_hdr_find(dp, tp->content + sizeof("Delivered-To:")); - msg_info("test case: %c >%s<: %s (expected: %s)", - tp->rec_type, tp->content, - actual_find == tp->expect_find ? "PASS" : "FAIL", - tp->expect_find ? "MATCH" : "NO MATCH"); - errors += (actual_find != tp->expect_find);; - } - exit(errors); -} - -#endif diff --git a/postfix/src/global/delivered_hdr_test.c b/postfix/src/global/delivered_hdr_test.c new file mode 100644 index 000000000..d47086710 --- /dev/null +++ b/postfix/src/global/delivered_hdr_test.c @@ -0,0 +1,117 @@ + /* + * Test program to exercise delivered_hdr.c. See ptest_main.h for a + * documented example. + */ + + /* + * System library. + */ +#include +#include + + /* + * Utility library. + */ +#include +#include + + /* + * Global library. + */ +#include +#include +#include +#include + + /* + * Test library. + */ +#include + + /* + * Satisfy a configuration dependency. + */ +char *var_drop_hdrs; + + /* + * Test library adaptor. + */ +typedef struct PTEST_CASE { + const char *testname; + void (*action) (PTEST_CTX *, const struct PTEST_CASE *); +} PTEST_CASE; + +#define FOUND 1 +#define NOT_FOUND 0 + +static void test_delivered_hdr_find(PTEST_CTX *t, const PTEST_CASE *unused) +{ + VSTRING *mem_bp; + VSTREAM *mem_fp; + DELIVERED_HDR_INFO *dp; + struct test_case { + int rec_type; + const char *rec_content; + int want_found; + }; + + /* + * This structure specifies the order of records that will be written to + * a test queue file, and what we expect that delivered_hdr() will find. + * It should not find the record that immediately follows REC_TYPE_CONT. + */ + static const struct test_case test_cases[] = { + REC_TYPE_CONT, "Delivered-To: one", FOUND, + REC_TYPE_NORM, "Delivered-To: two", NOT_FOUND, + REC_TYPE_NORM, "Delivered-To: three", FOUND, + 0, + }; + const struct test_case *tp; + int got_found; + + var_drop_hdrs = mystrdup(DEF_DROP_HDRS); + + /* + * Write queue file records to memory stream. + */ +#define REC_PUT_LIT(fp, type, lit) rec_put((fp), (type), (lit), strlen(lit)) + + mem_bp = vstring_alloc(VSTREAM_BUFSIZE); + if ((mem_fp = vstream_memopen(mem_bp, O_WRONLY)) == 0) + ptest_fatal(t, "vstream_memopen O_WRONLY failed: %m"); + for (tp = test_cases; tp->rec_content != 0; tp++) + REC_PUT_LIT(mem_fp, tp->rec_type, tp->rec_content); + if (vstream_fclose(mem_fp)) + ptest_fatal(t, "vstream_fclose fail: %m"); + + /* + * Reopen the memory stream and populate the Delivered-To: cache. + */ + if ((mem_fp = vstream_memopen(mem_bp, O_RDONLY)) == 0) + ptest_fatal(t, "vstream_memopen O_RDONLY failed: %m"); + dp = delivered_hdr_init(mem_fp, 0, FOLD_ADDR_ALL); + + /* + * Verify that what should be found will be found. XXX delivered_hdr() + * assumes that Delivered-To: records fit in one queue file record. + */ +#define FOUND_OR_NOT(x) ((x) ? "found" : "not found") + + for (tp = test_cases; tp->rec_content != 0; tp++) { + got_found = + delivered_hdr_find(dp, tp->rec_content + sizeof("Delivered-To:")); + if (!got_found != !tp->want_found) + ptest_error(t, "delivered_hdr_find '%s': got '%s', want '%s'", + tp->rec_content, FOUND_OR_NOT(got_found), + FOUND_OR_NOT(tp->want_found)); + } +} + + /* + * Test library adaptor. + */ +static PTEST_CASE ptestcases[] = { + "test_delivered_hdr_find", test_delivered_hdr_find, +}; + +#include diff --git a/postfix/src/global/dict_mysql.c b/postfix/src/global/dict_mysql.c index d90bc090d..00e37ac3d 100644 --- a/postfix/src/global/dict_mysql.c +++ b/postfix/src/global/dict_mysql.c @@ -890,7 +890,9 @@ static HOST *host_init(const char *hostname) } host->name = mystrdup(d); if ((s = split_at_right(host->name, ':')) != 0) - host->port = ntohs(find_inet_port(s, "tcp")); + if ((host->port = find_inet_service(s, "tcp")) < 0) + /* TODO: return null and create a surrogate dictionary. */ + msg_fatal("unknown service: %s/tcp", s); if (strcasecmp(host->name, "localhost") == 0) { /* The MySQL way: this will actually connect over the UNIX socket */ myfree(host->name); diff --git a/postfix/src/global/fold_addr.h b/postfix/src/global/fold_addr.h index ba8021df2..67008154b 100644 --- a/postfix/src/global/fold_addr.h +++ b/postfix/src/global/fold_addr.h @@ -11,6 +11,11 @@ /* DESCRIPTION /* .nf + /* + * Utility library. + */ +#include + /* * External interface. */ diff --git a/postfix/src/global/hfrom_format.c b/postfix/src/global/hfrom_format.c index f0f850a3e..8510d8785 100644 --- a/postfix/src/global/hfrom_format.c +++ b/postfix/src/global/hfrom_format.c @@ -119,7 +119,7 @@ static void vstream_swap(VSTREAM *one, VSTREAM *two) *two = save; } -jmp_buf test_fatal_jbuf; +jmp_buf ptest_fatal_jbuf; #undef msg_fatal @@ -132,7 +132,7 @@ void test_msg_fatal(const char *fmt,...) va_start(ap, fmt); vmsg_warn(fmt, ap); va_end(ap); - longjmp(test_fatal_jbuf, 1); + longjmp(ptest_fatal_jbuf, 1); } struct name_test_case { @@ -214,7 +214,7 @@ int main(int argc, char **argv) if ((memory_stream = vstream_memopen(msg_buf, O_WRONLY)) == 0) msg_fatal("open memory stream: %m"); vstream_swap(VSTREAM_ERR, memory_stream); - if (setjmp(test_fatal_jbuf) == 0) + if (setjmp(ptest_fatal_jbuf) == 0) code = hfrom_format_parse(np->label, np->config); vstream_swap(memory_stream, VSTREAM_ERR); if (vstream_fclose(memory_stream)) @@ -248,7 +248,7 @@ int main(int argc, char **argv) if ((memory_stream = vstream_memopen(msg_buf, O_WRONLY)) == 0) msg_fatal("open memory stream: %m"); vstream_swap(VSTREAM_ERR, memory_stream); - if (setjmp(test_fatal_jbuf) == 0) + if (setjmp(ptest_fatal_jbuf) == 0) name = str_hfrom_format_code(cp->code); vstream_swap(memory_stream, VSTREAM_ERR); if (vstream_fclose(memory_stream)) diff --git a/postfix/src/global/hfrom_format.ref b/postfix/src/global/hfrom_format.ref deleted file mode 100644 index 28ba8708a..000000000 --- a/postfix/src/global/hfrom_format.ref +++ /dev/null @@ -1,8 +0,0 @@ -hfrom_format: hfrom_format_parse good-standard: PASS -hfrom_format: hfrom_format_parse good-obsolete: PASS -hfrom_format: hfrom_format_parse bad: PASS -hfrom_format: hfrom_format_parse empty: PASS -hfrom_format: str_hfrom_format_code good-standard: PASS -hfrom_format: str_hfrom_format_code good-obsolete: PASS -hfrom_format: str_hfrom_format_code bad: PASS -hfrom_format: PASS=7 FAIL=0 diff --git a/postfix/src/global/hfrom_format_test.c b/postfix/src/global/hfrom_format_test.c new file mode 100644 index 000000000..9a6fe2e18 --- /dev/null +++ b/postfix/src/global/hfrom_format_test.c @@ -0,0 +1,139 @@ + /* + * Test program to exercise hfrom_format.c. See ptest_main.h for a + * documented example. + */ + + /* + * System library. + */ +#include +#include + + /* + * Global library. + */ +#include +#include + + /* + * Test library. + */ +#include + + /* + * Test adapter. + */ +typedef struct PTEST_CASE { + const char *testname; + void (*action) (PTEST_CTX *, const struct PTEST_CASE *); +} PTEST_CASE; + +struct name_test_case { + const char *label; /* identifies test case */ + const char *config; /* configuration under test */ + const char *want_warning; /* expected warning or empty */ + const int want_code; /* expected code */ +}; + +static struct name_test_case name_test_cases[] = { + {"hfrom_format_parse good-standard", + /* config */ HFROM_FORMAT_NAME_STD, + /* warning */ "", + /* want_code */ HFROM_FORMAT_CODE_STD + }, + {"hfrom_format_parse good-obsolete", + /* config */ HFROM_FORMAT_NAME_OBS, + /* warning */ "", + /* want_code */ HFROM_FORMAT_CODE_OBS + }, + {"hfrom_format_parse bad", + /* config */ "does-not-exist,", + /* warning */ "invalid setting: \"hfrom_format_parse bad = does-not-exist,\"", + /* code */ 0, + }, + {"hfrom_format_parse empty", + /* config */ "", + /* warning */ "invalid setting: \"hfrom_format_parse empty = \"", + /* code */ 0, + }, + 0, +}; + +static void test_hfrom_format_parse(PTEST_CTX *t, const PTEST_CASE *tp) +{ + struct name_test_case *np; + + for (np = name_test_cases; np->label != 0; np++) { + PTEST_RUN(t, np->label, { + int got_code; + + if (*np->want_warning) + expect_ptest_error(t, np->want_warning); + got_code = hfrom_format_parse(np->label, np->config); + if (*np->want_warning == 0) { + if (got_code != np->want_code) { + ptest_error(t, "got code \"%d\", want \"%d\"(%s)", + got_code, np->want_code, + str_hfrom_format_code(np->want_code)); + } + } + }); + } +} + +struct code_test_case { + const char *label; /* identifies test case */ + int code; /* code under test */ + const char *want_warning; /* expected warning or empty */ + const char *want_name; /* expected namme */ +}; + +static struct code_test_case code_test_cases[] = { + {"str_hfrom_format_code good-standard", + /* code */ HFROM_FORMAT_CODE_STD, + /* warning */ "", + /* want_name */ HFROM_FORMAT_NAME_STD + }, + {"str_hfrom_format_code good-obsolete", + /* code */ HFROM_FORMAT_CODE_OBS, + /* warning */ "", + /* want_name */ HFROM_FORMAT_NAME_OBS + }, + {"str_hfrom_format_code bad", + /* config */ 12345, + /* warning */ "invalid header format code: 12345", + /* want_name */ 0 + }, + 0, +}; + +static void test_str_hfrom_format_code(PTEST_CTX *t, const PTEST_CASE *tp) +{ + struct code_test_case *cp; + + for (cp = code_test_cases; cp->label != 0; cp++) { + PTEST_RUN(t, cp->label, { + const char *got_name; + + if (*cp->want_warning) + expect_ptest_error(t, cp->want_warning); + got_name = str_hfrom_format_code(cp->code); + if (*cp->want_warning == 0) { + if (strcmp(got_name, cp->want_name) != 0) { + ptest_error(t, "got name: \"%s\", want: \"%s\"", + got_name, cp->want_name); + } + } + }); + } +} + + /* + * Test adapter. + */ +static const PTEST_CASE ptestcases[] = { + {"test hfrom_format_parse", test_hfrom_format_parse,}, + {"test str_hfrom_format_code", test_str_hfrom_format_code,}, +}; + +#include diff --git a/postfix/src/global/login_sender_match.c b/postfix/src/global/login_sender_match.c index e263762ef..c03c53f3d 100644 --- a/postfix/src/global/login_sender_match.c +++ b/postfix/src/global/login_sender_match.c @@ -234,131 +234,3 @@ int login_sender_match(LOGIN_SENDER_MATCH *lsm, const char *login_name, } return (found_or_error); } - -#ifdef TEST - -int main(int argc, char **argv) -{ - struct testcase { - const char *title; - const char *map_names; - const char *ext_delimiters; - const char *null_sender; - const char *wildcard; - const char *login_name; - const char *sender_addr; - int exp_return; - }; - struct testcase testcases[] = { - {"wildcard works", - "inline:{root=*, {foo = foo,foo@example.com}, bar=<>}", - "+-", "<>", "*", "root", "anything", LSM_STAT_FOUND - }, - {"unknown user", - "inline:{root=*, {foo = foo,foo@example.com}, bar=<>}", - "+-", "<>", "*", "toor", "anything", LSM_STAT_NOTFOUND - }, - {"bare user", - "inline:{root=*, {foo = foo,foo@example.com}, bar=<>}", - "+-", "<>", "*", "foo", "foo", LSM_STAT_FOUND - }, - {"user@domain", - "inline:{root=*, {foo = foo,foo@example.com}, bar=<>}", - "+-", "<>", "*", "foo", "foo@example.com", LSM_STAT_FOUND - }, - {"user+ext@domain", - "inline:{root=*, {foo = foo,foo@example.com}, bar=<>}", - "+-", "<>", "*", "foo", "foo+bar@example.com", LSM_STAT_FOUND - }, - {"wrong sender", - "inline:{root=*, {foo = foo,foo@example.com}, bar=<>}", - "+-", "<>", "*", "foo", "bar@example.com", LSM_STAT_NOTFOUND - }, - {"@domain", - "inline:{root=*, {foo = @example.com}, bar=<>}", - "+-", "<>", "*", "foo", "anyone@example.com", LSM_STAT_FOUND - }, - {"wrong @domain", - "inline:{root=*, {foo = @example.com}, bar=<>}", - "+-", "<>", "*", "foo", "anyone@example.org", LSM_STAT_NOTFOUND - }, - {"null sender", - "inline:{root=*, {foo = foo,foo@example.com}, bar=<>}", - "+-", "<>", "*", "bar", "", LSM_STAT_FOUND - }, - {"wrong null sender", - "inline:{root=*, {foo = foo,foo@example.com}, bar=<>}", - "+-", "<>", "*", "baz", "", LSM_STAT_NOTFOUND - }, - {"error", - "inline:{root=*}, fail:sorry", - "+-", "<>", "*", "baz", "whatever", LSM_STAT_RETRY - }, - {"no error", - "inline:{root=*}, fail:sorry", - "+-", "<>", "*", "root", "whatever", LSM_STAT_FOUND - }, - {"unknown uid:number", - "inline:{root=*, {uid:12345 = foo,foo@example.com}, bar=<>}", - "+-", "<>", "*", "uid:54321", "foo", LSM_STAT_NOTFOUND - }, - {"known uid:number", - "inline:{root=*, {uid:12345 = foo,foo@example.com}, bar=<>}", - "+-", "<>", "*", "uid:12345", "foo", LSM_STAT_FOUND - }, - {"unknown \"other last\"", - "inline:{root=*, {foo = \"first last\",\"first last\"@example.com}, bar=<>}", - "+-", "<>", "*", "foo", "other last", LSM_STAT_NOTFOUND - }, - {"bare \"first last\"", - "inline:{root=*, {foo = \"first last\",\"first last\"@example.com}, bar=<>}", - "+-", "<>", "*", "foo", "first last", LSM_STAT_FOUND - }, - {"\"first last\"@domain", - "inline:{root=*, {foo = \"first last\",\"first last\"@example.com}, bar=<>}", - "+-", "<>", "*", "foo", "first last@example.com", LSM_STAT_FOUND - }, - }; - struct testcase *tp; - int act_return; - int pass; - int fail; - LOGIN_SENDER_MATCH *lsm; - - /* - * Fake variable settings. - */ - var_double_bounce_sender = DEF_DOUBLE_BOUNCE; - var_ownreq_special = DEF_OWNREQ_SPECIAL; - -#define NUM_TESTS sizeof(testcases)/sizeof(testcases[0]) - - for (pass = fail = 0, tp = testcases; tp < testcases + NUM_TESTS; tp++) { - msg_info("RUN test case %ld %s", (long) (tp - testcases), tp->title); -#if 0 - msg_info("title=%s", tp->title); - msg_info("map_names=%s", tp->map_names); - msg_info("ext_delimiters=%s", tp->ext_delimiters); - msg_info("null_sender=%s", tp->null_sender); - msg_info("wildcard=%s", tp->wildcard); - msg_info("login_name=%s", tp->login_name); - msg_info("sender_addr=%s", tp->sender_addr); - msg_info("exp_return=%d", tp->exp_return); -#endif - lsm = login_sender_create("test map", tp->map_names, - tp->ext_delimiters, tp->null_sender, - tp->wildcard); - act_return = login_sender_match(lsm, tp->login_name, tp->sender_addr); - if (act_return == tp->exp_return) { - msg_info("PASS test %ld", (long) (tp - testcases)); - pass++; - } else { - msg_info("FAIL test %ld", (long) (tp - testcases)); - fail++; - } - login_sender_free(lsm); - } - return (fail > 0); -} - -#endif /* TEST */ diff --git a/postfix/src/global/login_sender_match.ref b/postfix/src/global/login_sender_match.ref deleted file mode 100644 index 20ea4832f..000000000 --- a/postfix/src/global/login_sender_match.ref +++ /dev/null @@ -1,35 +0,0 @@ -unknown: RUN test case 0 wildcard works -unknown: PASS test 0 -unknown: RUN test case 1 unknown user -unknown: PASS test 1 -unknown: RUN test case 2 bare user -unknown: PASS test 2 -unknown: RUN test case 3 user@domain -unknown: PASS test 3 -unknown: RUN test case 4 user+ext@domain -unknown: PASS test 4 -unknown: RUN test case 5 wrong sender -unknown: PASS test 5 -unknown: RUN test case 6 @domain -unknown: PASS test 6 -unknown: RUN test case 7 wrong @domain -unknown: PASS test 7 -unknown: RUN test case 8 null sender -unknown: PASS test 8 -unknown: RUN test case 9 wrong null sender -unknown: PASS test 9 -unknown: RUN test case 10 error -unknown: warning: fail:sorry lookup error for "baz" -unknown: PASS test 10 -unknown: RUN test case 11 no error -unknown: PASS test 11 -unknown: RUN test case 12 unknown uid:number -unknown: PASS test 12 -unknown: RUN test case 13 known uid:number -unknown: PASS test 13 -unknown: RUN test case 14 unknown "other last" -unknown: PASS test 14 -unknown: RUN test case 15 bare "first last" -unknown: PASS test 15 -unknown: RUN test case 16 "first last"@domain -unknown: PASS test 16 diff --git a/postfix/src/global/login_sender_match_test.c b/postfix/src/global/login_sender_match_test.c new file mode 100644 index 000000000..16a366f03 --- /dev/null +++ b/postfix/src/global/login_sender_match_test.c @@ -0,0 +1,142 @@ + /* + * Test program to exercise login_sender_match.c. See and ptest_main.h for a + * documented example. + */ + + /* + * System library. + */ +#include + + /* + * Global library. + */ +#include +#include + + /* + * Test library. + */ +#include + +typedef struct PTEST_CASE { + const char *testname; + void (*action) (PTEST_CTX *, const struct PTEST_CASE *tp); + const char *map_names; + const char *ext_delimiters; + const char *null_sender; + const char *wildcard; + const char *login_name; + const char *sender_addr; + int want_return; + const char *want_logging; +} PTEST_CASE; + +static void tester(PTEST_CTX *t, const PTEST_CASE *tp) +{ + int got_return; + LOGIN_SENDER_MATCH *lsm; + + /* + * Fake variable settings. + */ + var_double_bounce_sender = DEF_DOUBLE_BOUNCE; + var_ownreq_special = DEF_OWNREQ_SPECIAL; + +#define NUM_TESTS sizeof(ptestcases)/sizeof(ptestcases[0]) + +#if 0 + msg_info("name=%s", tp->title); + msg_info("map_names=%s", tp->map_names); + msg_info("ext_delimiters=%s", tp->ext_delimiters); + msg_info("null_sender=%s", tp->null_sender); + msg_info("wildcard=%s", tp->wildcard); + msg_info("login_name=%s", tp->login_name); + msg_info("sender_addr=%s", tp->sender_addr); + msg_info("want_return=%d", tp->exp_return); +#endif + lsm = login_sender_create("test map", tp->map_names, + tp->ext_delimiters, tp->null_sender, + tp->wildcard); + if (tp->want_logging) + expect_ptest_log_event(t, tp->want_logging); + got_return = login_sender_match(lsm, tp->login_name, tp->sender_addr); + if (got_return != tp->want_return) + ptest_error(t, "login_sender_match() got %d, want %d", + got_return, tp->want_return); + login_sender_free(lsm); +} + +static const PTEST_CASE ptestcases[] = { + {"wildcard works", tester, + "inline:{root=*, {foo = foo,foo@example.com}, bar=<>}", + "+-", "<>", "*", "root", "anything", LSM_STAT_FOUND + }, + {"unknown user", tester, + "inline:{root=*, {foo = foo,foo@example.com}, bar=<>}", + "+-", "<>", "*", "toor", "anything", LSM_STAT_NOTFOUND + }, + {"bare user", tester, + "inline:{root=*, {foo = foo,foo@example.com}, bar=<>}", + "+-", "<>", "*", "foo", "foo", LSM_STAT_FOUND + }, + {"user@domain", tester, + "inline:{root=*, {foo = foo,foo@example.com}, bar=<>}", + "+-", "<>", "*", "foo", "foo@example.com", LSM_STAT_FOUND + }, + {"user+ext@domain", tester, + "inline:{root=*, {foo = foo,foo@example.com}, bar=<>}", + "+-", "<>", "*", "foo", "foo+bar@example.com", LSM_STAT_FOUND + }, + {"wrong sender", tester, + "inline:{root=*, {foo = foo,foo@example.com}, bar=<>}", + "+-", "<>", "*", "foo", "bar@example.com", LSM_STAT_NOTFOUND + }, + {"@domain", tester, + "inline:{root=*, {foo = @example.com}, bar=<>}", + "+-", "<>", "*", "foo", "anyone@example.com", LSM_STAT_FOUND + }, + {"wrong @domain", tester, + "inline:{root=*, {foo = @example.com}, bar=<>}", + "+-", "<>", "*", "foo", "anyone@example.org", LSM_STAT_NOTFOUND + }, + {"null sender", tester, + "inline:{root=*, {foo = foo,foo@example.com}, bar=<>}", + "+-", "<>", "*", "bar", "", LSM_STAT_FOUND + }, + {"wrong null sender", tester, + "inline:{root=*, {foo = foo,foo@example.com}, bar=<>}", + "+-", "<>", "*", "baz", "", LSM_STAT_NOTFOUND + }, + {"error", tester, + "inline:{root=*}, fail:sorry", + "+-", "<>", "*", "baz", "whatever", LSM_STAT_RETRY, + "fail:sorry lookup error" + }, + {"no error", tester, + "inline:{root=*}, fail:sorry", + "+-", "<>", "*", "root", "whatever", LSM_STAT_FOUND + }, + {"unknown uid:number", tester, + "inline:{root=*, {uid:12345 = foo,foo@example.com}, bar=<>}", + "+-", "<>", "*", "uid:54321", "foo", LSM_STAT_NOTFOUND + }, + {"known uid:number", tester, + "inline:{root=*, {uid:12345 = foo,foo@example.com}, bar=<>}", + "+-", "<>", "*", "uid:12345", "foo", LSM_STAT_FOUND + }, + {"unknown \"other last\"", tester, + "inline:{root=*, {foo = \"first last\",\"first last\"@example.com}, bar=<>}", + "+-", "<>", "*", "foo", "other last", LSM_STAT_NOTFOUND + }, + {"bare \"first last\"", tester, + "inline:{root=*, {foo = \"first last\",\"first last\"@example.com}, bar=<>}", + "+-", "<>", "*", "foo", "first last", LSM_STAT_FOUND + }, + {"\"first last\"@domain", tester, + "inline:{root=*, {foo = \"first last\",\"first last\"@example.com}, bar=<>}", + "+-", "<>", "*", "foo", "first last@example.com", LSM_STAT_FOUND + }, +}; + +#include diff --git a/postfix/src/global/mail_dict.c b/postfix/src/global/mail_dict.c index c640a807a..1c391fbd8 100644 --- a/postfix/src/global/mail_dict.c +++ b/postfix/src/global/mail_dict.c @@ -114,7 +114,7 @@ int main(int argc, char **argv) var_proxywrite_service = DEF_PROXYWRITE_SERVICE; var_ipc_timeout = 3600; mail_dict_init(); - dict_test(argc, argv); + dict_cli(argc, argv); return (0); } diff --git a/postfix/src/global/map_search.c b/postfix/src/global/map_search.c index be4b42b33..497fbb904 100644 --- a/postfix/src/global/map_search.c +++ b/postfix/src/global/map_search.c @@ -251,145 +251,3 @@ const MAP_SEARCH *map_search_lookup(const char *map_spec) return ((MAP_SEARCH *) htable_find(map_search_table, map_spec)); } - - /* - * Test driver. - */ -#ifdef TEST -#include - - /* - * Test search actions. - */ -#define TEST_NAME_1 "one" -#define TEST_NAME_2 "two" -#define TEST_CODE_1 1 -#define TEST_CODE_2 2 - -#define BAD_NAME "bad" - -static const NAME_CODE search_actions[] = { - TEST_NAME_1, TEST_CODE_1, - TEST_NAME_2, TEST_CODE_2, - 0, MAP_SEARCH_CODE_UNKNOWN, -}; - -/* Helpers to simplify tests. */ - -static const char *string_or_null(const char *s) -{ - return (s ? s : "(null)"); -} - -static char *escape_order(VSTRING *buf, const char *search_order) -{ - return (STR(escape(buf, search_order, strlen(search_order)))); -} - -int main(int argc, char **argv) -{ - /* Test cases with inputs and expected outputs. */ - typedef struct TEST_CASE { - const char *map_spec; - int exp_return; /* 0=fail, 1=success */ - const char *exp_map_type_name; /* 0 or match */ - const char *exp_search_order; /* 0 or match */ - } TEST_CASE; - static TEST_CASE test_cases[] = { - {"type", 0, 0, 0}, - {"type:name", 1, "type:name", 0}, - {"{type:name}", 1, "type:name", 0}, - {"{type:name", 0, 0, 0}, /* } */ - {"{type}", 0, 0, 0}, - {"{type:name foo}", 0, 0, 0}, - {"{type:name foo=bar}", 0, 0, 0}, - {"{type:name search_order=}", 1, "type:name", ""}, - {"{type:name search_order=one, two}", 0, 0, 0}, - {"{type:name {search_order=one, two}}", 1, "type:name", "\01\02"}, - {"{type:name {search_order=one, two, bad}}", 0, 0, 0}, - {"{inline:{a=b} {search_order=one, two}}", 1, "inline:{a=b}", "\01\02"}, - {0}, - }; - TEST_CASE *test_case; - - /* Actual results. */ - const MAP_SEARCH *map_search_from_create; - const MAP_SEARCH *map_search_from_create_2nd; - const MAP_SEARCH *map_search_from_lookup; - - /* Findings. */ - int tests_failed = 0; - int test_failed; - - /* Scratch */ - VSTRING *expect_escaped = vstring_alloc(100); - VSTRING *actual_escaped = vstring_alloc(100); - - map_search_init(search_actions); - - for (tests_failed = 0, test_case = test_cases; test_case->map_spec; - tests_failed += test_failed, test_case++) { - test_failed = 0; - msg_info("test case %d: '%s'", - (int) (test_case - test_cases), test_case->map_spec); - map_search_from_create = map_search_create(test_case->map_spec); - if (!test_case->exp_return != !map_search_from_create) { - if (map_search_from_create) - msg_warn("test case %d return expected %s actual {%s, %s}", - (int) (test_case - test_cases), - test_case->exp_return ? "success" : "fail", - map_search_from_create->map_type_name, - escape_order(actual_escaped, - map_search_from_create->search_order)); - else - msg_warn("test case %d return expected %s actual %s", - (int) (test_case - test_cases), "success", - map_search_from_create ? "success" : "fail"); - test_failed = 1; - continue; - } - if (test_case->exp_return == 0) - continue; - map_search_from_lookup = map_search_lookup(test_case->map_spec); - if (map_search_from_create != map_search_from_lookup) { - msg_warn("test case %d map_search_lookup expected=%p actual=%p", - (int) (test_case - test_cases), - map_search_from_create, map_search_from_lookup); - test_failed = 1; - } - map_search_from_create_2nd = map_search_create(test_case->map_spec); - if (map_search_from_create != map_search_from_create_2nd) { - msg_warn("test case %d repeated map_search_create " - "expected=%p actual=%p", - (int) (test_case - test_cases), - map_search_from_create, map_search_from_create_2nd); - test_failed = 1; - } - if (strcmp(string_or_null(test_case->exp_map_type_name), - string_or_null(map_search_from_create->map_type_name))) { - msg_warn("test case %d map_type_name expected=%s actual=%s", - (int) (test_case - test_cases), - string_or_null(test_case->exp_map_type_name), - string_or_null(map_search_from_create->map_type_name)); - test_failed = 1; - } - if (strcmp(string_or_null(test_case->exp_search_order), - string_or_null(map_search_from_create->search_order))) { - msg_warn("test case %d search_order expected=%s actual=%s", - (int) (test_case - test_cases), - escape_order(expect_escaped, - string_or_null(test_case->exp_search_order)), - escape_order(actual_escaped, - string_or_null(map_search_from_create->search_order))); - test_failed = 1; - } - } - vstring_free(expect_escaped); - vstring_free(actual_escaped); - - if (tests_failed) - msg_info("tests failed: %d", tests_failed); - exit(tests_failed != 0); -} - -#endif diff --git a/postfix/src/global/map_search.ref b/postfix/src/global/map_search.ref deleted file mode 100644 index f072f253e..000000000 --- a/postfix/src/global/map_search.ref +++ /dev/null @@ -1,21 +0,0 @@ -unknown: test case 0: 'type' -unknown: warning: malformed map specification: 'type' -unknown: warning: expected maptype:mapname instead of 'type' -unknown: test case 1: 'type:name' -unknown: test case 2: '{type:name}' -unknown: test case 3: '{type:name' -unknown: warning: malformed map specification: 'missing '}' in "{type:name"' -unknown: test case 4: '{type}' -unknown: warning: malformed map specification: '{type}' -unknown: warning: expected maptype:mapname instead of 'type' -unknown: test case 5: '{type:name foo}' -unknown: warning: malformed map attribute in '{type:name foo}': 'missing '=' after attribute name' -unknown: test case 6: '{type:name foo=bar}' -unknown: warning: unknown map attribute in '{type:name foo=bar}': 'foo' -unknown: test case 7: '{type:name search_order=}' -unknown: test case 8: '{type:name search_order=one, two}' -unknown: warning: malformed map attribute in '{type:name search_order=one, two}': 'missing '=' after attribute name' -unknown: test case 9: '{type:name {search_order=one, two}}' -unknown: test case 10: '{type:name {search_order=one, two, bad}}' -unknown: warning: unknown search type 'bad' in '{type:name {search_order=one, two, bad}}' -unknown: test case 11: '{inline:{a=b} {search_order=one, two}}' diff --git a/postfix/src/global/map_search_test.c b/postfix/src/global/map_search_test.c new file mode 100644 index 000000000..ef8f88c7c --- /dev/null +++ b/postfix/src/global/map_search_test.c @@ -0,0 +1,189 @@ + /* + * Test program to exercise map_search.c. See ptest_main.h for a documented + * example. + */ + + /* + * System library. + */ +#include +#include +#include + + /* + * Utility library. + */ +#include +#include + + /* + * Global library. + */ +#include + + /* + * Test library. + */ +#include + + /* + * Test search actions. + */ +#define TEST_NAME_1 "one" +#define TEST_NAME_2 "two" +#define TEST_CODE_1 1 +#define TEST_CODE_2 2 + +#define BAD_NAME "bad" + +#define STR(x) vstring_str(x) + +static const NAME_CODE search_actions[] = { + TEST_NAME_1, TEST_CODE_1, + TEST_NAME_2, TEST_CODE_2, + 0, MAP_SEARCH_CODE_UNKNOWN, +}; + + /* + * Test library adaptor. + */ +typedef struct PTEST_CASE { + const char *testname; + void (*action) (PTEST_CTX *, const struct PTEST_CASE *); +} PTEST_CASE; + +/* Helpers to simplify tests. */ + +static const char *string_or_null(const char *s) +{ + return (s ? s : "(null)"); +} + +static char *escape_order(VSTRING *buf, const char *search_order) +{ + return (STR(escape(buf, search_order, strlen(search_order)))); +} + +#define MAX_WANT_LOG 5 + +static void test_map_search(PTEST_CTX *t, const struct PTEST_CASE *unused) +{ + /* Test cases with inputs and expected outputs. */ + struct test { + const char *map_spec; + int want_return; /* 0=fail, 1=success */ + const char *want_log[MAX_WANT_LOG]; + const char *want_map_type_name; /* 0 or match */ + const char *exp_search_order; /* 0 or match */ + }; + static struct test test_cases[] = { + { /* 0 */ "type", 0, { + "malformed map specification: 'type'", + "expected maptype:mapname instead of 'type'", + }, 0}, + { /* 1 */ "type:name", 1, {}, "type:name", 0}, + { /* 2 */ "{type:name}", 1, {}, "type:name", 0}, + { /* 3 */ "{type:name", 0, { + "missing '}' in \"{type:name\"" + }, 0, 0}, /* } */ + { /* 4 */ "{type}", 0, { + "malformed map specification: '{type}'", + "expected maptype:mapname instead of 'type'", + }, 0, 0}, + { /* 5 */ "{type:name foo}", 0, { + "missing '=' after attribute name", + }, 0, 0}, + { /* 6 */ "{type:name foo=bar}", 0, { + "warning: unknown map attribute in '{type:name foo=bar}': 'foo'", + }, 0, 0}, + { /* 7 */ "{type:name search_order=}", 1, {}, "type:name", ""}, + { /* 8 */ "{type:name search_order=one, two}", 0, { + "missing '=' after attribute name'", + }, 0, 0}, + { /* 9 */ "{type:name {search_order=one, two}}", 1, { + }, "type:name", "\01\02"}, + { /* 10 */ "{type:name {search_order=one, two, bad}}", 0, { + "'bad' in '{type:name {search_order=one, two, bad}}'", + }, 0, 0}, + { /* 11 */ "{inline:{a=b} {search_order=one, two}}", 1, { + }, "inline:{a=b}", "\01\02"}, + {0}, + }; + struct test *tp; + const char *const * cpp; + + /* Actual results. */ + const MAP_SEARCH *map_search_from_create; + const MAP_SEARCH *map_search_from_create_2nd; + const MAP_SEARCH *map_search_from_lookup; + + /* Scratch */ + VSTRING *want_escaped = vstring_alloc(100); + VSTRING *got_escaped = vstring_alloc(100); + + /* Other */ + VSTRING *test_label = vstring_alloc(100); + + map_search_init(search_actions); + + for (tp = test_cases; tp->map_spec; tp++) { + vstring_sprintf(test_label, "test %d", (int) (tp - test_cases)); +/**INDENT** Error@126: Unbalanced parens */ + PTEST_RUN(t, STR(test_label), { + for (cpp = tp->want_log; cpp < tp->want_log + MAX_WANT_LOG && *cpp; cpp++) + expect_ptest_log_event(t, *cpp); + map_search_from_create = map_search_create(tp->map_spec); + if (!tp->want_return != !map_search_from_create) { + if (map_search_from_create) + ptest_fatal(t, "return: got {%s, %s}, want '%s'", + map_search_from_create->map_type_name, + escape_order(got_escaped, + map_search_from_create->search_order), + tp->want_return ? "success" : "fail"); + else + ptest_fatal(t, "return: got '%s', want '%s'", + map_search_from_create ? "success" : "fail", + "success"); + } + if (tp->want_return == 0) + ptest_return(t); + map_search_from_lookup = map_search_lookup(tp->map_spec); + if (map_search_from_create != map_search_from_lookup) { + ptest_error(t, "map_search_lookup: got %p, want %p", + map_search_from_lookup, map_search_from_create); + } + map_search_from_create_2nd = map_search_create(tp->map_spec); + if (map_search_from_create != map_search_from_create_2nd) { + ptest_error(t, "repeated map_search_create: got %p want %p", + map_search_from_create_2nd, map_search_from_create); + } + if (strcmp(string_or_null(tp->want_map_type_name), + string_or_null(map_search_from_create->map_type_name))) { + ptest_error(t, "map_type_name: got '%s', want '%s'", + string_or_null(map_search_from_create->map_type_name), + string_or_null(tp->want_map_type_name)); + } + if (strcmp(string_or_null(tp->exp_search_order), + string_or_null(map_search_from_create->search_order))) { + ptest_error(t, "search_order: got '%s', want '%s'", + escape_order(got_escaped, + string_or_null(map_search_from_create->search_order)), + escape_order(want_escaped, + string_or_null(tp->exp_search_order))); + } +/**INDENT** Warning@168: Extra ) */ + }); + } + vstring_free(want_escaped); + vstring_free(got_escaped); + vstring_free(test_label); +} + + /* + * Test library adaptor. + */ +static const PTEST_CASE ptestcases[] = { + "test_map_search", test_map_search, +}; + +#include diff --git a/postfix/src/global/maps.ref b/postfix/src/global/maps.ref index 84033c7d8..2d3ad20ee 100644 --- a/postfix/src/global/maps.ref +++ b/postfix/src/global/maps.ref @@ -1,3 +1,4 @@ +unknown: dict_open_lookup: fail unknown: dict_open: fail:1maps unknown: dict_register: fail:1maps(0,lock) 1 "": not found diff --git a/postfix/src/global/normalize_mailhost_addr.c b/postfix/src/global/normalize_mailhost_addr.c index ba0f7bd10..a2a52e3b3 100644 --- a/postfix/src/global/normalize_mailhost_addr.c +++ b/postfix/src/global/normalize_mailhost_addr.c @@ -149,111 +149,3 @@ int normalize_mailhost_addr(const char *string, char **mailhost_addr, } return (0); } - - /* - * Test program. - */ -#ifdef TEST -#include -#include -#include - - /* - * Main test program. - */ -int main(int argc, char **argv) -{ - /* Test cases with inputs and expected outputs. */ - typedef struct TEST_CASE { - const char *inet_protocols; - const char *mailhost_addr; - int exp_return; - const char *exp_mailhost_addr; - char *exp_bare_addr; - int exp_addr_family; - } TEST_CASE; - static TEST_CASE test_cases[] = { - /* IPv4 in IPv6. */ - {"ipv4, ipv6", "ipv6:::ffff:1.2.3.4", 0, "1.2.3.4", "1.2.3.4", AF_INET}, - {"ipv6", "ipv6:::ffff:1.2.3.4", 0, "IPv6:::ffff:1.2.3.4", "::ffff:1.2.3.4", AF_INET6}, - /* Pass IPv4 or IPV6. */ - {"ipv4, ipv6", "ipv6:fc00::1", 0, "IPv6:fc00::1", "fc00::1", AF_INET6}, - {"ipv4, ipv6", "1.2.3.4", 0, "1.2.3.4", "1.2.3.4", AF_INET}, - /* Normalize IPv4 or IPV6. */ - {"ipv4, ipv6", "ipv6:fc00::0", 0, "IPv6:fc00::", "fc00::", AF_INET6}, - {"ipv4, ipv6", "01.02.03.04", 0, "1.2.3.4", "1.2.3.4", AF_INET}, - /* Suppress specific outputs. */ - {"ipv4, ipv6", "ipv6:fc00::1", 0, 0, "fc00::1", AF_INET6}, - {"ipv4, ipv6", "ipv6:fc00::1", 0, "IPv6:fc00::1", 0, AF_INET6}, - {"ipv4, ipv6", "ipv6:fc00::1", 0, "IPv6:fc00::1", "fc00::1", -1}, - /* Address type mismatch. */ - {"ipv4, ipv6", "::ffff:1.2.3.4", -1}, - {"ipv4", "ipv6:fc00::1", -1}, - {"ipv6", "1.2.3.4", -1}, - 0, - }; - TEST_CASE *test_case; - - /* Actual results. */ - int act_return; - char *act_mailhost_addr = mystrdup("initial_mailhost_addr"); - char *act_bare_addr = mystrdup("initial_bare_addr"); - int act_addr_family = 0xdeadbeef; - - /* Findings. */ - int tests_failed = 0; - int test_failed; - - for (tests_failed = 0, test_case = test_cases; test_case->inet_protocols; - tests_failed += test_failed, test_case++) { - test_failed = 0; - inet_proto_init(argv[0], test_case->inet_protocols); - act_return = - normalize_mailhost_addr(test_case->mailhost_addr, - test_case->exp_mailhost_addr ? - &act_mailhost_addr : (char **) 0, - test_case->exp_bare_addr ? - &act_bare_addr : (char **) 0, - test_case->exp_addr_family >= 0 ? - &act_addr_family : (int *) 0); - if (act_return != test_case->exp_return) { - msg_warn("test case %d return expected=%d actual=%d", - (int) (test_case - test_cases), - test_case->exp_return, act_return); - test_failed = 1; - continue; - } - if (test_case->exp_return != 0) - continue; - if (test_case->exp_mailhost_addr - && strcmp(test_case->exp_mailhost_addr, act_mailhost_addr)) { - msg_warn("test case %d mailhost_addr expected=%s actual=%s", - (int) (test_case - test_cases), - test_case->exp_mailhost_addr, act_mailhost_addr); - test_failed = 1; - } - if (test_case->exp_bare_addr - && strcmp(test_case->exp_bare_addr, act_bare_addr)) { - msg_warn("test case %d bare_addr expected=%s actual=%s", - (int) (test_case - test_cases), - test_case->exp_bare_addr, act_bare_addr); - test_failed = 1; - } - if (test_case->exp_addr_family >= 0 - && test_case->exp_addr_family != act_addr_family) { - msg_warn("test case %d addr_family expected=0x%x actual=0x%x", - (int) (test_case - test_cases), - test_case->exp_addr_family, act_addr_family); - test_failed = 1; - } - } - if (act_mailhost_addr) - myfree(act_mailhost_addr); - if (act_bare_addr) - myfree(act_bare_addr); - if (tests_failed) - msg_info("tests failed: %d", tests_failed); - exit(tests_failed != 0); -} - -#endif diff --git a/postfix/src/global/normalize_mailhost_addr_test.c b/postfix/src/global/normalize_mailhost_addr_test.c new file mode 100644 index 000000000..5dcf58b61 --- /dev/null +++ b/postfix/src/global/normalize_mailhost_addr_test.c @@ -0,0 +1,179 @@ + /* + * Test program to exercise normalize_mailhost_addr.c. See ptest_main.h for + * a documented example. + */ + + /* + * System library. + */ +#include +#include +#include +#include + + /* + * Utility library. + */ +#include +#include + + /* + * Global library. + */ +#include + + /* + * Test library. + */ +#include + +typedef struct PTEST_CASE { + const char *testname; + void (*action) (PTEST_CTX *, const struct PTEST_CASE *); + const char *inet_protocols; + const char *mailhost_addr; + int want_return; + const char *want_mailhost_addr; + char *want_bare_addr; + int want_addr_family; +} PTEST_CASE; + +static void test_normalize_mailhost_addr(PTEST_CTX *t, const PTEST_CASE *tp) +{ + /* Actual results. */ + int got_return; + char *got_mailhost_addr = mystrdup("initial_mailhost_addr"); + char *got_bare_addr = mystrdup("initial_bare_addr"); + int got_addr_family = 0xdeadbeef; + +#define CLEANUP_AND_RETURN() do { \ + if (got_mailhost_addr) \ + myfree(got_mailhost_addr); \ + got_mailhost_addr = 0; \ + if (got_bare_addr) \ + myfree(got_bare_addr); \ + got_bare_addr = 0; \ + } while (0) + + inet_proto_init(tp->testname, tp->inet_protocols); + got_return = normalize_mailhost_addr(tp->mailhost_addr, + tp->want_mailhost_addr ? + &got_mailhost_addr : (char **) 0, + tp->want_bare_addr ? + &got_bare_addr : (char **) 0, + tp->want_addr_family >= 0 ? + &got_addr_family : (int *) 0); + if (got_return != tp->want_return) { + ptest_error(t, "return value: got %d, want %d", + got_return, tp->want_return); + CLEANUP_AND_RETURN(); + } + if (tp->want_return != 0) + CLEANUP_AND_RETURN(); + if (tp->want_mailhost_addr + && strcmp(tp->want_mailhost_addr, got_mailhost_addr)) { + ptest_error(t, "mailhost_addr value: got '%s', want '%s'", + got_mailhost_addr, tp->want_mailhost_addr); + } + if (tp->want_bare_addr && strcmp(tp->want_bare_addr, got_bare_addr)) { + ptest_error(t, "bare_addr value: got '%s', want '%s'", + got_bare_addr, tp->want_bare_addr); + } + if (tp->want_addr_family > 0 && tp->want_addr_family != got_addr_family) { + ptest_error(t, "addr_family: got 0x%x, want 0x%x", + got_addr_family, tp->want_addr_family); + } + CLEANUP_AND_RETURN(); +} + +static PTEST_CASE ptestcases[] = { + { + "IPv4 in IPv6 #1", test_normalize_mailhost_addr, + /* inet_protocols */ "ipv4, ipv6", + /* mailhost_addr */ "ipv6:::ffff:1.2.3.4", + /* want_return */ 0, + /* want_mailhost_addr */ "1.2.3.4", + /* want_bare_addr */ "1.2.3.4", + /* want_addr_family */ AF_INET + }, { + "IPv4 in IPv6 #2", test_normalize_mailhost_addr, + /* inet_protocols */ "ipv6", + /* mailhost_addr */ "ipv6:::ffff:1.2.3.4", + /* want_return */ 0, + /* want_mailhost_addr */ "IPv6:::ffff:1.2.3.4", + /* want_bare_addr */ "::ffff:1.2.3.4", + /* want_addr_family */ AF_INET6 + }, { + "Pass IPv4 or IPV6 #1", test_normalize_mailhost_addr, + /* inet_protocols */ "ipv4, ipv6", + /* mailhost_addr */ "ipv6:fc00::1", + /* want_return */ 0, + /* want_mailhost_addr */ "IPv6:fc00::1", + /* want_bare_addr */ "fc00::1", + /* want_addr_family */ AF_INET6 + }, { + "Pass IPv4 or IPV6 #2", test_normalize_mailhost_addr, + /* inet_protocols */ "ipv4, ipv6", + /* mailhost_addr */ "1.2.3.4", + /* want_return */ 0, + /* want_mailhost_addr */ "1.2.3.4", + /* want_bare_addr */ "1.2.3.4", + /* want_addr_family */ AF_INET + }, { + "Normalize IPv4 or IPV6 #1", test_normalize_mailhost_addr, + /* inet_protocols */ "ipv4, ipv6", + /* mailhost_addr */ "ipv6:fc00::0", + /* want_return */ 0, + /* want_mailhost_addr */ "IPv6:fc00::", + /* want_bare_addr */ "fc00::", + /* want_addr_family */ AF_INET6 + }, { + "Normalize IPv4 or IPV6 #2", test_normalize_mailhost_addr, + /* inet_protocols */ "ipv4, ipv6", + /* mailhost_addr */ "01.02.03.04", + /* want_return */ 0, + /* want_mailhost_addr */ "1.2.3.4", + /* want_bare_addr */ "1.2.3.4", + /* want_addr_family */ AF_INET + }, { + "Suppress specific outputs #1", test_normalize_mailhost_addr, + /* inet_protocols */ "ipv4, ipv6", + /* mailhost_addr */ "ipv6:fc00::1", + /* want_return */ 0, + /* want_mailhost_addr */ 0, + /* want_bare_addr */ "fc00::1", + /* want_addr_family */ AF_INET6 + }, { + "Suppress specific outputs #2", test_normalize_mailhost_addr, + /* inet_protocols */ "ipv4, ipv6", + /* mailhost_addr */ "ipv6:fc00::1", + /* want_return */ 0, + /* want_mailhost_addr */ "IPv6:fc00::1", + /* want_bare_addr */ 0, + /* want_addr_family */ AF_INET6 + }, { + "Suppress specific outputs #3", test_normalize_mailhost_addr, + /* inet_protocols */ "ipv4, ipv6", + /* mailhost_addr */ "ipv6:fc00::1", + /* want_return */ 0, + /* want_mailhost_addr */ "IPv6:fc00::1", + /* want_bare_addr */ "fc00::1", -1 + }, { + "Address type mismatch #1", test_normalize_mailhost_addr, + /* inet_protocols */ "ipv4, ipv6", + /* mailhost_addr */ "::ffff:1.2.3.4", + /* want_return */ -1 + }, { + "Address type mismatch #2", test_normalize_mailhost_addr, + /* inet_protocols */ "ipv4", + /* mailhost_addr */ "ipv6:fc00::1", + /* want_return */ -1 + }, { + "Address type mismatch #3", test_normalize_mailhost_addr, + /* inet_protocols */ "ipv6", + /* mailhost_addr */ "1.2.3.4", + /* want_return */ -1 + }, +}; + +#include diff --git a/postfix/src/global/smtp_reply_footer.c b/postfix/src/global/smtp_reply_footer.c index 6e5bb75d9..ef1e40d39 100644 --- a/postfix/src/global/smtp_reply_footer.c +++ b/postfix/src/global/smtp_reply_footer.c @@ -207,82 +207,3 @@ int smtp_reply_footer(VSTRING *buffer, ssize_t start, vstring_strcat(buffer, "\r\n"); return (mac_expand_error ? -2 : 0); } - -#ifdef TEST - -#include -#include -#include -#include -#include -#include -#include - -struct test_case { - const char *title; - const char *orig_reply; - const char *template; - const char *filter; - int expected_status; - const char *expected_reply; -}; - -#define NO_FILTER ((char *) 0) -#define NO_TEMPLATE "NO_TEMPLATE" -#define NO_ERROR (0) -#define BAD_SMTP (-1) -#define BAD_MACRO (-2) - -static const struct test_case test_cases[] = { - {"missing reply", "", NO_TEMPLATE, NO_FILTER, BAD_SMTP, 0}, - {"long smtp_code", "1234 foo", NO_TEMPLATE, NO_FILTER, BAD_SMTP, 0}, - {"short smtp_code", "12 foo", NO_TEMPLATE, NO_FILTER, BAD_SMTP, 0}, - {"good+bad smtp_code", "321 foo\r\n1234 foo", NO_TEMPLATE, NO_FILTER, BAD_SMTP, 0}, - {"1-line no dsn", "550 Foo", "\\c footer", NO_FILTER, NO_ERROR, "550 Foo footer"}, - {"1-line no dsn", "550 Foo", "Bar", NO_FILTER, NO_ERROR, "550-Foo\r\n550 Bar"}, - {"2-line no dsn", "550-Foo\r\n550 Bar", "Baz", NO_FILTER, NO_ERROR, "550-Foo\r\n550-Bar\r\n550 Baz"}, - {"1-line with dsn", "550 5.1.1 Foo", "Bar", NO_FILTER, NO_ERROR, "550-5.1.1 Foo\r\n550 5.1.1 Bar"}, - {"2-line with dsn", "550-5.1.1 Foo\r\n450 4.1.1 Bar", "Baz", NO_FILTER, NO_ERROR, "550-5.1.1 Foo\r\n450-4.1.1 Bar\r\n450 4.1.1 Baz"}, - {"bad macro", "220 myhostname", "\\c ${whatever", NO_FILTER, BAD_MACRO, 0}, - {"bad macroCRLF", "220 myhostname\r\n", "\\c ${whatever", NO_FILTER, BAD_MACRO, 0}, - {"good macro", "220 myhostname", "\\c $whatever", NO_FILTER, NO_ERROR, "220 myhostname DUMMY"}, - {"good macroCRLF", "220 myhostname\r\n", "\\c $whatever", NO_FILTER, NO_ERROR, "220 myhostname DUMMY\r\n"}, - 0, -}; - -static const char *lookup(const char *name, int unused_mode, void *context) -{ - return "DUMMY"; -} - -int main(int argc, char **argv) -{ - const struct test_case *tp; - int status; - VSTRING *buf = vstring_alloc(10); - void *context = 0; - - msg_vstream_init(argv[0], VSTREAM_ERR); - - for (tp = test_cases; tp->title != 0; tp++) { - vstring_strcpy(buf, tp->orig_reply); - status = smtp_reply_footer(buf, 0, tp->template, tp->filter, - lookup, context); - if (status != tp->expected_status) { - msg_warn("test \"%s\": status %d, expected %d", - tp->title, status, tp->expected_status); - } else if (status < 0 && strcmp(STR(buf), tp->orig_reply) != 0) { - msg_warn("test \"%s\": result \"%s\", expected \"%s\"", - tp->title, STR(buf), tp->orig_reply); - } else if (status == 0 && strcmp(STR(buf), tp->expected_reply) != 0) { - msg_warn("test \"%s\": result \"%s\", expected \"%s\"", - tp->title, STR(buf), tp->expected_reply); - } else { - msg_info("test \"%s\": pass", tp->title); - } - } - vstring_free(buf); - exit(0); -} - -#endif diff --git a/postfix/src/global/smtp_reply_footer.ref b/postfix/src/global/smtp_reply_footer.ref deleted file mode 100644 index d7eb5a7b5..000000000 --- a/postfix/src/global/smtp_reply_footer.ref +++ /dev/null @@ -1,15 +0,0 @@ -./smtp_reply_footer: test "missing reply": pass -./smtp_reply_footer: test "long smtp_code": pass -./smtp_reply_footer: test "short smtp_code": pass -./smtp_reply_footer: test "good+bad smtp_code": pass -./smtp_reply_footer: test "1-line no dsn": pass -./smtp_reply_footer: test "1-line no dsn": pass -./smtp_reply_footer: test "2-line no dsn": pass -./smtp_reply_footer: test "1-line with dsn": pass -./smtp_reply_footer: test "2-line with dsn": pass -./smtp_reply_footer: warning: truncated macro reference: " ${whatever" -./smtp_reply_footer: test "bad macro": pass -./smtp_reply_footer: warning: truncated macro reference: " ${whatever" -./smtp_reply_footer: test "bad macroCRLF": pass -./smtp_reply_footer: test "good macro": pass -./smtp_reply_footer: test "good macroCRLF": pass diff --git a/postfix/src/global/smtp_reply_footer_test.c b/postfix/src/global/smtp_reply_footer_test.c new file mode 100644 index 000000000..13aeaab86 --- /dev/null +++ b/postfix/src/global/smtp_reply_footer_test.c @@ -0,0 +1,90 @@ + /* + * Test program to exercise smtp_reply_footer.c. See ptest_main.h for a + * documented example. + */ + + /* + * System library. + */ +#include +#include + + /* + * Global library. + */ +#include +#include + + /* + * Test library. + */ +#include + + /* + * SLMs. + */ +#define STR vstring_str + +typedef struct PTEST_CASE { + const char *testname; + void (*action) (PTEST_CTX *, const struct PTEST_CASE *); + const char *orig_reply; + const char *template; + const char *filter; + int want_status; + const char *new_reply; + const char *ignore_warning; +} PTEST_CASE; + +#define NO_FILTER ((char *) 0) +#define NO_TEMPLATE "NO_TEMPLATE" +#define NO_ERROR (0) +#define BAD_SMTP (-1) +#define BAD_MACRO (-2) + +static const char *lookup(const char *testname, int unused_mode, void *context) +{ + return "DUMMY"; +} + +static void test_footer(PTEST_CTX *t, const PTEST_CASE *tp) +{ + VSTRING *buf = vstring_alloc(10); + int got_status; + void *context = 0; + + if (tp->ignore_warning) + expect_ptest_log_event(t, tp->ignore_warning); + vstring_strcpy(buf, tp->orig_reply); + got_status = smtp_reply_footer(buf, 0, tp->template, tp->filter, + lookup, context); + if (got_status != tp->want_status) { + ptest_error(t, "smtp_reply_footer status: got %d, want %d", + got_status, tp->want_status); + } else if (got_status < 0 && strcmp(STR(buf), tp->orig_reply) != 0) { + ptest_error(t, "smtp_reply_footer result: got \"%s\", want \"%s\"", + STR(buf), tp->orig_reply); + } else if (got_status == 0 && strcmp(STR(buf), tp->new_reply) != 0) { + ptest_error(t, "smtp_reply_footer result: got \"%s\", want \"%s\"", + STR(buf), tp->new_reply); + } + vstring_free(buf); +} + +const PTEST_CASE ptestcases[] = { + {"missing reply", test_footer, "", NO_TEMPLATE, NO_FILTER, BAD_SMTP, 0}, + {"long smtp_code", test_footer, "1234 foo", NO_TEMPLATE, NO_FILTER, BAD_SMTP, 0}, + {"short smtp_code", test_footer, "12 foo", NO_TEMPLATE, NO_FILTER, BAD_SMTP, 0}, + {"good+bad smtp_code", test_footer, "321 foo\r\n1234 foo", NO_TEMPLATE, NO_FILTER, BAD_SMTP, 0}, + {"1-line no dsn", test_footer, "550 Foo", "\\c footer", NO_FILTER, NO_ERROR, "550 Foo footer"}, + {"1-line no dsn", test_footer, "550 Foo", "Bar", NO_FILTER, NO_ERROR, "550-Foo\r\n550 Bar"}, + {"2-line no dsn", test_footer, "550-Foo\r\n550 Bar", "Baz", NO_FILTER, NO_ERROR, "550-Foo\r\n550-Bar\r\n550 Baz"}, + {"1-line with dsn", test_footer, "550 5.1.1 Foo", "Bar", NO_FILTER, NO_ERROR, "550-5.1.1 Foo\r\n550 5.1.1 Bar"}, + {"2-line with dsn", test_footer, "550-5.1.1 Foo\r\n450 4.1.1 Bar", "Baz", NO_FILTER, NO_ERROR, "550-5.1.1 Foo\r\n450-4.1.1 Bar\r\n450 4.1.1 Baz"}, + {"bad macro", test_footer, "220 myhostname", "\\c ${whatever", NO_FILTER, BAD_MACRO, 0, "truncated macro reference"}, + {"bad macroCRLF", test_footer, "220 myhostname\r\n", "\\c ${whatever", NO_FILTER, BAD_MACRO, 0, "truncated macro reference"}, + {"good macro", test_footer, "220 myhostname", "\\c $whatever", NO_FILTER, NO_ERROR, "220 myhostname DUMMY"}, + {"good macroCRLF", test_footer, "220 myhostname\r\n", "\\c $whatever", NO_FILTER, NO_ERROR, "220 myhostname DUMMY\r\n"}, +}; + +#include diff --git a/postfix/src/global/test_main.c b/postfix/src/global/test_server_main.c similarity index 93% rename from postfix/src/global/test_main.c rename to postfix/src/global/test_server_main.c index a783ce354..936d897d9 100644 --- a/postfix/src/global/test_main.c +++ b/postfix/src/global/test_server_main.c @@ -1,12 +1,12 @@ /*++ /* NAME -/* test_main 3 +/* test_server_main 3 /* SUMMARY /* test main program /* SYNOPSIS -/* #include +/* #include /* -/* NORETURN test_main(argc, argv, test_driver, key, value, ...) +/* NORETURN test_server_main(argc, argv, test_driver, key, value, ...) /* int argc; /* char **argv; /* void (*test_driver)(int argc, char **argv); @@ -15,10 +15,10 @@ /* This module implements a test main program for stand-alone /* module tests. /* -/* test_main() should be called from a main program. It does +/* test_server_main() should be called from a main program. It does /* generic command-line options processing, and initializes /* configurable parameters. After calling the test_driver() -/* function, the test_main() function terminates. +/* function, the test_server_main() function terminates. /* /* Arguments: /* .IP "void (*test_driver)(int argc, char **argv)" @@ -27,7 +27,7 @@ /* The argc and argv specify the process name and non-option /* command-line arguments. /* .PP -/* Optional test_main() arguments are specified as a null-terminated +/* Optional test_server_main() arguments are specified as a null-terminated /* list with macros that have zero or more arguments: /* .IP "CA_TEST_MAIN_INT_TABLE(CONFIG_INT_TABLE *)" /* A table with configurable parameters, to be loaded from the @@ -116,11 +116,11 @@ /* * Test library. */ -#include +#include /* test_driver_main - the real main program */ -NORETURN test_main(int argc, char **argv, TEST_DRIVER_FN test_driver,...) +NORETURN test_server_main(int argc, char **argv, TEST_DRIVER_FN test_driver,...) { const char *myname = "test_driver_main"; va_list ap; diff --git a/postfix/src/global/test_main.h b/postfix/src/global/test_server_main.h similarity index 94% rename from postfix/src/global/test_main.h rename to postfix/src/global/test_server_main.h index aea605a1f..c2c247191 100644 --- a/postfix/src/global/test_main.h +++ b/postfix/src/global/test_server_main.h @@ -1,10 +1,10 @@ /*++ /* NAME -/* test_main 3h +/* test_server_main 3h /* SUMMARY /* test main program /* SYNOPSIS -/* #include +/* #include /* DESCRIPTION /* .nf @@ -45,7 +45,7 @@ CHECK_CPTR_HELPER_DCL(TEST_MAIN, CONFIG_NBOOL_TABLE); CHECK_CPTR_HELPER_DCL(TEST_MAIN, CONFIG_LONG_TABLE); typedef void (*TEST_DRIVER_FN) (int, char **); -extern NORETURN test_main(int, char **, TEST_DRIVER_FN,...); +extern NORETURN test_server_main(int, char **, TEST_DRIVER_FN,...); /* LICENSE /* .ad diff --git a/postfix/src/local/Makefile.in b/postfix/src/local/Makefile.in index 648ad5133..9659b6195 100644 --- a/postfix/src/local/Makefile.in +++ b/postfix/src/local/Makefile.in @@ -102,9 +102,12 @@ alias.o: ../../include/vstream.h alias.o: ../../include/vstring.h alias.o: alias.c alias.o: local.h +biff_notify.o: ../../include/inet_proto.h biff_notify.o: ../../include/iostuff.h biff_notify.o: ../../include/msg.h +biff_notify.o: ../../include/myaddrinfo.h biff_notify.o: ../../include/sys_defs.h +biff_notify.o: ../../include/wrap_netdb.h biff_notify.o: biff_notify.c biff_notify.o: biff_notify.h bounce_workaround.o: ../../include/argv.h diff --git a/postfix/src/local/biff_notify.c b/postfix/src/local/biff_notify.c index a6a49258e..6aa75a220 100644 --- a/postfix/src/local/biff_notify.c +++ b/postfix/src/local/biff_notify.c @@ -30,6 +30,11 @@ /* IBM T.J. Watson Research /* P.O. Box 704 /* Yorktown Heights, NY 10598, USA +/* +/* Wietse Venema +/* Google, Inc. +/* 111 8th Avenue +/* New York, NY 10011, USA /*--*/ /* System library. */ @@ -37,13 +42,14 @@ #include "sys_defs.h" #include #include -#include #include /* Utility library. */ #include #include +#include +#include /* Application-specific. */ @@ -53,37 +59,65 @@ void biff_notify(const char *text, ssize_t len) { - static struct sockaddr_in sin; + const char myname[] = "biff_notify"; + const char *hostname = "localhost"; + const char *servname = "biff"; + int sock_type = SOCK_DGRAM; + const INET_PROTO_INFO *proto_info; + static struct sockaddr_storage sa; + static SOCKADDR_SIZE sa_len; + static int sa_family; static int sock = -1; - struct hostent *hp; - struct servent *sp; + struct addrinfo *res0, *res; + int aierr; + int found; /* * Initialize a socket address structure, or re-use an existing one. */ - if (sin.sin_family == 0) { - if ((sp = getservbyname("biff", "udp")) == 0) { - msg_warn("service not found: biff/udp"); + if (sa_len == 0) { + if ((aierr = hostname_to_sockaddr(hostname, servname, sock_type, + &res0)) != 0) { + msg_warn("lookup failed for host '%s' or service '%s': %s", + hostname, servname, MAI_STRERROR(aierr)); return; } - if ((hp = gethostbyname("localhost")) == 0) { - msg_warn("host not found: localhost"); - return; + proto_info = inet_proto_info(); + for (found = 0, res = res0; !found && res != 0; res = res->ai_next) { + if (strchr((char *) proto_info->sa_family_list, res->ai_family) == 0) { + msg_info("skipping address family %d for host '%s' service '%s'", + res->ai_family, hostname, servname); + continue; + } + if (res->ai_addrlen > sizeof(sa)) { + msg_warn("skipping address size %d for host '%s' service '%s'", + res->ai_addrlen, hostname, servname); + continue; + } + found++; + memcpy(&sa, res->ai_addr, res->ai_addrlen); + sa_len = res->ai_addrlen; + sa_family = res->ai_family; + if (msg_verbose) { + MAI_HOSTADDR_STR hostaddr_str; + MAI_SERVPORT_STR servport_str; + + SOCKADDR_TO_HOSTADDR((struct sockaddr *) &sa, sa_len, + &hostaddr_str, &servport_str, 0); + msg_info("%s: sending to: {%s, %s}", + myname, hostaddr_str.buf, servport_str.buf); + } } - if ((int) hp->h_length > (int) sizeof(sin.sin_addr)) { - msg_warn("bad address size %d for localhost", hp->h_length); + freeaddrinfo(res0); + if (!found) return; - } - sin.sin_family = hp->h_addrtype; - sin.sin_port = sp->s_port; - memcpy((void *) &sin.sin_addr, hp->h_addr_list[0], hp->h_length); } /* * Open a socket, or re-use an existing one. */ if (sock < 0) { - if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { + if ((sock = socket(sa_family, sock_type, 0)) < 0) { msg_warn("socket: %m"); return; } @@ -93,6 +127,6 @@ void biff_notify(const char *text, ssize_t len) /* * Biff! */ - if (sendto(sock, text, len, 0, (struct sockaddr *) &sin, sizeof(sin)) != len) + if (sendto(sock, text, len, 0, (struct sockaddr *) &sa, sa_len) != len) msg_warn("biff_notify: %m"); } diff --git a/postfix/src/master/Makefile.in b/postfix/src/master/Makefile.in index db67a68eb..51987a79b 100644 --- a/postfix/src/master/Makefile.in +++ b/postfix/src/master/Makefile.in @@ -243,6 +243,7 @@ master_ent.o: ../../include/vbuf.h master_ent.o: ../../include/vstream.h master_ent.o: ../../include/vstring.h master_ent.o: ../../include/wildcard_inet_addr.h +master_ent.o: ../../include/wrap_netdb.h master_ent.o: master.h master_ent.o: master_ent.c master_ent.o: master_proto.h @@ -268,6 +269,7 @@ master_listen.o: ../../include/stringops.h master_listen.o: ../../include/sys_defs.h master_listen.o: ../../include/vbuf.h master_listen.o: ../../include/vstring.h +master_listen.o: ../../include/wrap_netdb.h master_listen.o: master.h master_listen.o: master_listen.c master_monitor.o: ../../include/iostuff.h diff --git a/postfix/src/postconf/Makefile.in b/postfix/src/postconf/Makefile.in index 4f6536fd3..633708887 100644 --- a/postfix/src/postconf/Makefile.in +++ b/postfix/src/postconf/Makefile.in @@ -1237,6 +1237,7 @@ postconf_other.o: ../../include/tls.h postconf_other.o: ../../include/vbuf.h postconf_other.o: ../../include/vstream.h postconf_other.o: ../../include/vstring.h +postconf_other.o: ../../include/wrap_netdb.h postconf_other.o: ../../include/xsasl.h postconf_other.o: postconf.h postconf_other.o: postconf_other.c diff --git a/postfix/src/postlogd/.indent.pro b/postfix/src/postlogd/.indent.pro new file mode 120000 index 000000000..5c837eca6 --- /dev/null +++ b/postfix/src/postlogd/.indent.pro @@ -0,0 +1 @@ +../../.indent.pro \ No newline at end of file diff --git a/postfix/src/postscreen/Makefile.in b/postfix/src/postscreen/Makefile.in index 8ed869229..034c01be7 100644 --- a/postfix/src/postscreen/Makefile.in +++ b/postfix/src/postscreen/Makefile.in @@ -100,6 +100,7 @@ postscreen.o: ../../include/sys_defs.h postscreen.o: ../../include/vbuf.h postscreen.o: ../../include/vstream.h postscreen.o: ../../include/vstring.h +postscreen.o: ../../include/wrap_netdb.h postscreen.o: postscreen.c postscreen.o: postscreen.h postscreen_dict.o: ../../include/addr_match_list.h @@ -120,6 +121,7 @@ postscreen_dict.o: ../../include/sys_defs.h postscreen_dict.o: ../../include/vbuf.h postscreen_dict.o: ../../include/vstream.h postscreen_dict.o: ../../include/vstring.h +postscreen_dict.o: ../../include/wrap_netdb.h postscreen_dict.o: postscreen.h postscreen_dict.o: postscreen_dict.c postscreen_dnsbl.o: ../../include/addr_match_list.h @@ -151,6 +153,7 @@ postscreen_dnsbl.o: ../../include/valid_hostname.h postscreen_dnsbl.o: ../../include/vbuf.h postscreen_dnsbl.o: ../../include/vstream.h postscreen_dnsbl.o: ../../include/vstring.h +postscreen_dnsbl.o: ../../include/wrap_netdb.h postscreen_dnsbl.o: postscreen.h postscreen_dnsbl.o: postscreen_dnsbl.c postscreen_early.o: ../../include/addr_match_list.h @@ -174,6 +177,7 @@ postscreen_early.o: ../../include/sys_defs.h postscreen_early.o: ../../include/vbuf.h postscreen_early.o: ../../include/vstream.h postscreen_early.o: ../../include/vstring.h +postscreen_early.o: ../../include/wrap_netdb.h postscreen_early.o: postscreen.h postscreen_early.o: postscreen_early.c postscreen_endpt.o: ../../include/addr_match_list.h @@ -197,6 +201,7 @@ postscreen_endpt.o: ../../include/sys_defs.h postscreen_endpt.o: ../../include/vbuf.h postscreen_endpt.o: ../../include/vstream.h postscreen_endpt.o: ../../include/vstring.h +postscreen_endpt.o: ../../include/wrap_netdb.h postscreen_endpt.o: postscreen.h postscreen_endpt.o: postscreen_endpt.c postscreen_endpt.o: postscreen_haproxy.h @@ -225,6 +230,7 @@ postscreen_expand.o: ../../include/sys_defs.h postscreen_expand.o: ../../include/vbuf.h postscreen_expand.o: ../../include/vstream.h postscreen_expand.o: ../../include/vstring.h +postscreen_expand.o: ../../include/wrap_netdb.h postscreen_expand.o: postscreen.h postscreen_expand.o: postscreen_expand.c postscreen_haproxy.o: ../../include/addr_match_list.h @@ -249,6 +255,7 @@ postscreen_haproxy.o: ../../include/sys_defs.h postscreen_haproxy.o: ../../include/vbuf.h postscreen_haproxy.o: ../../include/vstream.h postscreen_haproxy.o: ../../include/vstring.h +postscreen_haproxy.o: ../../include/wrap_netdb.h postscreen_haproxy.o: postscreen.h postscreen_haproxy.o: postscreen_haproxy.c postscreen_haproxy.o: postscreen_haproxy.h @@ -273,6 +280,7 @@ postscreen_misc.o: ../../include/sys_defs.h postscreen_misc.o: ../../include/vbuf.h postscreen_misc.o: ../../include/vstream.h postscreen_misc.o: ../../include/vstring.h +postscreen_misc.o: ../../include/wrap_netdb.h postscreen_misc.o: postscreen.h postscreen_misc.o: postscreen_misc.c postscreen_send.o: ../../include/addr_match_list.h @@ -303,6 +311,7 @@ postscreen_send.o: ../../include/sys_defs.h postscreen_send.o: ../../include/vbuf.h postscreen_send.o: ../../include/vstream.h postscreen_send.o: ../../include/vstring.h +postscreen_send.o: ../../include/wrap_netdb.h postscreen_send.o: postscreen.h postscreen_send.o: postscreen_send.c postscreen_smtpd.o: ../../include/addr_match_list.h @@ -339,6 +348,7 @@ postscreen_smtpd.o: ../../include/tls.h postscreen_smtpd.o: ../../include/vbuf.h postscreen_smtpd.o: ../../include/vstream.h postscreen_smtpd.o: ../../include/vstring.h +postscreen_smtpd.o: ../../include/wrap_netdb.h postscreen_smtpd.o: postscreen.h postscreen_smtpd.o: postscreen_smtpd.c postscreen_starttls.o: ../../include/addr_match_list.h @@ -373,6 +383,7 @@ postscreen_starttls.o: ../../include/tls_proxy.h postscreen_starttls.o: ../../include/vbuf.h postscreen_starttls.o: ../../include/vstream.h postscreen_starttls.o: ../../include/vstring.h +postscreen_starttls.o: ../../include/wrap_netdb.h postscreen_starttls.o: postscreen.h postscreen_starttls.o: postscreen_starttls.c postscreen_state.o: ../../include/addr_match_list.h @@ -401,6 +412,7 @@ postscreen_state.o: ../../include/sys_defs.h postscreen_state.o: ../../include/vbuf.h postscreen_state.o: ../../include/vstream.h postscreen_state.o: ../../include/vstring.h +postscreen_state.o: ../../include/wrap_netdb.h postscreen_state.o: postscreen.h postscreen_state.o: postscreen_state.c postscreen_tests.o: ../../include/addr_match_list.h @@ -424,5 +436,6 @@ postscreen_tests.o: ../../include/sys_defs.h postscreen_tests.o: ../../include/vbuf.h postscreen_tests.o: ../../include/vstream.h postscreen_tests.o: ../../include/vstring.h +postscreen_tests.o: ../../include/wrap_netdb.h postscreen_tests.o: postscreen.h postscreen_tests.o: postscreen_tests.c diff --git a/postfix/src/posttls-finger/Makefile.in b/postfix/src/posttls-finger/Makefile.in index d246303f2..6b8d53de5 100644 --- a/postfix/src/posttls-finger/Makefile.in +++ b/postfix/src/posttls-finger/Makefile.in @@ -100,6 +100,7 @@ posttls-finger.o: ../../include/vbuf.h posttls-finger.o: ../../include/vstream.h posttls-finger.o: ../../include/vstring.h posttls-finger.o: ../../include/vstring_vstream.h +posttls-finger.o: ../../include/wrap_netdb.h posttls-finger.o: posttls-finger.c posttls-finger.o: tlsmgrmem.h tlsmgrmem.o: ../../include/argv.h diff --git a/postfix/src/ptest/.indent.pro b/postfix/src/ptest/.indent.pro new file mode 120000 index 000000000..5c837eca6 --- /dev/null +++ b/postfix/src/ptest/.indent.pro @@ -0,0 +1 @@ +../../.indent.pro \ No newline at end of file diff --git a/postfix/src/ptest/Makefile.in b/postfix/src/ptest/Makefile.in new file mode 100644 index 000000000..e9b85bbc3 --- /dev/null +++ b/postfix/src/ptest/Makefile.in @@ -0,0 +1,160 @@ +SHELL = /bin/sh +SRCS = ptest_ctx.c ptest_error.c ptest_log.c pmock_expect.c ptest_run.c +LIB_OBJ = ptest_ctx.o ptest_error.o ptest_log.o pmock_expect.o ptest_run.o +TEST_OBJ = pmock_expect_test.o ptest_log_test.o +HDRS = ptest.h pmock_expect.h ptest_main.h +TESTSRC = +DEFS = -I. -I$(INC_DIR) -D$(SYSTYPE) +CFLAGS = $(DEBUG) $(OPT) $(DEFS) +INCL = +LIB = libptest.a +TESTPROG= pmock_expect_test ptest_log_test + +LIBS = ../../lib/lib$(LIB_PREFIX)global$(LIB_SUFFIX) \ + ../../lib/lib$(LIB_PREFIX)util$(LIB_SUFFIX) +LIB_DIR = ../../lib +INC_DIR = ../../include +MAKES = + +.c.o:; $(CC) $(SHLIB_CFLAGS) $(CFLAGS) -c $*.c + +all: $(LIB_OBJ) $(MOCK_OBJ) + +$(LIB_OBJ) $(TEST_OBJ): ../../conf/makedefs.out + +Makefile: Makefile.in + cat ../../conf/makedefs.out $? >$@ + +test: $(TESTPROG) + +$(LIB): $(LIB_OBJ) + $(_AR) $(ARFL) $(LIB) $? + $(_RANLIB) $(LIB) + +$(LIB_DIR)/$(LIB): $(LIB) + cp $(LIB) $(LIB_DIR) + $(_RANLIB) $(LIB_DIR)/$(LIB) + +update: $(LIB_DIR)/$(LIB) $(HDRS) $(MOCK_OBJ) + -for i in $(HDRS); \ + do \ + cmp -s $$i $(INC_DIR)/$$i 2>/dev/null || cp $$i $(INC_DIR); \ + done + (cd $(INC_DIR); chmod 644 $(HDRS)) + -for i in $(MOCK_OBJ); \ + do \ + cmp -s $$i $(LIB_DIR)/$$i 2>/dev/null || cp $$i $(LIB_DIR); \ + done + +clean: + rm -f *.o $(LIB) *core $(TESTPROG) junk $(MAKES) *.tmp + +tidy: clean + +tests: test_pmock_expect test_ptest_log + +pmock_expect_test: pmock_expect_test.o $(LIB) $(LIBS) + $(CC) $(CFLAGS) -o $@ $@.o $(LIB) $(LIBS) $(SYSLIBS) + +test_pmock_expect: update pmock_expect_test + $(SHLIB_ENV) ${VALGRIND} ./pmock_expect_test + +ptest_log_test: ptest_log_test.o ptest_log.o $(LIB) $(LIBS) + $(CC) $(CFLAGS) -o $@ $@.o ptest_log.o $(LIB) $(LIBS) $(SYSLIBS) + +test_ptest_log: update ptest_log_test + $(SHLIB_ENV) ${VALGRIND} ./ptest_log_test + +depend: $(MAKES) + (sed '1,/^# do not edit/!d' Makefile.in; \ + set -e; for i in [a-z][a-z0-9]*.c; do \ + $(CC) -E $(DEFS) $(INCL) $$i | grep -v '[<>]' | sed -n -e '/^# *1 *"\([^"]*\)".*/{' \ + -e 's//'`echo $$i|sed 's/c$$/o/'`': \1/' \ + -e 's/o: \.\//o: /' -e p -e '}' ; \ + done | LANG=C sort -u) | grep -v '[.][o][:][ ][/]' >$$$$ && mv $$$$ Makefile.in + @$(EXPORT) make -f Makefile.in Makefile 1>&2 + +# do not edit below this line - it is generated by 'make depend' +pmock_expect.o: ../../include/argv.h +pmock_expect.o: ../../include/check_arg.h +pmock_expect.o: ../../include/htable.h +pmock_expect.o: ../../include/msg.h +pmock_expect.o: ../../include/mymalloc.h +pmock_expect.o: ../../include/sys_defs.h +pmock_expect.o: ../../include/vbuf.h +pmock_expect.o: ../../include/vstream.h +pmock_expect.o: ../../include/vstring.h +pmock_expect.o: pmock_expect.c +pmock_expect.o: pmock_expect.h +pmock_expect.o: ptest.h +pmock_expect_test.o: ../../include/argv.h +pmock_expect_test.o: ../../include/check_arg.h +pmock_expect_test.o: ../../include/msg.h +pmock_expect_test.o: ../../include/msg_output.h +pmock_expect_test.o: ../../include/msg_vstream.h +pmock_expect_test.o: ../../include/mymalloc.h +pmock_expect_test.o: ../../include/stringops.h +pmock_expect_test.o: ../../include/sys_defs.h +pmock_expect_test.o: ../../include/vbuf.h +pmock_expect_test.o: ../../include/vstream.h +pmock_expect_test.o: ../../include/vstring.h +pmock_expect_test.o: pmock_expect.h +pmock_expect_test.o: pmock_expect_test.c +pmock_expect_test.o: ptest.h +pmock_expect_test.o: ptest_main.h +ptest_ctx.o: ../../include/argv.h +ptest_ctx.o: ../../include/check_arg.h +ptest_ctx.o: ../../include/msg.h +ptest_ctx.o: ../../include/mymalloc.h +ptest_ctx.o: ../../include/stringops.h +ptest_ctx.o: ../../include/sys_defs.h +ptest_ctx.o: ../../include/vbuf.h +ptest_ctx.o: ../../include/vstream.h +ptest_ctx.o: ../../include/vstring.h +ptest_ctx.o: ptest.h +ptest_ctx.o: ptest_ctx.c +ptest_error.o: ../../include/argv.h +ptest_error.o: ../../include/check_arg.h +ptest_error.o: ../../include/msg.h +ptest_error.o: ../../include/mymalloc.h +ptest_error.o: ../../include/sys_defs.h +ptest_error.o: ../../include/vbuf.h +ptest_error.o: ../../include/vstream.h +ptest_error.o: ../../include/vstring.h +ptest_error.o: ptest.h +ptest_error.o: ptest_error.c +ptest_log.o: ../../include/argv.h +ptest_log.o: ../../include/check_arg.h +ptest_log.o: ../../include/msg.h +ptest_log.o: ../../include/msg_output.h +ptest_log.o: ../../include/sys_defs.h +ptest_log.o: ../../include/vbuf.h +ptest_log.o: ../../include/vstream.h +ptest_log.o: ../../include/vstring.h +ptest_log.o: ptest.h +ptest_log.o: ptest_log.c +ptest_log_test.o: ../../include/argv.h +ptest_log_test.o: ../../include/check_arg.h +ptest_log_test.o: ../../include/msg.h +ptest_log_test.o: ../../include/msg_output.h +ptest_log_test.o: ../../include/msg_vstream.h +ptest_log_test.o: ../../include/stringops.h +ptest_log_test.o: ../../include/sys_defs.h +ptest_log_test.o: ../../include/vbuf.h +ptest_log_test.o: ../../include/vstream.h +ptest_log_test.o: ../../include/vstring.h +ptest_log_test.o: pmock_expect.h +ptest_log_test.o: ptest.h +ptest_log_test.o: ptest_log_test.c +ptest_log_test.o: ptest_main.h +ptest_run.o: ../../include/argv.h +ptest_run.o: ../../include/check_arg.h +ptest_run.o: ../../include/msg.h +ptest_run.o: ../../include/msg_vstream.h +ptest_run.o: ../../include/sys_defs.h +ptest_run.o: ../../include/vbuf.h +ptest_run.o: ../../include/vstream.h +ptest_run.o: ../../include/vstring.h +ptest_run.o: pmock_expect.h +ptest_run.o: ptest.h +ptest_run.o: ptest_run.c diff --git a/postfix/src/ptest/pmock_expect.c b/postfix/src/ptest/pmock_expect.c new file mode 100644 index 000000000..e6dd22514 --- /dev/null +++ b/postfix/src/ptest/pmock_expect.c @@ -0,0 +1,286 @@ +/*++ +/* NAME +/* pmock_expect 3h +/* SUMMARY +/* mock support for hermetic tests +/* SYNOPSIS +/* #include +/* +/* MOCK_EXPECT *pmock_expect_create( +/* const MOCK_APPL_SIG *sig, +/* const char *file, +/* int line, +/* int calls_expected, +/* ssize_t size) +/* +/* int pmock_expect_apply( +/* const MOCK_APPL_SIG *sig, +/* const MOCK_EXPECT *inputs, +/* void *targets) +/* +/* void pmock_expect_free(MOCK_EXPECT *me) +/* +/* void pmock_expect_wrapup(PTEST_CTX *t) +/* DESCRIPTION +/* This module provides support to implement mock functions +/* that emulate real functions with the same name, but that +/* respond to calls with prepared outputs. This requires that +/* the real function has the "MOCKABLE" annotation. +/* +/* For a simple example, see the pmock_expect_test.c file. +/* +/* pmock_expect_create() creates an expectation for calls into +/* a mock function (whose details are given with the MOCK_APPL_SIG +/* argument). pmock_expect_create() initializes the generic +/* expectation fields (file name, line number, and number of +/* calls), and appends the resulting object to a dedicated +/* list for the user-defined mock function. The pmock_expect_create() +/* caller must save deep copies of the expected inputs and +/* prepared outputs. +/* +/* pmock_expect_apply() takes an inputs argument with mock call +/* inputs, and looks up a matching expectation. If a match is +/* found, and if its call count isn't already saturated, +/* pmock_expect_apply() uses the targets argument to update the +/* mock call outputs. +/* +/* pmock_expect_wrapup() reports unused expectations, and +/* destroys all expectations. Subsequent calls of this function +/* do nothing. +/* DIAGNOSTICS +/* pmock_expect_apply() returns 'true' when a match is found +/* and the match is not saturated. Otherwise, it returns 'false' +/* after generating a "too many calls" or "unexpected call" +/* error. If that error is expected (with expect_ptest_error()), +/* then it is ignored (not reported) and it will not count as +/* a test failure. +/* +/* pmock_expect_wrapup() logs a warning when some expectation +/* has not been used. +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* Google, Inc. +/* 111 8th Avenue +/* New York, NY 10011, USA +/*--*/ + + /* + * System library. + */ +#include + + /* + * Utility library. + */ +#include +#include + + /* + * Testing library. + */ +#include +#include + + /* + * Private structure with all expectations for a single mock application. + */ +typedef struct MOCK_APPL { + const MOCK_APPL_SIG *sig; /* application-specific */ + MOCK_EXPECT *head; /* first expectation */ + MOCK_EXPECT *tail; /* last expectation */ +} MOCK_APPL; + + /* + * Collection of MOCK_APPL instances indexed by application name. + */ +static HTABLE *mock_appl_list; + +/* mock_appl_create - create empty list for same-type expectations */ + +static MOCK_APPL *mock_appl_create(const MOCK_APPL_SIG *sig) +{ + MOCK_APPL *ma; + + /* + * Initialize self. + */ + ma = (MOCK_APPL *) mymalloc(sizeof(*ma)); + ma->sig = sig; + ma->head = ma->tail = 0; + return (ma); +} + +/* mock_appl_list_free - destroy application node */ + +static void mock_appl_list_free(MOCK_APPL *ma) +{ + myfree(ma); +} + +/* pmock_expect_create - create one mock expectation */ + +MOCK_EXPECT *pmock_expect_create(const MOCK_APPL_SIG *sig, const char *file, + int line, int calls_expected, + ssize_t size) +{ + MOCK_APPL *ma; + MOCK_EXPECT *me; + + /* + * Look up or instantiate the expectation for this mock application. + */ + if (mock_appl_list == 0) + mock_appl_list = htable_create(13); + if ((ma = (MOCK_APPL *) htable_find(mock_appl_list, sig->name)) == 0) { + ma = mock_appl_create(sig); + (void) htable_enter(mock_appl_list, sig->name, (void *) ma); + } + + /* + * Initialize the generic expectation fields. + */ + me = (MOCK_EXPECT *) mymalloc(size); + me->file = mystrdup(file); + me->line = line; + me->calls_expected = calls_expected; + me->calls_made = 0; + me->next = 0; + + /* + * Append the new expectation to this mock application list. + */ + if (ma->head == 0) + ma->head = me; + else + ma->tail->next = me; + ma->tail = me; + + /* + * Let the caller fill in their application-specific fields. + */ + return (me); +} + +/* pmock_expect_free - destroy one expectation node */ + +void pmock_expect_free(MOCK_EXPECT *me) +{ + myfree(me->file); + myfree(me); +} + +/* pmock_expect_apply - match inputs and apply outputs */ + +int pmock_expect_apply(const MOCK_APPL_SIG *sig, + const MOCK_EXPECT *inputs, + void *targets) +{ + MOCK_APPL *ma; + MOCK_EXPECT *me; + MOCK_EXPECT *saturated = 0; /* saturated expectation */ + VSTRING *buf; + PTEST_CTX *t; + + /* + * Look up the mock application list. + */ + if (mock_appl_list != 0 && (ma = (MOCK_APPL *) + htable_find(mock_appl_list, sig->name)) != 0) { + + /* + * Look for an expectation match that is not saturated. Remember the + * last saturated match. + */ + for (me = ma->head; me != 0; me = me->next) { + const MOCK_APPL_SIG *sig = ma->sig; + + if (sig->match_expect == 0 || sig->match_expect(me, inputs)) { + if (me->calls_expected == 0 + || me->calls_made < me->calls_expected) { + if (sig->assign_expect) + sig->assign_expect(me, targets); + me->calls_made += 1; + return (1); + } else { + saturated = me; + } + } + } + } + + /* + * Report a saturated or unmatched expectation. + */ + buf = vstring_alloc(100); + t = ptest_ctx_current(); + if (saturated != 0) { + ptest_error(t, "%s:%d too many calls: %s(%s)", + saturated->file, saturated->line, sig->name, + sig->print_expect(saturated, buf)); + } else { + ptest_error(t, "unexpected call: %s(%s)", sig->name, + sig->print_expect(inputs, buf)); + } + vstring_free(buf); + return (0); +} + +/* pmock_expect_wrapup - report unused expectations and clean up */ + +void pmock_expect_wrapup(PTEST_CTX *t) +{ + HTABLE_INFO **info, **ht; + MOCK_APPL *ma; + MOCK_EXPECT *me, *next_me; + VSTRING *buf = 0; + const char *plural[] = {"", "s"}; + + /* + * Iterate over each mock application. + * + * NOTE: do not call ptest_fatal(). This code runs after the test has + * completed. + */ + if (mock_appl_list != 0) { + info = htable_list(mock_appl_list); + for (ht = info; *ht; ht++) { + ma = (MOCK_APPL *) ht[0]->value; + + /* + * Iterate over each expectation. + */ + for (me = ma->head; me != 0; me = next_me) { + next_me = me->next; + if (me->calls_expected > 0 + && me->calls_expected > me->calls_made) { + ma->sig->print_expect(me, buf ? buf : + (buf = vstring_alloc(100))); + ptest_error(t, "%s:%d got %d call%s for %s(%s), want %d", + me->file, me->line, me->calls_made, + plural[me->calls_made != 1], + ma->sig->name, vstring_str(buf), + me->calls_expected); + } else if (me->calls_made == 0) { + ma->sig->print_expect(me, buf ? buf : + (buf = vstring_alloc(100))); + ptest_error(t, "%s:%d got 0 calls for %s(%s), want 1 or more", + me->file, me->line, ma->sig->name, + vstring_str(buf)); + } + ma->sig->free_expect(me); + } + htable_delete(mock_appl_list, ma->sig->name, (void (*) (void *)) 0); + mock_appl_list_free(ma); + } + if (buf) + vstring_free(buf); + myfree(info); + } + if (mock_appl_list != 0 && mock_appl_list->used != 0) + ptest_error(t, "pmock_expect_wrapup: mock_appl_list->used is %ld", + (long) mock_appl_list->used); +} diff --git a/postfix/src/ptest/pmock_expect.h b/postfix/src/ptest/pmock_expect.h new file mode 100644 index 000000000..541cb52ea --- /dev/null +++ b/postfix/src/ptest/pmock_expect.h @@ -0,0 +1,78 @@ +#ifndef _PMOCK_EXPECT_H_INCLUDED_ +#define _PMOCK_EXPECT_H_INCLUDED_ + +/*++ +/* NAME +/* pmock_expect 3h +/* SUMMARY +/* mock test support +/* SYNOPSIS +/* #include +/* DESCRIPTION +/* .nf + + /* + * Utility library. + */ +#include + + /* + * Test library. + */ +#include + + /* + * Generic MOCK expectation parent class. Real mock applications will + * subclass this, and add their own application-specific fields with + * expected inputs and prepared outputs. + */ +typedef struct MOCK_EXPECT { + char *file; /* __FILE__ */ + int line; /* __LINE__ */ + int calls_expected; /* expected count */ + int calls_made; /* actual count */ + struct MOCK_EXPECT *next; /* linkage */ +} MOCK_EXPECT; + + /* + * Application signature with MOCK_EXPECT generic utility functions. + */ +typedef int (*MOCK_EXPECT_MATCH_FN) (const MOCK_EXPECT *, const MOCK_EXPECT *); +typedef void (*MOCK_EXPECT_ASSIGN_FN) (const MOCK_EXPECT *, void *); +typedef char *(*MOCK_EXPECT_PRNT_FN) (const MOCK_EXPECT *, VSTRING *); +typedef void (*MOCK_EXPECT_FREE_FN) (MOCK_EXPECT *); + + /* + * Common information for all expectations of a specific mock application. + */ +typedef struct MOCK_APPL_SIG { + const char *name; /* application sans mock_ prefix */ + MOCK_EXPECT_MATCH_FN match_expect; /* match expectation inputs */ + MOCK_EXPECT_ASSIGN_FN assign_expect;/* assign expectation outputs */ + MOCK_EXPECT_PRNT_FN print_expect; /* print call or expectation */ + MOCK_EXPECT_FREE_FN free_expect; /* destruct expectation */ +} MOCK_APPL_SIG; + +extern MOCK_EXPECT *pmock_expect_create(const MOCK_APPL_SIG *, const char *file, + int line, int calls_expected, + ssize_t); +extern int pmock_expect_apply(const MOCK_APPL_SIG *, const MOCK_EXPECT *, void *); +extern void pmock_expect_free(MOCK_EXPECT *); + + /* + * Report unused expectations and destroy all evidence and expectations. + */ +extern void pmock_expect_wrapup(PTEST_CTX *); + +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* Google, Inc. +/* 111 8th Avenue +/* New York, NY 10011, USA +/*--*/ + +#endif diff --git a/postfix/src/ptest/pmock_expect_test.c b/postfix/src/ptest/pmock_expect_test.c new file mode 100644 index 000000000..ef440dc3f --- /dev/null +++ b/postfix/src/ptest/pmock_expect_test.c @@ -0,0 +1,245 @@ + /* + * This file contains two parts. + * + * 1 - A trivial mock function, including code to set up expectations and to + * respond to calls. + * + * 2 - Test cases that exercise this mock function and the mock support + * infrastructure. + */ + + /* + * Part 1: This emulates a trivial function: + * + * int foo(const char *arg_in, char **arg_out) + * + * When the mock foo() function is called with an arg_in value that matches an + * expected input (see below) then the mock foo() function stores a prepared + * value through the arg_out argument, and returns a prepared function + * result value. + * + * The prepared response an result are set up with: + * + * void expect_foo(const char *file, int line, int calls_expected, int retval, + * const char *arg_in, const char *arg_out) + * + * This saves deep copies of arg_in and arg_out, and the result value in + * retval. The file name and line number are used to improve warning + * messages; typically these are specified at the call site with __FILE__ + * and __LINE__. + * + * The code below provides mock-specific helpers that match inputs against an + * expectation and that output prepared responses. These are called by the + * mock support infrastructure as needed. + */ + + /* + * System library. + */ +#include +#include +#include + + /* + * Utility library. + */ +#include + + /* + * Test library. + */ +#include +#include +#include + + /* + * Deep copies of expected inputs and prepared outputs specified in an + * 'expect_foo' call. This structure will also be used to capture shallow + * copies of inputs for a 'foo' call. + */ +struct foo_expectation { + MOCK_EXPECT mock_expect; /* generic fields */ + char *arg_in; /* input arguments */ + int retval; /* result value */ + char *arg_out; /* output argument */ +}; + + /* + * Pointers to the outputs for a 'foo' call. + */ +struct foo_targets { + char **arg_out; /* output argument pointer */ + int *retval; /* result value pointer */ +}; + +/* match_foo - match inputs against expectation */ + +static int match_foo(const MOCK_EXPECT *expect, const MOCK_EXPECT *inputs) +{ + struct foo_expectation *pe = (struct foo_expectation *) expect; + struct foo_expectation *pi = (struct foo_expectation *) inputs; + + return (strcmp(pe->arg_in, pi->arg_in) == 0); +} + +/* assign_foo - assign expected output */ + +static void assign_foo(const MOCK_EXPECT *expect, void *targets) +{ + struct foo_expectation *pe = (struct foo_expectation *) expect; + struct foo_targets *pt = (struct foo_targets *) targets; + + *(pt->arg_out) = mystrdup(pe->arg_out); + *(pt->retval) = pe->retval; +} + +/* print_foo - print expected inputs */ + +static char *print_foo(const MOCK_EXPECT *expect, VSTRING *buf) +{ + struct foo_expectation *pe = (struct foo_expectation *) expect; + + vstring_sprintf(buf, "%s", pe->arg_in); + return (vstring_str(buf)); +} + +/* free_foo - destructor */ + +static void free_foo(MOCK_EXPECT *expect) +{ + struct foo_expectation *pe = (struct foo_expectation *) expect; + + if (pe->arg_in) + myfree(pe->arg_in); + if (pe->arg_out) + myfree(pe->arg_out); + pmock_expect_free(expect); +} + + /* + * The mock name and its helper callbacks in one place. + */ +static const MOCK_APPL_SIG foo_sig = { + "foo", + match_foo, + assign_foo, + print_foo, + free_foo, +}; + +/* expect_foo - set up expectation */ + +static void expect_foo(const char *file, int line, int calls_expected, + int retval, const char *arg_in, + const char *arg_out) +{ + struct foo_expectation *pe; + + pe = (struct foo_expectation *) + pmock_expect_create(&foo_sig, file, line, calls_expected, sizeof(*pe)); + pe->arg_in = mystrdup(arg_in); + pe->retval = retval; + pe->arg_out = mystrdup(arg_out); +} + +/* foo - mock foo */ + +static int foo(const char *arg_in, char **arg_out) +{ + struct foo_expectation inputs; + struct foo_targets targets; + int retval = -1; + + /* + * Bundle the arguments to simplify handling. + */ + inputs.arg_in = (char *) arg_in; + targets.arg_out = arg_out; + targets.retval = &retval; + + /* + * TODO: bail out if there was no match? + */ + (void) pmock_expect_apply(&foo_sig, &inputs.mock_expect, (void *) &targets); + + return (retval); +} + + /* + * Part 2: Test cases. See ptest_main.h for a documented example. + */ + + /* + * The ptestcase structure. + */ +typedef struct PTEST_CASE { + const char *testname; /* Human-readable description */ + void (*action) (PTEST_CTX *, const struct PTEST_CASE *); +} PTEST_CASE; + +static void test_unused_expectation_1_of_2(PTEST_CTX *t, + const PTEST_CASE *unused) +{ + const char *want_arg_out = "output"; + char *got_arg_out = 0; + int got_retval, want_retval = 42; + + /* + * Set up an expectation for two calls, but intentionally make only one. + */ + expect_foo(__FILE__, __LINE__, 2, want_retval, "input", want_arg_out); + got_retval = foo("input", &got_arg_out); + if (got_arg_out == 0 || strcmp(got_arg_out, want_arg_out) != 0) { + ptest_error(t, "foo: got '%s', want '%s'", + got_arg_out ? got_arg_out : "(null)", want_arg_out); + } else if (got_retval != want_retval) { + ptest_error(t, "foo: got retval %d, want %d", got_retval, want_retval); + } + + /* + * This error is intentional. Do not count as a failure. The error will + * be logged after this test terminates. + */ + expect_ptest_error(t, " got 1 call for foo(input), want 2"); + + /* + * Cleanup. + */ + if (got_arg_out) + myfree(got_arg_out); +} + +static void test_unused_expectation_0_of_0_1(PTEST_CTX *t, + const PTEST_CASE *unused) +{ + int want_retval = 42; + + /* + * Give each expectation a unique line number. Here, we make zero calls + * while expecting exactly one call, or one or more calls. + */ + expect_foo(__FILE__, __LINE__, 1, want_retval, "input", "output"); + expect_foo(__FILE__, __LINE__, 0, want_retval, "input", "output"); + + /* + * These errors are intentional. Do not count as a failure. + */ + expect_ptest_error(t, " got 0 calls for foo(input), want 1 or more"); + expect_ptest_error(t, " got 0 calls for foo(input), want 1"); +} + + /* + * Test cases. The "success" calls exercise the expectation match and apply + * helpers, and "missing" tests exercise the print helpers. All tests + * exercise the expectation free helpers. + */ +const PTEST_CASE ptestcases[] = { + { + "unused expectation 1 of 2", test_unused_expectation_1_of_2, + }, + { + "unused expectation 0 of 0-1", test_unused_expectation_0_of_0_1, + }, +}; + +#include diff --git a/postfix/src/ptest/ptest.h b/postfix/src/ptest/ptest.h new file mode 100644 index 000000000..ae7b57bc9 --- /dev/null +++ b/postfix/src/ptest/ptest.h @@ -0,0 +1,127 @@ +#ifndef _PTEST_H_INCLUDED_ +#define _PTEST_H_INCLUDED_ + +/*++ +/* NAME +/* ptest 3h +/* SUMMARY +/* run-time test support +/* SYNOPSIS +/* #include +/* DESCRIPTION +/* .nf + + /* + * System library. + */ +#include +#include + + /* + * Utility library. + */ +#include +#include /* XXX for MSG_JMP_BUF in PTEST_RUN */ +#include +#include + + /* + * TODO: factor out, and merge with DICT_JMP_BUF, MSG_JMP_BUF, + * SLMDB_JMP_BUF, VSTREAM_JMP_BUF. + */ +#ifdef NO_SIGSETJMP +#define TEST_JMP_BUF jmp_buf +#define ptest_longjmp(bufp, val) longjmp((bufp)[0], (val)) +#else +#define TEST_JMP_BUF sigjmp_buf +#define ptest_longjmp(bufp, val) siglongjmp((bufp)[0], (val)) +#endif + + /* + * All run-time test info in one place. + */ +typedef void (*PTEST_DEFER_FN) (void *); + +#define PTEST_CTX_FLAG_SKIP (1<<0) /* This test is skipped */ +#define PTEST_CTX_FLAG_FAIL (1<<1) /* This test has failed */ + +typedef struct PTEST_CTX { + /* ptest_ctx.c */ + char *name; /* Null, name, or name/name/... */ + TEST_JMP_BUF *jbuf; /* Used by ptest_fatal(), msg(3) */ + struct PTEST_CTX *parent; /* In case tests are nested */ + int flags; /* See above */ + /* ptest_run.c */ + int sub_pass; /* Subtests that passed */ + int sub_fail; /* Subtests that failed */ + int sub_skip; /* Subtests that were skipped */ + PTEST_DEFER_FN defer_fn; /* To be called after test... */ + void *defer_ctx; /* ...with this argument */ + /* ptest_error.c */ + VSTREAM *err_stream; /* Output stream */ + VSTRING *err_buf; /* Formatting buffer */ + ARGV *allow_errors; /* Allowed errors */ + /* ptest_log.c */ + VSTRING *log_buf; /* Formatting buffer */ + ARGV *allow_logs; /* Allowed logs */ +} PTEST_CTX; + + /* + * ptest_ctx.c + */ +extern PTEST_CTX *ptest_ctx_create(const char *, TEST_JMP_BUF *); +extern PTEST_CTX *ptest_ctx_current(void); +extern void ptest_ctx_free(PTEST_CTX *); + + /* + * ptest_error.c + */ +extern void ptest_error_setup(PTEST_CTX *, VSTREAM *); +extern void expect_ptest_error(PTEST_CTX *, const char *); +extern void PRINTFLIKE(2, 3) ptest_info(PTEST_CTX *, const char *,...); +extern void PRINTFLIKE(2, 3) ptest_error(PTEST_CTX *, const char *,...); +extern NORETURN PRINTFLIKE(2, 3) ptest_fatal(PTEST_CTX *, const char *,...); +extern int ptest_error_wrapup(PTEST_CTX *); + + /* + * ptest_log.c + */ +extern void ptest_log_setup(PTEST_CTX *); +extern void expect_ptest_log_event(PTEST_CTX *, const char *); +extern void ptest_log_wrapup(PTEST_CTX *); + + /* + * ptest_run.c + */ +extern void ptest_run_prolog(PTEST_CTX *); +extern void ptest_run_epilog(PTEST_CTX *, PTEST_CTX *); +extern NORETURN ptest_skip(PTEST_CTX *); +extern NORETURN ptest_return(PTEST_CTX *); +extern void ptest_defer(PTEST_CTX *, PTEST_DEFER_FN, void *); + +#define PTEST_RUN(t, test_name, body_in_braces) do { \ + MSG_JMP_BUF new_buf; \ + PTEST_CTX *parent = t; \ + t = ptest_ctx_create((test_name), &new_buf); \ + ptest_run_prolog(t); \ + if (msg_setjmp(&new_buf) == 0) { \ + body_in_braces \ + } \ + msg_resetjmp(parent->jbuf); \ + ptest_run_epilog(t, parent); \ + ptest_ctx_free(t); \ + t = parent; \ +} while (0) + +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* Google, Inc. +/* 111 8th Avenue +/* New York, NY 10011, USA +/*--*/ + +#endif diff --git a/postfix/src/ptest/ptest_ctx.c b/postfix/src/ptest/ptest_ctx.c new file mode 100644 index 000000000..71b7c568a --- /dev/null +++ b/postfix/src/ptest/ptest_ctx.c @@ -0,0 +1,131 @@ +/*++ +/* NAME +/* ptest_ctx 3 +/* SUMMARY +/* test context support +/* SYNOPSIS +/* #include +/* +/* PTEST_CTX ptest_ctx_create( +/* const char *name, +/* TEST_JMP_BUF *jbuf) +/* +/* PTEST_CTX *ptest_ctx_current(void) +/* +/* int ptest_ctx_free(PTEST_CTX *t) +/* DESCRIPTION +/* This module manages a stack of contexts that are used by +/* tests. +/* +/* ptest_ctx_create() is called by test infrastructure before +/* a test is run. It returns an initialized PTEST_CTX object +/* after making it the current test context. The jbuf argument +/* references jump buffer that will be used by ptest_fatal(), +/* msg_fatal() and msg_panic(). +/* +/* ptest_ctx_current() returns the current test context. This +/* function exists because mocked functions must be called +/* without an argument that specifies a test context. +/* +/* ptest_ctx_free() is called by test infrastructure after a +/* test terminates and all error reporting has completed. +/* It destroys the PTEST_CTX object. +/* DIAGNOSTICS +/* ptest_ctx_current() will panic if the test context stack is +/* empty. +/* +/* ptest_ctx_free() will panic if the argument does not specify +/* the current test context. +/* SEE ALSO +/* pmock_expect(3), mock test support +/* ptest_error(3), test error reporter +/* ptest_log(3), log receiver +/* ptest_main(3), test driver +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* Google, Inc. +/* 111 8th Avenue +/* New York, NY 10011, USA +/*--*/ + + /* + * System library. + */ +#include +#include + + /* + * Utility library. + */ +#include +#include +#include +#include + + /* + * Test library. + */ +#include + +static PTEST_CTX *ptest_ctx_head; + +/* ptest_ctx_create - create initialized PTEST_CTX object */ + +PTEST_CTX *ptest_ctx_create(const char *name, TEST_JMP_BUF *jbuf) +{ + PTEST_CTX *parent = ptest_ctx_head; + PTEST_CTX *t; + + t = mymalloc(sizeof(*t)); + if (name == 0) /* main-level context */ + t->name = 0; + else if (parent->name == 0) /* top-level test context */ + t->name = mystrdup(name); + else /* sub test */ + t->name = concatenate(parent->name, "/", name, (char *) 0); + t->jbuf = jbuf; + t->parent = parent; + t->flags = 0; + /* ptest_run.c specific */ + t->sub_pass = t->sub_fail = t->sub_skip = 0; + /* ptest_error.c specific */ + t->err_stream = 0; + t->err_buf = 0; + t->allow_errors = 0; + /* ptest_log.c specific */ + t->log_buf = 0; + t->allow_logs = 0; + /* ptest_defer.c specific */ + t->defer_fn = 0; + t->defer_ctx = 0; + + ptest_ctx_head = t; + + return (t); +} + +/* ptest_ctx_current - return current context or die */ + +PTEST_CTX *ptest_ctx_current() +{ + if (ptest_ctx_head == 0) + msg_panic("ptest_ctx_current: no test context"); + return (ptest_ctx_head); +} + +/* ptest_ctx_free - destroy PTEST_CTX or die */ + +void ptest_ctx_free(PTEST_CTX *t) +{ + if (t != ptest_ctx_head) + msg_panic("ptest_ctx_free: wrong test context - " + "should you use ptest_return()?"); + ptest_ctx_head = t->parent; + if (t->name) + myfree(t->name); + myfree((void *) t); +} diff --git a/postfix/src/ptest/ptest_error.c b/postfix/src/ptest/ptest_error.c new file mode 100644 index 000000000..4c691de09 --- /dev/null +++ b/postfix/src/ptest/ptest_error.c @@ -0,0 +1,221 @@ +/*++ +/* NAME +/* ptest_error 3 +/* SUMMARY +/* test error and non-error support +/* SYNOPSIS +/* #include +/* +/* void expect_ptest_error( +/* PTEST_CTX *t, +/* const char *text) +/* +/* void PRINTFLIKE(2, 3) ptest_info( +/* PTEST_CTX *t, +/* const char *, ...) +/* +/* void PRINTFLIKE(2, 3) ptest_error( +/* PTEST_CTX *t, +/* const char *, ...) +/* +/* NORETURN PRINTFLIKE(2, 3) ptest_fatal( +/* PTEST_CTX *t, +/* const char *, ...) +/* TEST INFRASTRUCTURE SUPPORT +/* PTEST_CTX ptest_error_setup(VSTREAM *err_stream) +/* +/* int ptest_error_wrapup(PTEST_CTX *t) +/* DESCRIPTION +/* This module manages errors and non-errors that are reported +/* by tests. +/* +/* ptest_info() is called from inside a test, to report a +/* non-error condition, for example, to report progress. +/* +/* ptest_error() is called from inside a test, to report a +/* non-fatal test error (after the call is finished, the test +/* will continue). If the error text matches a pattern given +/* to an earlier expect_ptest_error() call (see below), then +/* this ptest_error() call will be ignored once, and treated +/* as a non-error. Otherwise, ptest_error() logs the error and +/* increments an error count. +/* +/* expect_ptest_error() is called from inside a test. It requires +/* that a ptest_error() call will be made whose formatted text +/* contains a substring that matches the text argument. For +/* robustness, do not include file line number information in +/* the expected text. If the expected ptest_error() call is +/* made, then that call will be ignored once, and treated as +/* a non-error (call expect_ptest_error() multiple times to +/* ignore an error multiple times). If the expected ptest_error() +/* call is not made, then ptest_error_wrapup() will report an +/* error and the test will fail. +/* +/* ptest_fatal() is called from inside a test. It reports a +/* fatal test error and increments an error count. A ptest_fatal() +/* call does not return, instead it terminates the test. +/* ptest_fatal() calls cannot be expected and ignored with +/* expect_ptest_error(). +/* +/* ptest_error_setup() is called by test infrastructure before +/* a test is run. It updates a PTEST_CTX object. The err_stream +/* argument specifies the output stream for error reporting. +/* +/* ptest_error_wrapup() is called by test infrastructure after +/* a test terminates. It calls ptest_error() to report any +/* missing ptest_error() calls, destroys the PTEST_CTX information +/* that was allocated with ptest_error_setup(), and returns the +/* final error count. +/* DIAGNOSTICS +/* The above functions write to the VSTREAM specified in the +/* ptest_error_setup() call. +/* SEE ALSO +/* pmock_expect(3), mock test support +/* ptest_main(3h), test driver +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* Google, Inc. +/* 111 8th Avenue +/* New York, NY 10011, USA +/*--*/ + + /* + * System library. + */ +#include +#include +#include + + /* + * Utility library. + */ +#include +#include +#include +#include +#include + + /* + * Test library. + */ +#include + + /* + * SLMs. + */ +#define STR(x) vstring_str(x) + +/* ptest_error_setup - populate PTEST_CTX object */ + +void ptest_error_setup(PTEST_CTX *t, VSTREAM *err_stream) +{ + t->flags &= ~PTEST_CTX_FLAG_FAIL; + t->err_stream = err_stream; + t->err_buf = vstring_alloc(100); + t->allow_errors = argv_alloc(1); +} + +/* expect_ptest_error - require and bless a non-fatal error */ + +void expect_ptest_error(PTEST_CTX *t, const char *text) +{ + argv_add(t->allow_errors, text, (char *) 0); +} + +/* ptest_info - report non-error condition */ + +void ptest_info(PTEST_CTX *t, const char *fmt,...) +{ + va_list ap; + + /* + * Format the message. + */ + va_start(ap, fmt); + vstream_vfprintf(t->err_stream, fmt, ap); + vstream_fprintf(t->err_stream, "\n"); + va_end(ap); + vstream_fflush(t->err_stream); +} + +/* ptest_error - report non-fatal error */ + +void ptest_error(PTEST_CTX *t, const char *fmt,...) +{ + va_list ap; + char **cpp; + + /* + * Format the message. + */ + va_start(ap, fmt); + vstring_vsprintf(t->err_buf, fmt, ap); + va_end(ap); + + /* + * Skip this error if it was expected. + */ + for (cpp = t->allow_errors->argv; *cpp; cpp++) { + if (strstr(STR(t->err_buf), *cpp) != 0) { + argv_delete(t->allow_errors, cpp - t->allow_errors->argv, 1); + return; + } + } + + /* + * Report the message. + */ + vstream_fprintf(t->err_stream, "error: %s\n", STR(t->err_buf)); + vstream_fflush(t->err_stream); + t->flags |= PTEST_CTX_FLAG_FAIL; +} + +/* ptest_fatal - report fatal error */ + +NORETURN ptest_fatal(PTEST_CTX *t, const char *fmt,...) +{ + va_list ap; + + /* + * This has no code in common with ptest_error(). + */ + vstream_fprintf(t->err_stream, "fatal: "); + va_start(ap, fmt); + vstream_vfprintf(t->err_stream, fmt, ap); + va_end(ap); + vstream_fprintf(t->err_stream, "\n"); + vstream_fflush(t->err_stream); + t->flags |= PTEST_CTX_FLAG_FAIL; + ptest_longjmp(t->jbuf, 1); +} + +/* ptest_error_wrapup - enforce error expectations and clean up */ + +extern int ptest_error_wrapup(PTEST_CTX *t) +{ + char **cpp; + int fail_flag; + + /* + * Report a new error if an expected error did not happen. + */ + for (cpp = t->allow_errors->argv; *cpp; cpp++) { + vstream_fprintf(t->err_stream, "Missing error: want '%s'\n", *cpp); + t->flags |= PTEST_CTX_FLAG_FAIL; + vstream_fflush(t->err_stream); + } + fail_flag = (t->flags & PTEST_CTX_FLAG_FAIL); + + /* + * Clean up the PTEST_CTX fields that we created. + */ + vstring_free(t->err_buf); + t->err_buf = 0; + argv_free(t->allow_errors); + t->flags &= ~PTEST_CTX_FLAG_FAIL; + return (fail_flag); +} diff --git a/postfix/src/ptest/ptest_log.c b/postfix/src/ptest/ptest_log.c new file mode 100644 index 000000000..40212e511 --- /dev/null +++ b/postfix/src/ptest/ptest_log.c @@ -0,0 +1,149 @@ +/*++ +/* NAME +/* ptest_log 3 +/* SUMMARY +/* log event receiver support +/* SYNOPSIS +/* #include +/* +/* void expect_ptest_log_event( +/* PTEST_CTX *t, +/* const char *text) +/* INFRASTRUCTURE SUPPORT +/* void ptest_log_setup( +/* PTEST_CTX *t) +/* +/* void ptest_log_wrapup( +/* PTEST_CTX *t) +/* DESCRIPTION +/* This module inspects msg(3) logging. +/* +/* expect_ptest_log_event() is called from a test. It requires +/* that an msg(3) call will be made whose formatted text +/* contains a substring that matches the text argument. For +/* robustness, do not include file name or line number +/* information. If a match fails, then the log event receiver +/* will call ptest_error() to report the unexpected msg(3) +/* call. If the expected msg(3) call is not made, then +/* ptest_log_wrapup() will call ptest_error() to report the +/* missing call. +/* +/* ptest_log_setup() is called by testing infrastructure before +/* a test is started. It updates the PTEST_CTX structure, and +/* installs an msg(3) log event receiver. +/* +/* ptest_log_wrapup() is called by test infrastructure after +/* a test terminates. It calls ptest_error() to report any +/* unmatched expect_ptest_log_event() expectations, and destroys +/* buffers that were created by ptest_log_setup(). +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* Google, Inc. +/* 111 8th Avenue +/* New York, NY 10011, USA +/*--*/ + + /* + * System library. + */ +#include +#include + + /* + * Utility library. + */ +#include +#include +#include + + /* + * Test library. + */ +#include + + /* + * SLMs. + */ +#define STR(x) vstring_str(x) + +/* ptest_log_event - receive log event */ + +static void ptest_log_event(int level, const char *text, void *context) +{ + static const char *level_text[] = { + "info", "warning", "error", "fatal", "panic", + }; + PTEST_CTX *t = (PTEST_CTX *) context; + char **cpp; + + /* + * Silence events for parent handlers. + */ + if (t != ptest_ctx_current()) + return; + + /* + * Sanity checks. + */ + if (level < 0 || level >= (int) (sizeof(level_text) / sizeof(level_text[0]))) + msg_panic("ptest_log_event: invalid severity level: %d", level); + + /* + * Format the text. + */ + if (level == MSG_INFO) { + vstring_sprintf(t->log_buf, "%s", text); + } else { + vstring_sprintf(t->log_buf, "%s: %s", level_text[level], text); + } + + /* + * Handle expected versus unexpected text. + */ + for (cpp = t->allow_logs->argv; *cpp; cpp++) { + if (strstr(STR(t->log_buf), *cpp) != 0) { + argv_delete(t->allow_logs, cpp - t->allow_logs->argv, 1); + return; + } + } + ptest_error(t, "Unexpected log event: got '%s'", STR(t->log_buf)); +} + +/* ptest_log_setup - install logging receiver */ + +void ptest_log_setup(PTEST_CTX *t) +{ + if (t != ptest_ctx_current()) + msg_panic("ptest_log_setup: not current context"); + t->log_buf = vstring_alloc(100); + t->allow_logs = argv_alloc(1); + msg_output_push(ptest_log_event, (void *) t); +} + +/* expect_ptest_log_event - add log event expectation */ + +void expect_ptest_log_event(PTEST_CTX *t, const char *text) +{ + if (t != ptest_ctx_current()) + msg_panic("expect_ptest_log_event: not current context"); + argv_add(t->allow_logs, text, (char *) 0); +} + +/* ptest_log_wrapup - enforce logging expectations */ + +void ptest_log_wrapup(PTEST_CTX *t) +{ + char **cpp; + + msg_output_pop(ptest_log_event, (void *) t); + for (cpp = t->allow_logs->argv; *cpp; cpp++) + ptest_error(t, "Missing log event: want '%s'", *cpp); + vstring_free(t->log_buf); + t->log_buf = 0; + argv_free(t->allow_logs); + t->allow_logs = 0; +} diff --git a/postfix/src/ptest/ptest_log_test.c b/postfix/src/ptest/ptest_log_test.c new file mode 100644 index 000000000..98ce2fd74 --- /dev/null +++ b/postfix/src/ptest/ptest_log_test.c @@ -0,0 +1,56 @@ + /* + * Test program to exercise ptest_log functions including logging. See + * comments in ptest_main.h and pmock_expect_test.c for a documented + * example. + */ + + /* + * System library. + */ +#include + + /* + * Test library. + */ +#include + +typedef struct PTEST_CASE { + const char *testname; /* Human-readable description */ + void (*action) (PTEST_CTX *t, const struct PTEST_CASE *); +} PTEST_CASE; + +static void ptest_log_non_error(PTEST_CTX *t, const PTEST_CASE *unused) +{ + /* This test passes if there is no error. */ + expect_ptest_log_event(t, "this is a non-error"); + msg_info("this is a non-error"); +} + +static void ptest_log_flags_unexpected_message(PTEST_CTX *t, const PTEST_CASE *unused) +{ + expect_ptest_error(t, "this is a forced 'Unexpected log event' error"); + msg_info("this is a forced 'Unexpected log event' error"); +} + +static void ptest_log_flags_missing_message(PTEST_CTX *t, const PTEST_CASE *unused) +{ + expect_ptest_error(t, "this is a forced 'Missing log event' error"); + expect_ptest_log_event(t, "this is a forced 'Missing log event' error"); +} + + /* + * Test cases. + */ +const PTEST_CASE ptestcases[] = { + { + "ptest_log_non_error", ptest_log_non_error, + }, + { + "ptest_log_flags_unexpected_message", ptest_log_flags_unexpected_message, + }, + { + "ptest_log_flags_missing_message", ptest_log_flags_missing_message, + }, +}; + +#include diff --git a/postfix/src/ptest/ptest_main.h b/postfix/src/ptest/ptest_main.h new file mode 100644 index 000000000..ff2d5112a --- /dev/null +++ b/postfix/src/ptest/ptest_main.h @@ -0,0 +1,162 @@ +/*++ +/* NAME +/* ptest_main 3h +/* SUMMARY +/* test driver +/* DESCRIPTION +/* This file should be included at the end of a *_test.c file. +/* It contains a main program and test driver, and supports +/* programs whether or not they use mocks as defined in +/* . +/* +/* Before including this file, a *_test.c file should define +/* the structure and content of its test cases, and the functions +/* that implement those tests: +/* +/* .nf +/* /* Begin example. */ +/* +/* #include +/* #include +/* +/* /* +/* * Test case structure. If multiple test functions cannot +/* * have their test data in a shared PTEST_CASE structure, then +/* * each test function can define its own test data, and run +/* * multiple tests with PTEST_RUN(). See documentation in +/* * ptest_run.c. +/* */ +/* typedef struct PTEST_CASE { +/* const char *testname; /* Human-readable description */ +/* void (*action) (PTEST_CTX *, const struct PTEST_CASE *); +/* /* Optionally, your test data fields... */ +/* } PTEST_CASE; +/* +/* /* +/* * Test functions. These should not use msg_xxx() functions. +/* * +/* * To report a test error use ptest_error(t, ...) and to abort a +/* * test use ptest_fatal(t, ...). Tests with errors will not PASS. +/* * +/* * To "expect" a non-fatal error (and not count it as a failure) +/* * use expect_ptest_error(t, text) where the text is a substring +/* * of the expected error message. +/* */ +/* +/* static void test_abc(PTEST_CTX *t, const PTEST_CASE *tp) +/* { +/* int ret; +/* +/* want_abc = 42; +/* got_abc = abc(1, 2, 3); +/* if (got_abc != want_abc) +/* ptest_error(t, "abc: got %d, want %d", got_abc, want_abc); +/* } +/* +/* /* More test functions... */ +/* +/* /* Test cases. Do not terminate with null. */ +/* +/* const PTEST_CASE ptestcases[] = { +/* { "test abc", test_abc, ...}, +/* /* More test cases... */ +/* }; +/* +/* #include +/* +/* /* End example. */ +/* .fi +/* +/* The test driver iterates over each test case +/* and invokes the test case's action function with a pointer +/* to its test case. The test driver captures all logging that +/* is generated while the test case runs, including logging +/* from test functions, library functions and from the mock +/* infrastructure. +/* +/* The action function should call ptest_error() or ptest_fatal() +/* when a test fails. ptest_fatal() terminates a test but does +/* not terminate the process. +/* +/* If the tests use mocks, the mock infrastructure will log +/* unexpected mock calls, and unused mock call expectations. +/* +/* The ptest_log module will log a warning if the captured +/* logging differs from the expected logging. +/* SEE ALSO +/* msg(3) diagnostics +/* ptest_error(3) test error and non-error handling +/* ptest_log(3) log event receiver support +/* BUGS +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* Google, Inc. +/* 111 8th Avenue +/* New York, NY 10011, USA +/*--*/ + + /* + * System library. + */ +#include +#include +#include + + /* + * Utility library. + */ +#include +#include +#include +#include +#include + + /* + * Test library. + */ +#include +#include + +/* main - test driver */ + +int main(int argc, char **argv) +{ + PTEST_CTX *t; + const PTEST_CASE *tp; + int fail; + + /* + * Send msg(3) logging to stderr by default. + */ + msg_vstream_init(basename(argv[0]), VSTREAM_ERR); + + /* + * The main-level PTEST_CTX context has no name and no long jump context. + * It's sole purpose is to run tests and to aggregate pass/skip/fail counts. + */ + t = ptest_ctx_create((char *) 0, (MSG_JMP_BUF *) 0); + + /* + * Run each test in its own PTEST_CTX context with its own log + * interceptor and long jump context. Each test can invoke PTEST_RUN() to + * run one or more of subtests in their own context with their own test + * data, instead of having to store all test data in a PTEST_CASE + * structure. + */ +#define NROF(x) (sizeof(x)/sizeof((x)[0])) + + for (tp = ptestcases; tp < ptestcases + NROF(ptestcases); tp++) { + if (tp->testname == 0) + msg_fatal("Null testname in ptestcases array!"); + PTEST_RUN(t, tp->testname, { + tp->action(t, tp); + }); + } + msg_info("PASS: %d, SKIP: %d, FAIL: %d", t->sub_pass, t->sub_skip, fail = t->sub_fail); + ptest_ctx_free(t); + exit(fail > 0); +} diff --git a/postfix/src/ptest/ptest_run.c b/postfix/src/ptest/ptest_run.c new file mode 100644 index 000000000..66c35cabc --- /dev/null +++ b/postfix/src/ptest/ptest_run.c @@ -0,0 +1,159 @@ +/*++ +/* NAME +/* ptest_run 3h +/* SUMMARY +/* test runner +/* SYNOPSIS +/* #include +/* +/* void PTEST_RUN( +/* PTEST_CTX *t, +/* const char *test_name, +/* { body_in_braces }) +/* +/* NORETURN ptest_skip(PTEST_CTX *t) +/* +/* NORETURN ptest_return(PTEST_CTX *t) +/* +/* void ptest_defer( +/* PTEST_CTX *t, +/* void (*defer_fn)(void *) +/* void *defer_ctx) +/* DESCRIPTION +/* PTEST_RUN() is called from inside a test to run a subtest. +/* +/* PTEST_RUN() is a macro that runs the { body_in_braces } +/* with msg(3) logging temporarily redirected to a buffer, and +/* with panic, fatal, error, and non-error functions that +/* terminate a test without terminating the process. +/* +/* To use this as a subtest inside a PTEST_CASE action: +/* +/* .na +/* static void action(PTEST_CTX *t, const PTEST_CASE *tp) +/* { +/* struct subtest { +/* // ... +/* }; +/* static const struct subtest tests[] = { +/* // ...subtest data and expectations... +/* }; +/* struct subtest *sp; +/* for (sp = tests; sp < tests + sizeof(tests) / sizeof(tests[0]); sp++) { +/* PTEST_RUN(t, sp->name, { +/* // Test code that uses sp->mumble here. +/* // Use ptest_error(), ptest_fatal(), or ptest_return() +/* // to report an error or terminate a test, or +/* // ptest_skip() to skip a test. +/* }); +/* } +/* } +/* +/* ptest_skip() is called from inside a test. It flags a test +/* as skipped, and terminates the test without terminating the +/* process. +/* +/* ptest_return() is called from inside a test. It terminates +/* the test without terminating the process. +/* +/* ptest_defer() may be called once from a test, to defer some +/* processing until after the test completes. This is typically +/* used to eliminate a resource leak in tests that terminate +/* the test early (i.e. that return with a long jump). +/* +/* To "undo" a ptest_defer() call, call the function with a +/* null defer_fn argument. Then, it may be called again to +/* set up deferred execution. +/* SEE ALSO +/* pmock_expect(3), mock for hermetic tests +/* ptest_error(3), test error support +/* ptest_log(3), logging receiver support +/* BUGS +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* Google, Inc. +/* 111 8th Avenue +/* New York, NY 10011, USA +/*--*/ + + /* + * System library. + */ +#include + + /* + * Utility library. + */ +#include +#include +#include + + /* + * Test library. + */ +#include +#include + +/* ptest_run_prolog - encapsulate PTEST_RUN() dependencies */ + +void ptest_run_prolog(PTEST_CTX *t) +{ + ptest_error_setup(t, VSTREAM_ERR); + ptest_info(t, "RUN %s", t->name); + ptest_log_setup(t); + msg_vstream_enable(0); +} + +/* ptest_run_epilog - encapsulate PTEST_RUN() dependencies */ + +void ptest_run_epilog(PTEST_CTX *t, PTEST_CTX *parent) +{ + msg_vstream_enable(1); + ptest_log_wrapup(t); + pmock_expect_wrapup(t); + if (ptest_error_wrapup(t) != 0 || t->sub_fail != 0) { + ptest_info(t, "FAIL %s", t->name); + parent->sub_fail += 1; + } else if (t->flags & PTEST_CTX_FLAG_SKIP) { + ptest_info(t, "SKIP %s", t->name); + parent->sub_skip += 1; + } else { + ptest_info(t, "PASS %s", t->name); + parent->sub_pass += 1; + } + parent->sub_pass += t->sub_pass; + parent->sub_fail += t->sub_fail; + parent->sub_skip += t->sub_skip; + if (t->defer_fn) + t->defer_fn(t->defer_ctx); +} + +/* ptest_skip - skip a test and return from test */ + +NORETURN ptest_skip(PTEST_CTX *t) +{ + t->flags |= PTEST_CTX_FLAG_SKIP; + ptest_longjmp(t->jbuf, 1); +} + +/* ptest_return - early return from test */ + +NORETURN ptest_return(PTEST_CTX *t) +{ + ptest_longjmp(t->jbuf, 1); +} + +/* ptest_defer - post-test processing */ + +void ptest_defer(PTEST_CTX *t, PTEST_DEFER_FN defer_fn, + void *defer_ctx) +{ + if (t->defer_fn && defer_fn) + msg_panic("ptest_defer: multiple calls for this test context"); + t->defer_fn = defer_fn; + t->defer_ctx = defer_ctx; +} diff --git a/postfix/src/qmqpd/Makefile.in b/postfix/src/qmqpd/Makefile.in index d4cdf3387..c2d8e940a 100644 --- a/postfix/src/qmqpd/Makefile.in +++ b/postfix/src/qmqpd/Makefile.in @@ -120,6 +120,7 @@ qmqpd_peer.o: ../../include/valid_mailhost_addr.h qmqpd_peer.o: ../../include/vbuf.h qmqpd_peer.o: ../../include/vstream.h qmqpd_peer.o: ../../include/vstring.h +qmqpd_peer.o: ../../include/wrap_netdb.h qmqpd_peer.o: qmqpd.h qmqpd_peer.o: qmqpd_peer.c qmqpd_state.o: ../../include/attr.h diff --git a/postfix/src/smtp/Makefile.in b/postfix/src/smtp/Makefile.in index 6412e0e36..bb0e27832 100644 --- a/postfix/src/smtp/Makefile.in +++ b/postfix/src/smtp/Makefile.in @@ -135,6 +135,7 @@ smtp.o: ../../include/tok822.h smtp.o: ../../include/vbuf.h smtp.o: ../../include/vstream.h smtp.o: ../../include/vstring.h +smtp.o: ../../include/wrap_netdb.h smtp.o: lmtp_params.c smtp.o: smtp.c smtp.o: smtp.h @@ -180,6 +181,7 @@ smtp_addr.o: ../../include/tok822.h smtp_addr.o: ../../include/vbuf.h smtp_addr.o: ../../include/vstream.h smtp_addr.o: ../../include/vstring.h +smtp_addr.o: ../../include/wrap_netdb.h smtp_addr.o: smtp.h smtp_addr.o: smtp_addr.c smtp_addr.o: smtp_addr.h @@ -231,6 +233,7 @@ smtp_chat.o: ../../include/tok822.h smtp_chat.o: ../../include/vbuf.h smtp_chat.o: ../../include/vstream.h smtp_chat.o: ../../include/vstring.h +smtp_chat.o: ../../include/wrap_netdb.h smtp_chat.o: smtp.h smtp_chat.o: smtp_chat.c smtp_connect.o: ../../include/argv.h @@ -282,6 +285,7 @@ smtp_connect.o: ../../include/tok822.h smtp_connect.o: ../../include/vbuf.h smtp_connect.o: ../../include/vstream.h smtp_connect.o: ../../include/vstring.h +smtp_connect.o: ../../include/wrap_netdb.h smtp_connect.o: smtp.h smtp_connect.o: smtp_addr.h smtp_connect.o: smtp_connect.c @@ -322,6 +326,7 @@ smtp_key.o: ../../include/tok822.h smtp_key.o: ../../include/vbuf.h smtp_key.o: ../../include/vstream.h smtp_key.o: ../../include/vstring.h +smtp_key.o: ../../include/wrap_netdb.h smtp_key.o: smtp.h smtp_key.o: smtp_key.c smtp_map11.o: ../../include/argv.h @@ -362,6 +367,7 @@ smtp_map11.o: ../../include/tok822.h smtp_map11.o: ../../include/vbuf.h smtp_map11.o: ../../include/vstream.h smtp_map11.o: ../../include/vstring.h +smtp_map11.o: ../../include/wrap_netdb.h smtp_map11.o: smtp.h smtp_map11.o: smtp_map11.c smtp_misc.o: ../../include/argv.h @@ -402,6 +408,7 @@ smtp_misc.o: ../../include/tok822.h smtp_misc.o: ../../include/vbuf.h smtp_misc.o: ../../include/vstream.h smtp_misc.o: ../../include/vstring.h +smtp_misc.o: ../../include/wrap_netdb.h smtp_misc.o: smtp.h smtp_misc.o: smtp_misc.c smtp_params.o: smtp_params.c @@ -465,6 +472,7 @@ smtp_proto.o: ../../include/vbuf.h smtp_proto.o: ../../include/vstream.h smtp_proto.o: ../../include/vstring.h smtp_proto.o: ../../include/vstring_vstream.h +smtp_proto.o: ../../include/wrap_netdb.h smtp_proto.o: ../../include/xtext.h smtp_proto.o: smtp.h smtp_proto.o: smtp_proto.c @@ -509,6 +517,7 @@ smtp_rcpt.o: ../../include/tok822.h smtp_rcpt.o: ../../include/vbuf.h smtp_rcpt.o: ../../include/vstream.h smtp_rcpt.o: ../../include/vstring.h +smtp_rcpt.o: ../../include/wrap_netdb.h smtp_rcpt.o: smtp.h smtp_rcpt.o: smtp_rcpt.c smtp_reuse.o: ../../include/argv.h @@ -547,6 +556,7 @@ smtp_reuse.o: ../../include/tok822.h smtp_reuse.o: ../../include/vbuf.h smtp_reuse.o: ../../include/vstream.h smtp_reuse.o: ../../include/vstring.h +smtp_reuse.o: ../../include/wrap_netdb.h smtp_reuse.o: smtp.h smtp_reuse.o: smtp_reuse.c smtp_reuse.o: smtp_reuse.h @@ -589,6 +599,7 @@ smtp_sasl_auth_cache.o: ../../include/tok822.h smtp_sasl_auth_cache.o: ../../include/vbuf.h smtp_sasl_auth_cache.o: ../../include/vstream.h smtp_sasl_auth_cache.o: ../../include/vstring.h +smtp_sasl_auth_cache.o: ../../include/wrap_netdb.h smtp_sasl_auth_cache.o: smtp.h smtp_sasl_auth_cache.o: smtp_sasl_auth_cache.c smtp_sasl_auth_cache.o: smtp_sasl_auth_cache.h @@ -632,6 +643,7 @@ smtp_sasl_glue.o: ../../include/tok822.h smtp_sasl_glue.o: ../../include/vbuf.h smtp_sasl_glue.o: ../../include/vstream.h smtp_sasl_glue.o: ../../include/vstring.h +smtp_sasl_glue.o: ../../include/wrap_netdb.h smtp_sasl_glue.o: ../../include/xsasl.h smtp_sasl_glue.o: smtp.h smtp_sasl_glue.o: smtp_sasl.h @@ -674,6 +686,7 @@ smtp_sasl_proto.o: ../../include/tok822.h smtp_sasl_proto.o: ../../include/vbuf.h smtp_sasl_proto.o: ../../include/vstream.h smtp_sasl_proto.o: ../../include/vstring.h +smtp_sasl_proto.o: ../../include/wrap_netdb.h smtp_sasl_proto.o: smtp.h smtp_sasl_proto.o: smtp_sasl.h smtp_sasl_proto.o: smtp_sasl_proto.c @@ -714,6 +727,7 @@ smtp_session.o: ../../include/tok822.h smtp_session.o: ../../include/vbuf.h smtp_session.o: ../../include/vstream.h smtp_session.o: ../../include/vstring.h +smtp_session.o: ../../include/wrap_netdb.h smtp_session.o: smtp.h smtp_session.o: smtp_sasl.h smtp_session.o: smtp_session.c @@ -753,6 +767,7 @@ smtp_state.o: ../../include/tok822.h smtp_state.o: ../../include/vbuf.h smtp_state.o: ../../include/vstream.h smtp_state.o: ../../include/vstring.h +smtp_state.o: ../../include/wrap_netdb.h smtp_state.o: smtp.h smtp_state.o: smtp_sasl.h smtp_state.o: smtp_state.c @@ -795,6 +810,7 @@ smtp_tls_policy.o: ../../include/valid_utf8_hostname.h smtp_tls_policy.o: ../../include/vbuf.h smtp_tls_policy.o: ../../include/vstream.h smtp_tls_policy.o: ../../include/vstring.h +smtp_tls_policy.o: ../../include/wrap_netdb.h smtp_tls_policy.o: smtp.h smtp_tls_policy.o: smtp_tls_policy.c smtp_trouble.o: ../../include/argv.h @@ -838,6 +854,7 @@ smtp_trouble.o: ../../include/tok822.h smtp_trouble.o: ../../include/vbuf.h smtp_trouble.o: ../../include/vstream.h smtp_trouble.o: ../../include/vstring.h +smtp_trouble.o: ../../include/wrap_netdb.h smtp_trouble.o: smtp.h smtp_trouble.o: smtp_sasl.h smtp_trouble.o: smtp_trouble.c @@ -875,5 +892,6 @@ smtp_unalias.o: ../../include/tok822.h smtp_unalias.o: ../../include/vbuf.h smtp_unalias.o: ../../include/vstream.h smtp_unalias.o: ../../include/vstring.h +smtp_unalias.o: ../../include/wrap_netdb.h smtp_unalias.o: smtp.h smtp_unalias.o: smtp_unalias.c diff --git a/postfix/src/smtp/smtp_addr.c b/postfix/src/smtp/smtp_addr.c index 2b5c126e5..8fd051389 100644 --- a/postfix/src/smtp/smtp_addr.c +++ b/postfix/src/smtp/smtp_addr.c @@ -22,7 +22,7 @@ /* lookups are done via the Internet domain name service (DNS). /* A reasonable number of CNAME indirections is permitted. When /* DNS lookups are disabled, host address lookup is done with -/* getnameinfo() or gethostbyname(). +/* myaddrinfo(3). /* /* smtp_domain_addr() looks up the network addresses for mail /* exchanger hosts listed for the named domain. Addresses are diff --git a/postfix/src/smtpd/Makefile.in b/postfix/src/smtpd/Makefile.in index 8c4132a30..f7d7bbb6a 100644 --- a/postfix/src/smtpd/Makefile.in +++ b/postfix/src/smtpd/Makefile.in @@ -252,6 +252,7 @@ smtpd.o: ../../include/vstream.h smtpd.o: ../../include/vstring.h smtpd.o: ../../include/vstring_vstream.h smtpd.o: ../../include/watchdog.h +smtpd.o: ../../include/wrap_netdb.h smtpd.o: ../../include/xtext.h smtpd.o: smtpd.c smtpd.o: smtpd.h @@ -303,6 +304,7 @@ smtpd_chat.o: ../../include/tls.h smtpd_chat.o: ../../include/vbuf.h smtpd_chat.o: ../../include/vstream.h smtpd_chat.o: ../../include/vstring.h +smtpd_chat.o: ../../include/wrap_netdb.h smtpd_chat.o: smtpd.h smtpd_chat.o: smtpd_chat.c smtpd_chat.o: smtpd_chat.h @@ -377,6 +379,7 @@ smtpd_check.o: ../../include/vbuf.h smtpd_check.o: ../../include/verify_clnt.h smtpd_check.o: ../../include/vstream.h smtpd_check.o: ../../include/vstring.h +smtpd_check.o: ../../include/wrap_netdb.h smtpd_check.o: ../../include/xtext.h smtpd_check.o: smtpd.h smtpd_check.o: smtpd_check.c @@ -414,6 +417,7 @@ smtpd_expand.o: ../../include/tls.h smtpd_expand.o: ../../include/vbuf.h smtpd_expand.o: ../../include/vstream.h smtpd_expand.o: ../../include/vstring.h +smtpd_expand.o: ../../include/wrap_netdb.h smtpd_expand.o: smtpd.h smtpd_expand.o: smtpd_expand.c smtpd_expand.o: smtpd_expand.h @@ -443,6 +447,7 @@ smtpd_haproxy.o: ../../include/valid_mailhost_addr.h smtpd_haproxy.o: ../../include/vbuf.h smtpd_haproxy.o: ../../include/vstream.h smtpd_haproxy.o: ../../include/vstring.h +smtpd_haproxy.o: ../../include/wrap_netdb.h smtpd_haproxy.o: smtpd.h smtpd_haproxy.o: smtpd_haproxy.c smtpd_milter.o: ../../include/argv.h @@ -469,6 +474,7 @@ smtpd_milter.o: ../../include/tls.h smtpd_milter.o: ../../include/vbuf.h smtpd_milter.o: ../../include/vstream.h smtpd_milter.o: ../../include/vstring.h +smtpd_milter.o: ../../include/wrap_netdb.h smtpd_milter.o: smtpd.h smtpd_milter.o: smtpd_milter.c smtpd_milter.o: smtpd_milter.h @@ -502,6 +508,7 @@ smtpd_peer.o: ../../include/valid_mailhost_addr.h smtpd_peer.o: ../../include/vbuf.h smtpd_peer.o: ../../include/vstream.h smtpd_peer.o: ../../include/vstring.h +smtpd_peer.o: ../../include/wrap_netdb.h smtpd_peer.o: smtpd.h smtpd_peer.o: smtpd_peer.c smtpd_proxy.o: ../../include/argv.h @@ -534,6 +541,7 @@ smtpd_proxy.o: ../../include/tls.h smtpd_proxy.o: ../../include/vbuf.h smtpd_proxy.o: ../../include/vstream.h smtpd_proxy.o: ../../include/vstring.h +smtpd_proxy.o: ../../include/wrap_netdb.h smtpd_proxy.o: ../../include/xtext.h smtpd_proxy.o: smtpd.h smtpd_proxy.o: smtpd_proxy.c @@ -581,6 +589,7 @@ smtpd_sasl_glue.o: ../../include/tls.h smtpd_sasl_glue.o: ../../include/vbuf.h smtpd_sasl_glue.o: ../../include/vstream.h smtpd_sasl_glue.o: ../../include/vstring.h +smtpd_sasl_glue.o: ../../include/wrap_netdb.h smtpd_sasl_glue.o: ../../include/xsasl.h smtpd_sasl_glue.o: smtpd.h smtpd_sasl_glue.o: smtpd_chat.h @@ -611,6 +620,7 @@ smtpd_sasl_proto.o: ../../include/tls.h smtpd_sasl_proto.o: ../../include/vbuf.h smtpd_sasl_proto.o: ../../include/vstream.h smtpd_sasl_proto.o: ../../include/vstring.h +smtpd_sasl_proto.o: ../../include/wrap_netdb.h smtpd_sasl_proto.o: smtpd.h smtpd_sasl_proto.o: smtpd_chat.h smtpd_sasl_proto.o: smtpd_sasl_glue.h @@ -642,6 +652,7 @@ smtpd_state.o: ../../include/tls.h smtpd_state.o: ../../include/vbuf.h smtpd_state.o: ../../include/vstream.h smtpd_state.o: ../../include/vstring.h +smtpd_state.o: ../../include/wrap_netdb.h smtpd_state.o: smtpd.h smtpd_state.o: smtpd_chat.h smtpd_state.o: smtpd_sasl_glue.h @@ -675,5 +686,6 @@ smtpd_xforward.o: ../../include/tls.h smtpd_xforward.o: ../../include/vbuf.h smtpd_xforward.o: ../../include/vstream.h smtpd_xforward.o: ../../include/vstring.h +smtpd_xforward.o: ../../include/wrap_netdb.h smtpd_xforward.o: smtpd.h smtpd_xforward.o: smtpd_xforward.c diff --git a/postfix/src/smtpstone/Makefile.in b/postfix/src/smtpstone/Makefile.in index f86e0195b..ad1d7deea 100644 --- a/postfix/src/smtpstone/Makefile.in +++ b/postfix/src/smtpstone/Makefile.in @@ -119,6 +119,7 @@ qmqp-source.o: ../../include/valid_mailhost_addr.h qmqp-source.o: ../../include/vbuf.h qmqp-source.o: ../../include/vstream.h qmqp-source.o: ../../include/vstring.h +qmqp-source.o: ../../include/wrap_netdb.h qmqp-source.o: qmqp-source.c smtp-sink.o: ../../include/check_arg.h smtp-sink.o: ../../include/chroot_uid.h @@ -144,6 +145,7 @@ smtp-sink.o: ../../include/vbuf.h smtp-sink.o: ../../include/vstream.h smtp-sink.o: ../../include/vstring.h smtp-sink.o: ../../include/vstring_vstream.h +smtp-sink.o: ../../include/wrap_netdb.h smtp-sink.o: smtp-sink.c smtp-source.o: ../../include/check_arg.h smtp-source.o: ../../include/compat_va_copy.h @@ -169,4 +171,5 @@ smtp-source.o: ../../include/vbuf.h smtp-source.o: ../../include/vstream.h smtp-source.o: ../../include/vstring.h smtp-source.o: ../../include/vstring_vstream.h +smtp-source.o: ../../include/wrap_netdb.h smtp-source.o: smtp-source.c diff --git a/postfix/src/testing/.indent.pro b/postfix/src/testing/.indent.pro new file mode 120000 index 000000000..5c837eca6 --- /dev/null +++ b/postfix/src/testing/.indent.pro @@ -0,0 +1 @@ +../../.indent.pro \ No newline at end of file diff --git a/postfix/src/testing/Makefile.in b/postfix/src/testing/Makefile.in new file mode 100644 index 000000000..6a6671200 --- /dev/null +++ b/postfix/src/testing/Makefile.in @@ -0,0 +1,306 @@ +SHELL = /bin/sh +SRCS = mock_myaddrinfo.c mock_dns_lookup.c mock_servent.c \ + mock_getaddrinfo.c match_basic.c match_addr.c make_addr.c \ + addrinfo_to_string.c +LIB_OBJ = match_basic.o match_addr.o make_addr.o addrinfo_to_string.o +MOCK_OBJ= mock_myaddrinfo.o mock_dns_lookup.o mock_servent.o mock_getaddrinfo.o +TEST_OBJ = mock_dns_lookup_test.o mock_getaddrinfo_test.o \ + mock_myaddrinfo_test.o mock_servent_test.o match_addr_test.o +HDRS = mock_myaddrinfo.h mock_dns.h mock_servent.h mock_getaddrinfo.h \ + match_basic.h match_addr.h make_addr.h addrinfo_to_string.h +TESTSRC = +DEFS = -I. -I$(INC_DIR) -D$(SYSTYPE) +CFLAGS = $(DEBUG) $(OPT) $(DEFS) +INCL = +LIB = libtesting.a +TESTPROG= mock_dns_lookup_test mock_getaddrinfo_test \ + mock_myaddrinfo_test mock_servent_test match_addr_test + +LIBS = ../../lib/libptest.a \ + ../../lib/lib$(LIB_PREFIX)dns$(LIB_SUFFIX) \ + ../../lib/lib$(LIB_PREFIX)global$(LIB_SUFFIX) \ + ../../lib/lib$(LIB_PREFIX)util$(LIB_SUFFIX) +LIB_DIR = ../../lib +INC_DIR = ../../include +MAKES = + +.c.o:; $(CC) $(SHLIB_CFLAGS) $(CFLAGS) -c $*.c + +all: $(LIB_OBJ) $(MOCK_OBJ) + +$(LIB_OBJ) $(MOCK_OBJ) $(TEST_OBJ): ../../conf/makedefs.out + +Makefile: Makefile.in + cat ../../conf/makedefs.out $? >$@ + +test: $(TESTPROG) + +$(LIB): $(LIB_OBJ) + $(_AR) $(ARFL) $(LIB) $? + $(_RANLIB) $(LIB) + +$(LIB_DIR)/$(LIB): $(LIB) + cp $(LIB) $(LIB_DIR) + $(_RANLIB) $(LIB_DIR)/$(LIB) + +update: $(LIB_DIR)/$(LIB) $(HDRS) $(MOCK_OBJ) + -for i in $(HDRS); \ + do \ + cmp -s $$i $(INC_DIR)/$$i 2>/dev/null || cp $$i $(INC_DIR); \ + done + (cd $(INC_DIR); chmod 644 $(HDRS)) + -for i in $(MOCK_OBJ); \ + do \ + cmp -s $$i $(LIB_DIR)/$$i 2>/dev/null || cp $$i $(LIB_DIR); \ + done + +clean: + rm -f *.o $(LIB) *core $(TESTPROG) junk $(MAKES) *.tmp + +tidy: clean + +tests: test_mock_dns_lookup test_mock_getaddrinfo test_mock_myaddrinfo \ + test_mock_servent test_match_addr + +mock_myaddrinfo_test: mock_myaddrinfo_test.o mock_myaddrinfo.o $(LIB) $(LIBS) + $(CC) $(CFLAGS) -o $@ $@.o mock_myaddrinfo.o $(LIB) $(LIBS) $(SYSLIBS) + +test_mock_myaddrinfo: update mock_myaddrinfo_test + $(SHLIB_ENV) ${VALGRIND} ./mock_myaddrinfo_test + +mock_dns_lookup_test: mock_dns_lookup_test.o mock_dns_lookup.o $(LIB) $(LIBS) + $(CC) $(CFLAGS) -o $@ $@.o mock_dns_lookup.o $(LIB) $(LIBS) $(SYSLIBS) + +test_mock_dns_lookup: update mock_dns_lookup_test + $(SHLIB_ENV) ${VALGRIND} ./mock_dns_lookup_test + +mock_servent_test: mock_servent_test.o mock_servent.o $(LIB) $(LIBS) + $(CC) $(CFLAGS) -o $@ $@.o mock_servent.o $(LIB) $(LIBS) $(SYSLIBS) + +test_mock_servent: update mock_servent_test + $(SHLIB_ENV) ${VALGRIND} ./mock_servent_test + +mock_getaddrinfo_test: mock_getaddrinfo_test.o mock_getaddrinfo.o $(LIB) $(LIBS) + $(CC) $(CFLAGS) -o $@ $@.o mock_getaddrinfo.o $(LIB) $(LIBS) $(SYSLIBS) + +test_mock_getaddrinfo: update mock_getaddrinfo_test + $(SHLIB_ENV) ${VALGRIND} ./mock_getaddrinfo_test + +match_addr_test: match_addr_test.o match_addr.o $(LIB) $(LIBS) + $(CC) $(CFLAGS) -o $@ $@.o match_addr.o $(LIB) $(LIBS) $(SYSLIBS) + +test_match_addr: update match_addr_test + $(SHLIB_ENV) ${VALGRIND} ./match_addr_test + +depend: $(MAKES) + (sed '1,/^# do not edit/!d' Makefile.in; \ + set -e; for i in [a-z][a-z0-9]*.c; do \ + $(CC) -E $(DEFS) $(INCL) $$i | grep -v '[<>]' | sed -n -e '/^# *1 *"\([^"]*\)".*/{' \ + -e 's//'`echo $$i|sed 's/c$$/o/'`': \1/' \ + -e 's/o: \.\//o: /' -e p -e '}' ; \ + done | LANG=C sort -u) | grep -v '[.][o][:][ ][/]' >$$$$ && mv $$$$ Makefile.in + @$(EXPORT) make -f Makefile.in Makefile 1>&2 + +# do not edit below this line - it is generated by 'make depend' +addrinfo_to_string.o: ../../include/check_arg.h +addrinfo_to_string.o: ../../include/msg.h +addrinfo_to_string.o: ../../include/myaddrinfo.h +addrinfo_to_string.o: ../../include/name_code.h +addrinfo_to_string.o: ../../include/name_mask.h +addrinfo_to_string.o: ../../include/sys_defs.h +addrinfo_to_string.o: ../../include/vbuf.h +addrinfo_to_string.o: ../../include/vstring.h +addrinfo_to_string.o: ../../include/wrap_netdb.h +addrinfo_to_string.o: addrinfo_to_string.c +addrinfo_to_string.o: addrinfo_to_string.h +make_addr.o: ../../include/msg.h +make_addr.o: ../../include/mymalloc.h +make_addr.o: ../../include/sys_defs.h +make_addr.o: ../../include/wrap_netdb.h +make_addr.o: make_addr.c +make_addr.o: make_addr.h +match_addr.o: ../../include/argv.h +match_addr.o: ../../include/check_arg.h +match_addr.o: ../../include/msg.h +match_addr.o: ../../include/ptest.h +match_addr.o: ../../include/sys_defs.h +match_addr.o: ../../include/vbuf.h +match_addr.o: ../../include/vstream.h +match_addr.o: ../../include/vstring.h +match_addr.o: ../../include/wrap_netdb.h +match_addr.o: addrinfo_to_string.h +match_addr.o: match_addr.c +match_addr.o: match_addr.h +match_addr.o: match_basic.h +match_addr_test.o: ../../include/argv.h +match_addr_test.o: ../../include/check_arg.h +match_addr_test.o: ../../include/msg.h +match_addr_test.o: ../../include/msg_output.h +match_addr_test.o: ../../include/msg_vstream.h +match_addr_test.o: ../../include/pmock_expect.h +match_addr_test.o: ../../include/ptest.h +match_addr_test.o: ../../include/ptest_main.h +match_addr_test.o: ../../include/stringops.h +match_addr_test.o: ../../include/sys_defs.h +match_addr_test.o: ../../include/vbuf.h +match_addr_test.o: ../../include/vstream.h +match_addr_test.o: ../../include/vstring.h +match_addr_test.o: ../../include/wrap_netdb.h +match_addr_test.o: make_addr.h +match_addr_test.o: match_addr.h +match_addr_test.o: match_addr_test.c +match_basic.o: ../../include/argv.h +match_basic.o: ../../include/check_arg.h +match_basic.o: ../../include/msg.h +match_basic.o: ../../include/ptest.h +match_basic.o: ../../include/sys_defs.h +match_basic.o: ../../include/vbuf.h +match_basic.o: ../../include/vstream.h +match_basic.o: ../../include/vstring.h +match_basic.o: match_basic.c +match_basic.o: match_basic.h +mock_dns_lookup.o: ../../include/argv.h +mock_dns_lookup.o: ../../include/check_arg.h +mock_dns_lookup.o: ../../include/dns.h +mock_dns_lookup.o: ../../include/hex_code.h +mock_dns_lookup.o: ../../include/msg.h +mock_dns_lookup.o: ../../include/myaddrinfo.h +mock_dns_lookup.o: ../../include/mymalloc.h +mock_dns_lookup.o: ../../include/name_code.h +mock_dns_lookup.o: ../../include/pmock_expect.h +mock_dns_lookup.o: ../../include/ptest.h +mock_dns_lookup.o: ../../include/sock_addr.h +mock_dns_lookup.o: ../../include/stringops.h +mock_dns_lookup.o: ../../include/sys_defs.h +mock_dns_lookup.o: ../../include/vbuf.h +mock_dns_lookup.o: ../../include/vstream.h +mock_dns_lookup.o: ../../include/vstring.h +mock_dns_lookup.o: ../../include/wrap_netdb.h +mock_dns_lookup.o: mock_dns.h +mock_dns_lookup.o: mock_dns_lookup.c +mock_dns_lookup_test.o: ../../include/argv.h +mock_dns_lookup_test.o: ../../include/check_arg.h +mock_dns_lookup_test.o: ../../include/dns.h +mock_dns_lookup_test.o: ../../include/msg.h +mock_dns_lookup_test.o: ../../include/msg_output.h +mock_dns_lookup_test.o: ../../include/msg_vstream.h +mock_dns_lookup_test.o: ../../include/myaddrinfo.h +mock_dns_lookup_test.o: ../../include/pmock_expect.h +mock_dns_lookup_test.o: ../../include/ptest.h +mock_dns_lookup_test.o: ../../include/ptest_main.h +mock_dns_lookup_test.o: ../../include/sock_addr.h +mock_dns_lookup_test.o: ../../include/stringops.h +mock_dns_lookup_test.o: ../../include/sys_defs.h +mock_dns_lookup_test.o: ../../include/vbuf.h +mock_dns_lookup_test.o: ../../include/vstream.h +mock_dns_lookup_test.o: ../../include/vstring.h +mock_dns_lookup_test.o: ../../include/wrap_netdb.h +mock_dns_lookup_test.o: mock_dns.h +mock_dns_lookup_test.o: mock_dns_lookup_test.c +mock_getaddrinfo.o: ../../include/argv.h +mock_getaddrinfo.o: ../../include/check_arg.h +mock_getaddrinfo.o: ../../include/msg.h +mock_getaddrinfo.o: ../../include/myaddrinfo.h +mock_getaddrinfo.o: ../../include/mymalloc.h +mock_getaddrinfo.o: ../../include/pmock_expect.h +mock_getaddrinfo.o: ../../include/ptest.h +mock_getaddrinfo.o: ../../include/sys_defs.h +mock_getaddrinfo.o: ../../include/vbuf.h +mock_getaddrinfo.o: ../../include/vstream.h +mock_getaddrinfo.o: ../../include/vstring.h +mock_getaddrinfo.o: ../../include/wrap_netdb.h +mock_getaddrinfo.o: addrinfo_to_string.h +mock_getaddrinfo.o: make_addr.h +mock_getaddrinfo.o: match_addr.h +mock_getaddrinfo.o: match_basic.h +mock_getaddrinfo.o: mock_getaddrinfo.c +mock_getaddrinfo.o: mock_getaddrinfo.h +mock_getaddrinfo_test.o: ../../include/argv.h +mock_getaddrinfo_test.o: ../../include/check_arg.h +mock_getaddrinfo_test.o: ../../include/msg.h +mock_getaddrinfo_test.o: ../../include/msg_output.h +mock_getaddrinfo_test.o: ../../include/msg_vstream.h +mock_getaddrinfo_test.o: ../../include/myaddrinfo.h +mock_getaddrinfo_test.o: ../../include/pmock_expect.h +mock_getaddrinfo_test.o: ../../include/ptest.h +mock_getaddrinfo_test.o: ../../include/ptest_main.h +mock_getaddrinfo_test.o: ../../include/stringops.h +mock_getaddrinfo_test.o: ../../include/sys_defs.h +mock_getaddrinfo_test.o: ../../include/vbuf.h +mock_getaddrinfo_test.o: ../../include/vstream.h +mock_getaddrinfo_test.o: ../../include/vstring.h +mock_getaddrinfo_test.o: ../../include/wrap_netdb.h +mock_getaddrinfo_test.o: addrinfo_to_string.h +mock_getaddrinfo_test.o: make_addr.h +mock_getaddrinfo_test.o: match_addr.h +mock_getaddrinfo_test.o: match_basic.h +mock_getaddrinfo_test.o: mock_getaddrinfo.h +mock_getaddrinfo_test.o: mock_getaddrinfo_test.c +mock_myaddrinfo.o: ../../include/argv.h +mock_myaddrinfo.o: ../../include/check_arg.h +mock_myaddrinfo.o: ../../include/msg.h +mock_myaddrinfo.o: ../../include/myaddrinfo.h +mock_myaddrinfo.o: ../../include/mymalloc.h +mock_myaddrinfo.o: ../../include/pmock_expect.h +mock_myaddrinfo.o: ../../include/ptest.h +mock_myaddrinfo.o: ../../include/sys_defs.h +mock_myaddrinfo.o: ../../include/vbuf.h +mock_myaddrinfo.o: ../../include/vstream.h +mock_myaddrinfo.o: ../../include/vstring.h +mock_myaddrinfo.o: ../../include/wrap_netdb.h +mock_myaddrinfo.o: addrinfo_to_string.h +mock_myaddrinfo.o: make_addr.h +mock_myaddrinfo.o: match_addr.h +mock_myaddrinfo.o: match_basic.h +mock_myaddrinfo.o: mock_myaddrinfo.c +mock_myaddrinfo.o: mock_myaddrinfo.h +mock_myaddrinfo_test.o: ../../include/argv.h +mock_myaddrinfo_test.o: ../../include/check_arg.h +mock_myaddrinfo_test.o: ../../include/msg.h +mock_myaddrinfo_test.o: ../../include/msg_output.h +mock_myaddrinfo_test.o: ../../include/msg_vstream.h +mock_myaddrinfo_test.o: ../../include/myaddrinfo.h +mock_myaddrinfo_test.o: ../../include/pmock_expect.h +mock_myaddrinfo_test.o: ../../include/ptest.h +mock_myaddrinfo_test.o: ../../include/ptest_main.h +mock_myaddrinfo_test.o: ../../include/stringops.h +mock_myaddrinfo_test.o: ../../include/sys_defs.h +mock_myaddrinfo_test.o: ../../include/vbuf.h +mock_myaddrinfo_test.o: ../../include/vstream.h +mock_myaddrinfo_test.o: ../../include/vstring.h +mock_myaddrinfo_test.o: ../../include/wrap_netdb.h +mock_myaddrinfo_test.o: addrinfo_to_string.h +mock_myaddrinfo_test.o: make_addr.h +mock_myaddrinfo_test.o: match_addr.h +mock_myaddrinfo_test.o: match_basic.h +mock_myaddrinfo_test.o: mock_myaddrinfo.h +mock_myaddrinfo_test.o: mock_myaddrinfo_test.c +mock_servent.o: ../../include/argv.h +mock_servent.o: ../../include/check_arg.h +mock_servent.o: ../../include/msg.h +mock_servent.o: ../../include/mymalloc.h +mock_servent.o: ../../include/pmock_expect.h +mock_servent.o: ../../include/ptest.h +mock_servent.o: ../../include/sys_defs.h +mock_servent.o: ../../include/vbuf.h +mock_servent.o: ../../include/vstream.h +mock_servent.o: ../../include/vstring.h +mock_servent.o: ../../include/wrap_netdb.h +mock_servent.o: mock_servent.c +mock_servent.o: mock_servent.h +mock_servent_test.o: ../../include/argv.h +mock_servent_test.o: ../../include/check_arg.h +mock_servent_test.o: ../../include/msg.h +mock_servent_test.o: ../../include/msg_output.h +mock_servent_test.o: ../../include/msg_vstream.h +mock_servent_test.o: ../../include/pmock_expect.h +mock_servent_test.o: ../../include/ptest.h +mock_servent_test.o: ../../include/ptest_main.h +mock_servent_test.o: ../../include/stringops.h +mock_servent_test.o: ../../include/sys_defs.h +mock_servent_test.o: ../../include/vbuf.h +mock_servent_test.o: ../../include/vstream.h +mock_servent_test.o: ../../include/vstring.h +mock_servent_test.o: ../../include/wrap_netdb.h +mock_servent_test.o: mock_servent.h +mock_servent_test.o: mock_servent_test.c diff --git a/postfix/src/testing/addrinfo_to_string.c b/postfix/src/testing/addrinfo_to_string.c new file mode 100644 index 000000000..c08d75a19 --- /dev/null +++ b/postfix/src/testing/addrinfo_to_string.c @@ -0,0 +1,304 @@ +/*++ +/* NAME +/* addrinfo_to_string 3 +/* SUMMARY +/* address info to string conversion +/* SYNOPSIS +/* #include +/* +/* const char *pf_to_string(int); +/* +/* const char *af_to_string(int); +/* +/* const char *socktype_to_string(int); +/* +/* const char *ipprotocol_to_string(int); +/* +/* const char *ai_flags_to_string( +/* VSTRING *buf, +/* int flags); +/* +/* const char *ni_flags_to_string( +/* VSTRING *buf, +/* int flags); +/* char *append_addrinfo_to_string( +/* VSTRING *buf, +/* const struct addrinfo *ai) +/* +/* char *addrinfo_hints_to_string( +/* VSTRING *buf, +/* const struct addrinfo *hints) +/* +/* char *sockaddr_to_string( +/* VSTRING *buf, +/* const struct sockaddr *sa, +/* size_t sa_len); +/* DESCRIPTION +/* The functions in this module convert address information +/* to textual form, for use in test error messages. They +/* implement only the necessary subsets. +/* +/* pf_to_string(), af_to_string(), socktype_to_string, +/* ipprotocol_to_string, ai_flags_to_string(), ni_flags_to_string() +/* produce a textual representation of addrinfo properties or +/* getnameinfo() flags. +/* +/* append_addrinfo_to_string() appends a textual representation +/* of the referenced addrinfo object (only one) to the specified +/* buffer. +/* +/* addrinfo_hints_to_string() writes a textual representation of +/* the referenced getaddrinfo() hints object. +/* +/* sockaddr_to_string() writes a textual representation of the +/* referenced sockaddr object. +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* Google, Inc. +/* 111 8th Avenue +/* New York, NY 10011, USA +/*--*/ + + /* + * System library. + */ +#include +#include +#include +#include +#include +#include +#include /* sprintf/snprintf */ + + /* + * Utility library. + */ +#include +#include +#include +#include + + /* + * Test library. + */ +#include + +#define STR_OR_NULL(s) ((s) ? (s) : "(null)") + +#ifdef NO_SNPRINTF +#define MY_SNPRINTF(bp, sz, fmt, ...) \ + sprintf((bp), (fmt), __VA_ARGS__) +#else +#define MY_SNPRINTF(bp, sz, fmt, ...) \ + snprintf((bp), (sz), (fmt), __VA_ARGS__) +#endif + +#define STR vstring_str + +/* pf_to_string - convert protocol family to human-readable form */ + +const char *pf_to_string(int af) +{ + static const NAME_CODE pf_codes[] = { + "PF_INET", PF_INET, + "PF_INET6", PF_INET6, + 0, + }; + const char *name; + + name = str_name_code(pf_codes, af); + return (name ? name : "unknown-protocol-family"); +} + +/* af_to_string - convert address family to human-readable form */ + +const char *af_to_string(int af) +{ + static const NAME_CODE af_codes[] = { + "AF_INET", AF_INET, + "AF_INET6", AF_INET6, + 0, + }; + const char *name; + + name = str_name_code(af_codes, af); + return (name ? name : "unknown-address-family"); +} + +/* socktype_to_string - convert socket type to human-readable form */ + +const char *socktype_to_string(int socktype) +{ + static const NAME_CODE socktypes[] = { + "SOCK_STREAM", SOCK_STREAM, + "SOCK_DGRAM", SOCK_DGRAM, + "SOCK_RAW", SOCK_RAW, + "0", 0, + 0, + }; + const char *name; + + name = str_name_code(socktypes, socktype); + return (name ? name : "unknown-socket-type"); +} + +/* ipprotocol_to_string - convert protocol to human-readable form */ + +const char *ipprotocol_to_string(int proto) +{ + static const NAME_CODE protocols[] = { + "IPPROTO_UDP", IPPROTO_UDP, + "IPPROTO_TCP", IPPROTO_TCP, + "0", 0, + 0, + }; + const char *name; + + name = str_name_code(protocols, proto); + return (name ? name : "unknown-protocol"); +} + +/* ai_flags_to_string - convert addrinfo flags to human-readable form */ + +const char *ai_flags_to_string(VSTRING *buf, int flags) +{ + static const NAME_MASK ai_flags[] = { +#ifdef AI_IDN + "AI_IDN", AI_IDN, +#endif +#ifdef AI_CANONIDN + "AI_CANONIDN", AI_CANONIDN, +#endif +#ifdef AI_IDN_ALLOW_UNASSIGNED + "AI_IDN_ALLOW_UNASSIGNED", AI_IDN_ALLOW_UNASSIGNED, +#endif +#ifdef AI_IDN_USE_STD3_ASCII_RULES + "AI_IDN_USE_STD3_ASCII_RULES", AI_IDN_USE_STD3_ASCII_RULES, +#endif + "AI_ADDRCONFIG", AI_ADDRCONFIG, + "AI_CANONNAME", AI_CANONNAME, + "AI_NUMERICHOST", AI_NUMERICHOST, + "AI_NUMERICSERV", AI_NUMERICSERV, + "AI_PASSIVE", AI_PASSIVE, + 0, + }; + + return (str_name_mask_opt(buf, "ai_flags_to_string", ai_flags, flags, + NAME_MASK_NUMBER | NAME_MASK_PIPE | NAME_MASK_NULL)); +} + +/* ni_flags_to_string - convert getnameinfo flags to human-readable form */ + +const char *ni_flags_to_string(VSTRING *buf, int flags) +{ + static const NAME_MASK ni_flags[] = { + "NI_NAMEREQD", NI_NAMEREQD, + "NI_DGRAM", NI_DGRAM, + "NI_NOFQDN", NI_NOFQDN, + "NI_NUMERICHOST", NI_NUMERICHOST, + "NI_NUMERICSERV", NI_NUMERICSERV, + 0, + }; + + return (str_name_mask_opt(buf, "ni_flags_to_string", ni_flags, flags, + NAME_MASK_NUMBER | NAME_MASK_PIPE | NAME_MASK_NULL)); +} + +/* append_addrinfo_to_string - print human-readable addrinfo */ + +char *append_addrinfo_to_string(VSTRING *buf, const struct addrinfo *ai) +{ + if (ai == 0) { + vstring_sprintf_append(buf, "(null)"); + } else { + VSTRING *sockaddr_buf = vstring_alloc(100); + VSTRING *flags_buf = vstring_alloc(100); + + vstring_sprintf_append(buf, "{%s, %s, %s, %s, %d, %s, %s}", + ai_flags_to_string(flags_buf, ai->ai_flags), + pf_to_string(ai->ai_family), + socktype_to_string(ai->ai_socktype), + ipprotocol_to_string(ai->ai_protocol), + ai->ai_addrlen, + sockaddr_to_string(sockaddr_buf, ai->ai_addr, + ai->ai_addrlen), + STR_OR_NULL(ai->ai_canonname)); + vstring_free(sockaddr_buf); + vstring_free(flags_buf); + } + return (STR(buf)); +} + +/* addrinfo_hints_to_string - append human-readable hints */ + +char *addrinfo_hints_to_string(VSTRING *buf, const struct addrinfo *ai) +{ + if (ai == 0) { + vstring_sprintf(buf, "(null)"); + } else { + VSTRING *flags_buf = vstring_alloc(100); + + vstring_sprintf(buf, "{%s, %s, %s, %s}", + ai_flags_to_string(flags_buf, ai->ai_flags), + pf_to_string(ai->ai_family), + socktype_to_string(ai->ai_socktype), + ipprotocol_to_string(ai->ai_protocol)); + vstring_free(flags_buf); + } + return (STR(buf)); +} + +/* sockaddr_to_strings - convert to printable host address and port */ + +static void sockaddr_to_strings(const struct sockaddr *sa, size_t salen, + MAI_HOSTADDR_STR *hostaddr, + MAI_SERVPORT_STR *portnum) +{ + if (sa->sa_family == AF_INET) { + struct sockaddr_in *sin = (struct sockaddr_in *) sa; + + if (salen < sizeof(*sin)) + msg_fatal("sockaddr_to_strings: bad sockaddr length %ld", + (long) salen); + MY_SNPRINTF(portnum->buf, sizeof(*portnum), "%d", ntohs(sin->sin_port)); + if (inet_ntop(AF_INET, &sin->sin_addr, hostaddr->buf, + sizeof(*hostaddr)) == 0) + msg_fatal("inet_ntop(AF_INET,...): %m"); +#ifdef HAS_IPV6 + } else if (sa->sa_family == AF_INET6) { + struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *) sa; + + if (salen < sizeof(*sin6)) + msg_fatal("sockaddr_to_strings: bad sockaddr length %ld", + (long) salen); + MY_SNPRINTF(portnum->buf, sizeof(*portnum), "%d", + ntohs(sin6->sin6_port)); + if (inet_ntop(AF_INET6, &sin6->sin6_addr, hostaddr->buf, + sizeof(*hostaddr)) == 0) + msg_fatal("inet_ntop(AF_INET,...): %m"); +#endif + } else { + errno = EAFNOSUPPORT; + msg_panic("sockaddr_to_strings: protocol familiy %d: %m", + sa->sa_family); + } +} + +/* sockaddr_to_string - render human-readable sockaddr */ + +char *sockaddr_to_string(VSTRING *buf, const struct sockaddr *sa, + size_t salen) +{ + MAI_HOSTADDR_STR hostaddr; + MAI_SERVPORT_STR portnum; + + sockaddr_to_strings(sa, salen, &hostaddr, &portnum); + vstring_sprintf(buf, "{%s, %s, %s}", + af_to_string(sa->sa_family), + hostaddr.buf, portnum.buf); + return (STR(buf)); +} diff --git a/postfix/src/testing/addrinfo_to_string.h b/postfix/src/testing/addrinfo_to_string.h new file mode 100644 index 000000000..758cc2ddf --- /dev/null +++ b/postfix/src/testing/addrinfo_to_string.h @@ -0,0 +1,49 @@ +#ifndef _ADDRINFO_TO_STRING_H_INCLUDED_ +#define _ADDRINFO_TO_STRING_H_INCLUDED_ + +/*++ +/* NAME +/* addrinfo_to_string 3h +/* SUMMARY +/* address info to string conversion +/* SYNOPSIS +/* #include +/* DESCRIPTION +/* .nf + + /* + * System library. + */ +#include +#include + + /* + * Utility library. + */ +#include + + /* + * External interface. + */ +extern const char *pf_to_string(int); +extern const char *af_to_string(int); +extern const char *socktype_to_string(int); +extern const char *ipprotocol_to_string(int); +extern const char *ai_flags_to_string(VSTRING *, int); +extern const char *ni_flags_to_string(VSTRING *, int); +extern char *append_addrinfo_to_string(VSTRING *, const struct addrinfo *); +extern char *addrinfo_hints_to_string(VSTRING *, const struct addrinfo *); +extern char *sockaddr_to_string(VSTRING *, const struct sockaddr *, size_t); + +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* Google, Inc. +/* 111 8th Avenue +/* New York, NY 10011, USA +/*--*/ + +#endif diff --git a/postfix/src/testing/make_addr.c b/postfix/src/testing/make_addr.c new file mode 100644 index 000000000..8b6fcfe21 --- /dev/null +++ b/postfix/src/testing/make_addr.c @@ -0,0 +1,208 @@ +/*++ +/* NAME +/* make_addr 3 +/* SUMMARY +/* make_addrinfo(), freeaddrinfo(), make_sockaddr() for hermetic tests +/* SYNOPSIS +/* #include +/* +/* struct addrinfo *make_addrinfo( +/* const struct addrinfo *hints, +/* const char *name, +/* const char *addr, +/* int port) +/* +/* struct addrinfo *copy_addrinfo(const struct addrinfo *ai) +/* +/* void freeaddrinfo(struct addrinfo *ai) +/* +/* struct sockaddr *make_sockaddr( +/* int family, +/* const char *addr, +/* int port) +/* +/* void free_sockaddr(struct sockaddr *sa) +/* DESCRIPTION +/* This module contains helper functions to set up mock +/* expectations. +/* +/* make_addrinfo() creates one addrinfo structure. The hints +/* argument must specify the protocol family for the addr +/* argument (i.e. not PF_UNSPEC). To create a linked list, +/* manually append make_addrinfo() results. The port is in +/* host byte order. +/* +/* copy_addrinfo() makes a deep copy of a linked list of +/* addrinfo structures. +/* +/* freeaddrinfo() deletes a linked list of addrinfo structures. +/* This function must be used for addrinfo structures created +/* with make_addrinfo() and copy_addrinfo(). +/* +/* make_sockaddr() creates a sockaddr structure from the string +/* representation of an IP address, and a port in host byte +/* order. +/* +/* free_sockaddr() exists to make program code more explicit. +/* DIAGNOSTICS +/* make_sockaddr() and make_addrinfo() terminate with a fatal +/* error when an unknown address family is specified, or when +/* the string representation of an IP address does not match +/* the address family. +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* Google, Inc. +/* 111 8th Avenue +/* New York, NY 10011, USA +/*--*/ + + /* + * System library. + */ +#include +#include +#include +#include +#include +#include +#include + + /* + * Utility library. + */ +#include +#include + + /* + * Test library. + */ +#include + +#define MYSTRDUP_OR_NULL(x) ((x) ? mystrdup(x) : 0) + +/* make_sockaddr - helper to create mock sockaddr instances */ + +struct sockaddr *make_sockaddr(int family, const char *addr, int port) +{ + if (family == AF_INET) { + struct sockaddr_in *sa; + + sa = (struct sockaddr_in *) mymalloc(sizeof(*sa)); + memset(sa, 0, sizeof(*sa)); + switch (inet_pton(AF_INET, addr, (void *) &sa->sin_addr)) { + case 1: + break; + case 0: + msg_fatal("bad address syntax: '%s'", addr); + case -1: + msg_fatal("inet_pton(AF_INET, %s, (ptr)): %m", addr); + } + sa->sin_family = AF_INET; + sa->sin_port = htons(port); + return ((struct sockaddr *) sa); +#ifdef HAS_IPV6 + } else if (family == AF_INET6) { + struct sockaddr_in6 *sa; + + sa = (struct sockaddr_in6 *) mymalloc(sizeof(*sa)); + memset(sa, 0, sizeof(*sa)); + switch (inet_pton(AF_INET6, addr, (void *) &sa->sin6_addr)) { + case 1: + break; + case 0: + msg_fatal("bad address syntax: '%s'", addr); + case -1: + msg_fatal("inet_pton(AF_INET6, %s, (ptr)): %m", addr); + } + sa->sin6_family = AF_INET6; + sa->sin6_port = htons(port); + return ((struct sockaddr *) sa); +#endif + } else { + errno = EAFNOSUPPORT; + msg_panic("make_sockaddr: address familiy %d: %m", family); + } +} + +/* free_sockaddr - destructor for make_sockaddr() and copy_addrinfo() */ + +void free_sockaddr(struct sockaddr *sa) +{ + myfree(sa); +} + +/* copy_addrinfo - expectation helper */ + +struct addrinfo *copy_addrinfo(const struct addrinfo *in) +{ + struct addrinfo *out; + + /* + * First a shallow copy, followed by deep copies for pointer fields. + */ + if (in == 0) + return (0); + out = (struct addrinfo *) mymalloc(sizeof(*out)); + *out = *in; + if (in->ai_addr != 0) { + out->ai_addr = (struct sockaddr *) mymalloc(in->ai_addrlen); + memcpy(out->ai_addr, in->ai_addr, in->ai_addrlen); + } + if (in->ai_canonname != 0) + out->ai_canonname = mystrdup(in->ai_canonname); + if (in->ai_next) + out->ai_next = copy_addrinfo(in->ai_next); + return (out); +} + +/* make_addrinfo - helper to create mock addrinfo input or result */ + +struct addrinfo *make_addrinfo(const struct addrinfo *hints, + const char *name, const char *addr, + int port) +{ + struct addrinfo *out; + + out = (struct addrinfo *) mymalloc(sizeof(*out)); + memset(out, 0, sizeof(*out)); + out->ai_canonname = MYSTRDUP_OR_NULL(name); + switch (hints->ai_family) { + case PF_INET6: + out->ai_addr = make_sockaddr(AF_INET6, addr, port); + out->ai_addrlen = sizeof(struct sockaddr_in6); + out->ai_family = hints->ai_family; + out->ai_socktype = hints->ai_socktype; + out->ai_protocol = hints->ai_protocol; + break; + case PF_INET: + out->ai_addr = make_sockaddr(AF_INET, addr, port); + out->ai_addrlen = sizeof(struct sockaddr_in); + out->ai_family = hints->ai_family; + out->ai_socktype = hints->ai_socktype; + out->ai_protocol = hints->ai_protocol; + break; + default: + msg_fatal("make_addrinfo: hints->ai_family: %d", hints->ai_family); + } + out->ai_next = 0; + return (out); +} + +/* freeaddrinfo - free the mock-generated addrinfo structures */ + +void freeaddrinfo(struct addrinfo *res) +{ + if (res) { + if (res->ai_next) + freeaddrinfo(res->ai_next); + if (res->ai_addr) + myfree(res->ai_addr); + if (res->ai_canonname) + myfree(res->ai_canonname); + myfree(res); + } +} diff --git a/postfix/src/testing/make_addr.h b/postfix/src/testing/make_addr.h new file mode 100644 index 000000000..ebe4a6df8 --- /dev/null +++ b/postfix/src/testing/make_addr.h @@ -0,0 +1,40 @@ +#ifndef _MAKE_ADDR_H_INCLUDED_ +#define _MAKE_ADDR_H_INCLUDED_ + +/*++ +/* NAME +/* make_addr 3h +/* SUMMARY +/* make_addrinfo(), freeaddrinfo(), make_sockaddr() for hermetic tests +/* SYNOPSIS +/* #include +/* DESCRIPTION +/* .nf + + /* + * System library. + */ +#include +#include + + /* + * External interface. + */ +extern struct addrinfo *make_addrinfo(const struct addrinfo *, const char *, + const char *, int); +extern struct addrinfo *copy_addrinfo(const struct addrinfo *); +extern struct sockaddr *make_sockaddr(int, const char *, int); +extern void free_sockaddr(struct sockaddr *); + +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* Google, Inc. +/* 111 8th Avenue +/* New York, NY 10011, USA +/*--*/ + +#endif diff --git a/postfix/src/testing/match_addr.c b/postfix/src/testing/match_addr.c new file mode 100644 index 000000000..bce07b7ef --- /dev/null +++ b/postfix/src/testing/match_addr.c @@ -0,0 +1,142 @@ +/*++ +/* NAME +/* match_addr 3 +/* SUMMARY +/* matchers for network address information +/* SYNOPSIS +/* #include +/* +/* int eq_addrinfo( +/* PTEST_CTX *t, +/* const char *what, +/* struct addrinfo got, +/* struct addrinfo want) +/* +/* int eq_sockaddr(PTEST_CTX *t, +/* const char *what, +/* const struct sockaddr *got, +/* size_t gotlen, +/* const struct sockaddr *want, +/* size_t wantlen) +/* DESCRIPTION +/* The functions described here are safe macros that include +/* call-site information (file name, line number) that may be +/* used in error messages. +/* +/* eq_addrinfo() compares two struct addrinfo linked lists, +/* and eq_sockaddr() compares two struct sockaddr instances. +/* Both functions return whether their arguments contain the +/* same values. If the t argument is not null, both functions +/* will report values that differ with ptest_error()). +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* Google, Inc. +/* 111 8th Avenue +/* New York, NY 10011, USA +/*--*/ + + /* + * System library. + */ +#include +#include +#include +#include +#include + + /* + * Test library. + */ +#include +#include +#include +#include + +#define STR vstring_str + +/* _eq_addrinfo - match struct addrinfo instances */ + +int _eq_addrinfo(const char *file, int line, + PTEST_CTX *t, const char *what, + const struct addrinfo *got, + const struct addrinfo *want) +{ + if (got == 0 && want == 0) + return (1); + if (got == 0 || want == 0) { + if (t) { + VSTRING *buf = vstring_alloc(100); + + ptest_error(t, "%s:%d %s: got %s, want %s", + file, line, what, got ? + append_addrinfo_to_string(buf, got) : "(null)", + want ? + append_addrinfo_to_string(buf, want) : "(null)"); + vstring_free(buf); + } + return (0); + } + return (_eq_flags(file, line, t, "ai_flags", got->ai_flags, want->ai_flags, + ai_flags_to_string) + && _eq_enum(file, line, t, "ai_family", got->ai_family, + want->ai_family, af_to_string) + && _eq_enum(file, line, t, "ai_socktype", got->ai_socktype, + want->ai_socktype, socktype_to_string) + && _eq_enum(file, line, t, "ai_protocol", + got->ai_protocol, want->ai_protocol, + ipprotocol_to_string) + && _eq_size_t(file, line, t, "ai_addrlen", + got->ai_addrlen, want->ai_addrlen) + && _eq_sockaddr(file, line, t, "ai_addr", got->ai_addr, + got->ai_addrlen, want->ai_addr, want->ai_addrlen) + && _eq_addrinfo(file, line, t, what, got->ai_next, + want->ai_next)); +} + +/* _eq_sockaddr - sockaddr matcher */ + +int _eq_sockaddr(const char *file, int line, + PTEST_CTX *t, const char *what, + const struct sockaddr *got, size_t gotlen, + const struct sockaddr *want, size_t wantlen) +{ + if (got == 0 && want == 0) + return (1); + if (got == 0 || want == 0) { + if (t) { + VSTRING *got_buf = vstring_alloc(100); + VSTRING *want_buf = vstring_alloc(100); + + ptest_error(t, "%s:%d %s: got %s, want %s", + file, line, what, + got ? sockaddr_to_string(got_buf, got, gotlen) + : "(null)", + want ? sockaddr_to_string(want_buf, want, wantlen) + : "(null)"); + vstring_free(want_buf); + vstring_free(got_buf); + } + return (0); + } + if (!_eq_enum(file, line, (PTEST_CTX *) 0, "struct sockaddr address family", + got->sa_family, want->sa_family, af_to_string) + || !_eq_size_t(file, line, (PTEST_CTX *) 0, "struct sockaddr length", + gotlen, wantlen) + || memcmp(got, want, gotlen) != 0) { + VSTRING *got_buf = vstring_alloc(100); + VSTRING *want_buf = vstring_alloc(100); + + ptest_error(t, "%s:%d %s: got %s, want %s", + file, line, what, + sockaddr_to_string(got_buf, got, gotlen), + sockaddr_to_string(want_buf, want, wantlen)); + vstring_free(want_buf); + vstring_free(got_buf); + return (0); + } + return (1); +} diff --git a/postfix/src/testing/match_addr.h b/postfix/src/testing/match_addr.h new file mode 100644 index 000000000..5d2e82c84 --- /dev/null +++ b/postfix/src/testing/match_addr.h @@ -0,0 +1,55 @@ +#ifndef _MATCH_ADDR_H_INCLUDED_ +#define _MATCH_ADDR_H_INCLUDED_ + +/*++ +/* NAME +/* match_addr 3h +/* SUMMARY +/* network address matcher +/* SYNOPSIS +/* #include +/* DESCRIPTION +/* .nf + + /* + * System library. + */ +#include +#include +#include +#include + + /* + * Test library. + */ +#include + + /* + * Matchers. + */ +#define eq_addrinfo(t, what, got, want) \ + _eq_addrinfo(__FILE__, __LINE__, (t), (what), (got), (want)) + +extern int _eq_addrinfo(const char *, int, PTEST_CTX *, const char *, + const struct addrinfo *, + const struct addrinfo *); + +#define eq_sockaddr(t, what, got, gotlen, want, wantlen) \ + _eq_sockaddr(__FILE__, __LINE__, (t), (what), (got), (gotlen), (want), (wantlen)) + +extern int _eq_sockaddr(const char *, int, PTEST_CTX *, const char *, + const struct sockaddr *, size_t, + const struct sockaddr *, size_t); + +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* Google, Inc. +/* 111 8th Avenue +/* New York, NY 10011, USA +/*--*/ + +#endif diff --git a/postfix/src/testing/match_addr_test.c b/postfix/src/testing/match_addr_test.c new file mode 100644 index 000000000..c577468c6 --- /dev/null +++ b/postfix/src/testing/match_addr_test.c @@ -0,0 +1,135 @@ + /* + * Test program to exercise make_addr functions including logging. See + * comments in ptest_main.h and pmock_expect_test.c for a documented + * example. + */ + + /* + * System library. + */ +#include +#include +#include +#include +#include + + /* + * Test library. + */ +#include +#include +#include + +typedef struct PTEST_CASE { + const char *testname; /* Human-readable description */ + void (*action) (PTEST_CTX *t, const struct PTEST_CASE *); +} PTEST_CASE; + +static void test_eq_addrinfo_diff(PTEST_CTX *t, const PTEST_CASE *unused) +{ + struct addrinfo hints; + struct addrinfo *want_addrinfo; + struct addrinfo *other_addrinfo; + + /* + * Set up expectations. + */ + memset(&hints, 0, sizeof(hints)); + hints.ai_family = PF_INET; + hints.ai_socktype = SOCK_STREAM; + want_addrinfo = make_addrinfo(&hints, "localhost", "127.0.0.1", 25); + other_addrinfo = make_addrinfo(&hints, "localhost", "127.0.0.2", 25); + + /* + * Verify that the two addrinfos don't match. Do not count this mismatch + * as an error. Instead, count the absence of the mismatch as an error. + */ + expect_ptest_error(t, " ai_addr: " + "got {AF_INET, 127.0.0.2, 25}, " + "want {AF_INET, 127.0.0.1, 25}"); + if (eq_addrinfo(t, "test_eq_addrinfo", other_addrinfo, want_addrinfo)) + ptest_error(t, "eq_addrinfo() returned true for different objects"); + + /* + * Clean up. + */ + freeaddrinfo(want_addrinfo); + freeaddrinfo(other_addrinfo); +} + +static void test_eq_addrinfo_null(PTEST_CTX *t, const PTEST_CASE *unused) +{ + struct addrinfo hints; + struct addrinfo *want_addrinfo; + struct addrinfo *other_addrinfo = 0; + + /* + * Set up expectations. + */ + memset(&hints, 0, sizeof(hints)); + hints.ai_family = PF_INET; + hints.ai_socktype = SOCK_STREAM; + want_addrinfo = make_addrinfo(&hints, "localhost", "127.0.0.1", 25); + + /* + * Verify that the two addrinfos don't match. Do not count this mismatch + * as an error. Instead, count the absence of the mismatch as an error. + */ + expect_ptest_error(t, "test_eq_addrinfo_null: got (null), " + "want {0, PF_INET, SOCK_STREAM, 0, 16, " + "{AF_INET, 127.0.0.1, 25}, localhost}"); + if (eq_addrinfo(t, "test_eq_addrinfo_null", other_addrinfo, want_addrinfo)) + ptest_error(t, "eq_addrinfo() returned true for different objects"); + + /* + * Clean up. + */ + freeaddrinfo(want_addrinfo); +} + +static void test_eq_sockaddr_diff(PTEST_CTX *t, const PTEST_CASE *unused) +{ + struct sockaddr *want_sockaddr; + struct sockaddr *other_sockaddr; + + /* + * Set up expectations. + */ + want_sockaddr = make_sockaddr(AF_INET, "127.0.0.1", 25); + other_sockaddr = make_sockaddr(AF_INET, "127.0.0.2", 25); + + /* + * Verify that the two sockaddrs don't match. Do not count this mismatch + * as an error. Instead, count the absence of the mismatch as an error. + */ + expect_ptest_error(t, "test_eq_sockaddr_diff: " + "got {AF_INET, 127.0.0.2, 25}, " + "want {AF_INET, 127.0.0.1, 25}"); + if (eq_sockaddr(t, "test_eq_sockaddr_diff", + other_sockaddr, sizeof(struct sockaddr_in), + want_sockaddr, sizeof(struct sockaddr_in))) + ptest_error(t, "eq_sockaddr() returned true for different objects"); + + /* + * Clean up. + */ + free_sockaddr(want_sockaddr); + free_sockaddr(other_sockaddr); +} + + /* + * Test cases. + */ +const PTEST_CASE ptestcases[] = { + { + "Compare different IPv4 addrinfos", test_eq_addrinfo_diff, + }, + { + "Compare null and non-null IPv4 addrinfos", test_eq_addrinfo_null, + }, + { + "Compare different IPv4 sockaddrs", test_eq_sockaddr_diff, + }, +}; + +#include diff --git a/postfix/src/testing/match_basic.c b/postfix/src/testing/match_basic.c new file mode 100644 index 000000000..8fcc2cbf3 --- /dev/null +++ b/postfix/src/testing/match_basic.c @@ -0,0 +1,213 @@ +/*++ +/* NAME +/* match_basic 3 +/* SUMMARY +/* basic matchers +/* SYNOPSIS +/* #include +/* +/* int eq_int( +/* PTEST_CTX *t, +/* const char *what, +/* int got, +/* int want) +/* +/* int eq_size_t( +/* PTEST_CTX *t, +/* const char *what, +/* size_t got, +/* size_t want) +/* +/* int eq_ssize_t( +/* PTEST_CTX *t, +/* const char *what, +/* ssize_t got, +/* ssize_t want) +/* +/* int eq_flags( +/* PTEST_CTX *t, +/* const char *what, +/* int got, +/* int want, +/* const char *(*flags_to_str) (VSTRING *, int)) +/* +/* int eq_enum( +/* PTEST_CTX *t, +/* const char *what, +/* int got, +/* int want, +/* const char *(*enum_to_str) (int)) +/* +/* int eq_str( +/* PTEST_CTX *t, +/* const char *what, +/* const char *got, +/* const char *want) +/* +/* int eq_argv( +/* PTEST_CTX *t, +/* const char *what, +/* const ARGV *got, +/* const ARGV *want) +/* DESCRIPTION +/* The functions described here are actually safe macros that +/* include call-site information (file name, line number) in +/* error messages. +/* +/* eq_int() compares two integers, and if t is not null, reports +/* values that differ with ptest_error()); +/* +/* eq_flags() compares two integer bitmasks, and if t is not +/* null, reports values that differ with ptest_error()); +/* +/* eq_enum() compares two integer enum values, and if t is not +/* null, reports values that differ with ptest_error()); +/* +/* eq_str() compares two null-terminated strings, and if t is +/* not null, reports values that differ with ptest_error()); +/* +/* eq_argv() compares two null-terminated string arrays, and +/* if t is not null, reports values that differ with ptest_error()); +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* Google, Inc. +/* 111 8th Avenue +/* New York, NY 10011, USA +/*--*/ + + /* + * System library. + */ +#include +#include + + /* + * Utility library. + */ +#include + + /* + * Test library. + */ +#include +#include + +#define STR_OR_NULL(s) ((s) ? (s) : "(null)") + +/* _eq_int - match integers */ + +int _eq_int(const char *file, int line, PTEST_CTX *t, + const char *what, int got, int want) +{ + if (got != want) { + if (t) + ptest_error(t, "%s:%d: %s: got %d, want %d", file, line, what, got, want); + return (0); + } + return (1); +} + +/* _eq_size_t - match size_t */ + +int _eq_size_t(const char *file, int line, PTEST_CTX *t, + const char *what, size_t got, size_t want) +{ + if (got != want) { + if (t) + ptest_error(t, "%s:%d: %s: got %lu, want %lu", + file, line, what, (long) got, (long) want); + return (0); + } + return (1); +} + +/* _eq_ssize_t - match ssize_t */ + +int _eq_ssize_t(const char *file, int line, PTEST_CTX *t, + const char *what, ssize_t got, ssize_t want) +{ + if (got != want) { + if (t) + ptest_error(t, "%s:%d: %s: got %ld, want %ld", + file, line, what, (long) got, (long) want); + return (0); + } + return (1); +} + +/* _eq_flags - match flags */ + +int _eq_flags(const char *file, int line, PTEST_CTX *t, + const char *what, int got, int want, + const char *(flags_to_string) (VSTRING *, int)) +{ + if (got != want) { + if (t) { + VSTRING *got_buf = vstring_alloc(100); + VSTRING *want_buf = vstring_alloc(100); + + ptest_error(t, "%s:%d: %s: got %s, want %s", file, line, what, + flags_to_string(got_buf, got), + flags_to_string(want_buf, want)); + vstring_free(got_buf); + vstring_free(want_buf); + } + return (0); + } + return (1); +} + +/* _eq_enum - match enum */ + +int _eq_enum(const char *file, int line, PTEST_CTX *t, + const char *what, int got, int want, + const char *(enum_to_string) (int)) +{ + if (got != want) { + if (t) + ptest_error(t, "%s:%d: %s: got %s, want %s", file, line, what, + enum_to_string(got), enum_to_string(want)); + return (0); + } + return (1); +} + +/* _eq_str - match null-terminated strings */ + +int _eq_str(const char *file, int line, PTEST_CTX *t, + const char *what, const char *got, const char *want) +{ + if (strcmp(got, want) != 0) { + if (t) + ptest_error(t, "%s:%d: %s: got '%s', want '%s'", + file, line, what, got, want); + return (0); + } + return (1); +} + +/* _eq_argv - match ARGV instances */ + +int _eq_argv(const char *file, int line, PTEST_CTX *t, + const char *what, const ARGV *got, const ARGV *want) +{ + char **gpp, **wpp; + + for (gpp = got->argv, wpp = want->argv; /* see below */ ; gpp++, wpp++) { + if (*gpp == 0 && *wpp == 0) { + return (1); + } else if (*gpp == 0 || *wpp == 0) { + if (t) + ptest_error(t, "%s:%d: %s: got %s, want %s", + file, line, what, STR_OR_NULL(*gpp), + STR_OR_NULL(*wpp)); + return (0); + } else if (!_eq_str(file, line, t, what, *gpp, *wpp)) { + return (0); + } + } +} diff --git a/postfix/src/testing/match_basic.h b/postfix/src/testing/match_basic.h new file mode 100644 index 000000000..5451e021f --- /dev/null +++ b/postfix/src/testing/match_basic.h @@ -0,0 +1,82 @@ +#ifndef _MATCH_BASIC_H_INCLUDED_ +#define _MATCH_BASIC_H_INCLUDED_ + +/*++ +/* NAME +/* match_basic 3h +/* SUMMARY +/* basic matchers +/* SYNOPSIS +/* #include +/* DESCRIPTION +/* .nf + + /* + * System library. + */ +#include +#include + + /* + * Test library. + */ +#include + + /* + * Matchers. + */ +#define eq_int(t, what, got, want) \ + _eq_int(__FILE__, __LINE__, (t), (what), (got), (want)) + +extern int _eq_int(const char *, int, PTEST_CTX *, + const char *, int, int); + +#define eq_size_t(t, what, got, want) \ + _eq_int(__FILE__, __LINE__, (t), (what), (got), (want)) + +extern int _eq_size_t(const char *, int, PTEST_CTX *, + const char *, size_t, size_t); + +#define eq_ssize_t(t, what, got, want) \ + _eq_int(__FILE__, __LINE__, (t), (what), (got), (want)) + +extern int _eq_ssize_t(const char *, int, PTEST_CTX *, + const char *, ssize_t, ssize_t); + +#define eq_flags(t, what, got, want, flags_to_str) \ + _eq_flags(__FILE__, __LINE__, (t), (what), (got), (want), (flags_to_str)) + +extern int _eq_flags(const char *, int, PTEST_CTX *, + const char *, int, int, + const char *(*flags_to_str) (VSTRING *, int)); + +#define eq_enum(t, what, got, want, enum_to_str) \ + _eq_lflags(__FILE__, __LINE__, (t), (what), (got), (want), (enum_to_str)) + +extern int _eq_enum(const char *, int, PTEST_CTX *, + const char *, int, int, const char *(*enum_to_str) (int)); + +#define eq_str(t, what, got, want) \ + _eq_str(__FILE__, __LINE__, (t), (what), (got), (want)) + +extern int _eq_str(const char *, int, PTEST_CTX *, + const char *, const char *, const char *); + +#define eq_argv(t, what, got, want) \ + _eq_argv(__FILE__, __LINE__, (t), (what), (got), (want)) + +extern int _eq_argv(const char *, int, PTEST_CTX *, + const char *, const ARGV *, const ARGV *); + +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* Google, Inc. +/* 111 8th Avenue +/* New York, NY 10011, USA +/*--*/ + +#endif diff --git a/postfix/src/testing/mock_dns.h b/postfix/src/testing/mock_dns.h new file mode 100644 index 000000000..674c402ac --- /dev/null +++ b/postfix/src/testing/mock_dns.h @@ -0,0 +1,70 @@ +#ifndef _MOCK_DNS_H_INCLUDED_ +#define _MOCK_DNS_H_INCLUDED_ + +/*++ +/* NAME +/* mock_dns 3h +/* SUMMARY +/* emulate DNS support for hermetic tests +/* SYNOPSIS +/* #include +/* DESCRIPTION +/* .nf + + /* + * DNS library. + */ +#include + + /* + * Test library. + */ +#include + + /* + * Manage expectations and responses. Capture the source file name and line + * number for better diagnostics. + */ +#define expect_dns_lookup_x(exp_calls, herrval, retval, name, type, rflags, \ + list, fqdn, why, rcode, lflags) \ + _expect_dns_lookup_x(__FILE__, __LINE__, (exp_calls), (herrval), \ + (retval), (name), (type), (rflags), (list), \ + (fqdn), (why), (rcode), (lflags)) + +extern void _expect_dns_lookup_x(const char *, int, int, int, int, + const char *, unsigned, unsigned, DNS_RR *, + VSTRING *, VSTRING *, int, unsigned); + + /* + * Matcher predicates. Capture the source file name and line number for + * better diagnostics. + */ +#define eq_dns_rr(t, what, got, want) _eq_dns_rr((t), __FILE__, __LINE__, \ + (what), (got), (want)) + +extern int _eq_dns_rr(PTEST_CTX *, const char *, int, const char *, DNS_RR *, DNS_RR *); + + /* + * Helper to create test data. + */ +extern DNS_RR *make_dns_rr(const char *, const char *, unsigned, unsigned, + unsigned, unsigned, unsigned, + void *, size_t); + + /* + * Other helper. + */ +extern const char *dns_status_to_string(int); + +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* Google, Inc. +/* 111 8th Avenue +/* New York, NY 10011, USA +/*--*/ + +#endif diff --git a/postfix/src/testing/mock_dns_lookup.c b/postfix/src/testing/mock_dns_lookup.c new file mode 100644 index 000000000..0c010bd4f --- /dev/null +++ b/postfix/src/testing/mock_dns_lookup.c @@ -0,0 +1,482 @@ +/*++ +/* NAME +/* mock_dns_lookup 3 +/* SUMMARY +/* dns_lookup mock for hermetic tests +/* SYNOPSIS +/* #include +/* +/* int dns_lookup_x( +/* const char *name, +/* unsigned type, +/* unsigned rflags, +/* DNS_RR **list, +/* VSTRING *fqdn, +/* VSTRING *why, +/* int *rcode, +/* unsigned lflags) +/* +/* int dns_get_h_errno(void) +/* +/* void dns_set_h_errno(int herrval) +/* EXPECTATION SETUP +/* void expect_dns_lookup_x( +/* int calls_expected, +/* int herrval, +/* int retval, +/* const char *name, +/* unsigned type, +/* unsigned flags, +/* DNS_RR *rrlist, +/* VSTRING *fqdn, +/* VSTRING *why, +/* int rcode, +/* unsigned lflags) +/* +/* DNS_RR *make_dns_rr( +/* const char *qname, +/* const char *rname, +/* unsigned type, +/* unsigned class, +/* unsigned ttl, +/* unsigned dnssec_valid, +/* unsigned pref, +/* void *data, +/* size_t data_len) +/* MATCHERS +/* int eq_dns_rr( +/* PTEST_CTX *t, +/* const char *what, +/* DNS_RR *got, +/* DNS_RR *want) +/* OTHER HELPERS +/* const char *dns_status_to_string(int status) +/* DESCRIPTION +/* This module implements a mock dns_lookup_x() lookup function +/* that produces prepared outputs in response to expected +/* inputs. This supports hermetic tests, i.e. tests that do +/* not depend on host configuration or on network access. +/* +/* expect_dns_lookup_x() makes deep copies of its input +/* arguments, and of the arguments that specify prepared +/* outputs. The herrval argument specifies a prepared +/* dns_get_h_errno() result value, and the retval argument +/* specifies a prepared dns_lookup_x() result value. The +/* calls_expected argument specifies the expected number of +/* dns_lookup_x() calls (zero means one or more calls, not: +/* zero calls). +/* +/* dns_get_h_errno() returns an error value that is configured +/* with expect_dns_lookup_x(), and that is assigned when +/* dns_lookup_x() or dns_set_h_errno() are called. +/* +/* dns_set_h_errno() assigns the dns_get_h_errno() result +/* value. +/* +/* make_dns_rr() is a wrapper around dns_rr_create() that also +/* controls the dnssec_valid flag. +/* +/* eq_dns_rr() is a predicate that compares its arguments +/* (linked lists) for equality. If t is not null, the what +/* argument is used in logging when the inputs differ. +/* DIAGNOSTICS +/* If a mock is called unexpectedly (the call arguments do not +/* match an expectation, or more calls are made than expected), +/* a warning is logged, and the test will be flagged as failed. +/* For now the mock returns an error result to the caller. +/* TODO: consider aborting the test. +/* SEE ALSO +/* dns_lookup(3), domain name service lookup +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* Google, Inc. +/* 111 8th Avenue +/* New York, NY 10011, USA +/*--*/ + + /* + * System library. + */ +#include + + /* + * Utility library. + */ +#include +#include +#include +#include +#include +#include + + /* + * DNS library. + */ +#include + + /* + * Test library. + */ +#include +#include +#include + + /* + * Helpers. + */ +#define MYSTRDUP_OR_NULL(x) ((x) ? mystrdup(x) : 0) +#define VSTRDUP_OR_NULL(x) \ + ((x) ? vstring_strcpy(vstring_alloc(VSTRING_LEN(x)), vstring_str(x)) : 0) + +#define STR(x) vstring_str(x) + +#define STR_OR_NULL(s) ((s) ? (s) : "(null)") +#define VSTR_STR_OR_NULL(s) ((s) ? STR(s) : "(null)") + + /* + * Local state for the mock functions dns_get_herrno() and dns_set_herrno(), + * also updated when the mock function dns_lookup_x() is called. + * + * XXX This could leak information when tests are run successively in the same + * process. Should the test infrastructure fork() the process before each + * test? Leakage will not happen when each test calls a function that resets + * global_herrval. + */ +static int global_herrval = ~0; + +/* dns_status_to_string - convert status code to string */ + +const char *dns_status_to_string(int status) +{ + static const NAME_CODE status_string[] = { + "DNS_OK", DNS_OK, + "DNS_POLICY", DNS_POLICY, + "DNS_RETRY", DNS_RETRY, + "DNS_INVAL", DNS_INVAL, + "DNS_FAIL", DNS_FAIL, + "DNS_NULLMX", DNS_NULLMX, + "DNS_NOTFOUND", DNS_NOTFOUND, + 0, + }; + + return (str_name_code(status_string, status)); +} + +/* copy_dns_rrlist - deep copy */ + +static DNS_RR *copy_dns_rrlist(DNS_RR *list) +{ + DNS_RR *rr; + + if (list == 0) + return (0); + rr = dns_rr_copy(list); + rr->next = copy_dns_rrlist(list->next); + return (rr); +} + +/* make_dns_rr - dns_rr_create() wrapper */ + +DNS_RR *make_dns_rr(const char *qname, const char *rname, unsigned type, + unsigned class, unsigned ttl, + unsigned dnssec_valid, unsigned pref, + void *data, size_t data_len) +{ + DNS_RR *rr; + + rr = dns_rr_create(qname, rname, type, class, ttl, pref, data, data_len); + rr->dnssec_valid = dnssec_valid; + return (rr); +} + +/* _eq_dns_rr - equality predicate */ + +int _eq_dns_rr(PTEST_CTX *t, const char *file, int line, + const char *what, + DNS_RR *got, DNS_RR *want) +{ + if (got == 0 && want == 0) { + return (1); + } + if (got == 0 || want == 0) { + if (t) + ptest_error(t, "%s:%d %s: got %s, want %s", + file, line, what, got ? "(DNS_RR *)" : "(null)", + want ? "(DNS_RR *)" : "(null)"); + return (0); + } + if (strcmp(got->qname, want->qname) != 0) { + if (t) + ptest_error(t, "%s:%d %s: got qname '%s', want '%s'", + file, line, what, got->qname, want->qname); + return (0); + } + if (strcmp(got->rname, want->rname) != 0) { + if (t) + ptest_error(t, "%s:%d %s: got rname '%s', want '%s'", + file, line, what, got->rname, want->rname); + return (0); + } + if (got->type != want->type) { + if (t) + ptest_error(t, "%s:%d %s: got type %d, want %d", + file, line, what, got->type, want->type); + return (0); + } + if (got->class != want->class) { + if (t) + ptest_error(t, "%s:%d %s: got class %d, want %d", + file, line, what, got->class, want->class); + return (0); + } + if (got->ttl != want->ttl) { + if (t) + ptest_error(t, "%s:%d %s: got ttl %d, want %d", + file, line, what, got->ttl, want->ttl); + return (0); + } + if (got->dnssec_valid != want->dnssec_valid) { + if (t) + ptest_error(t, "%s:%d %s: got dnssec_valid %d, want %d", + file, line, what, got->dnssec_valid, want->dnssec_valid); + return (0); + } + if (got->pref != want->pref) { + if (t) + ptest_error(t, "%s:%d %s: got pref %d, want %d", + file, line, what, got->pref, want->pref); + return (0); + } + if (got->data_len != want->data_len) { + if (t) + ptest_error(t, "%s:%d %s: got data_len %d, want %d", + file, line, what, (int) got->data_len, (int) want->data_len); + return (0); + } + if (memcmp(got->data, want->data, got->data_len) != 0) { + VSTRING *got_data_hex = vstring_alloc(100); + VSTRING *want_data_hex = vstring_alloc(100); + + if (t) + ptest_error(t, "%s:%d %s: got data %s, want %s", + file, line, what, STR(hex_encode_opt(got_data_hex, got->data, + got->data_len, HEX_ENCODE_FLAG_USE_COLON)), + STR(hex_encode_opt(want_data_hex, want->data, + want->data_len, HEX_ENCODE_FLAG_USE_COLON))); + vstring_free(got_data_hex); + vstring_free(want_data_hex); + return (0); + } + return (_eq_dns_rr(t, file, line, what, got->next, want->next)); +} + + /* + * Manage dns_lookup_x() expectations and responses. We use this structure + * for deep copies of expect_dns_lookup_x() expected inputs and prepared + * responses, and for shallow copies of dns_lookup_x() inputs. + */ +struct dns_lookup_x_expectation { + MOCK_EXPECT mock_expect; /* generic fields */ + int herrval; /* h_errno value */ + int retval; /* result value */ + char *name; /* inputs */ + unsigned type; + unsigned flags; + unsigned lflags; + DNS_RR *rrlist; /* outputs */ + VSTRING *fqdn; + VSTRING *why; + int rcode; +}; + + /* + * Pointers to dns_lookup_x() outputs. + */ +struct dns_lookup_x_targets { + int *herrval; + int *retval; + DNS_RR **rrlist; + VSTRING *fqdn; + VSTRING *why; + int *rcode; +}; + +/* match_dns_lookup_x - match inputs against expectation */ + +static int match_dns_lookup_x(const MOCK_EXPECT *expect, + const MOCK_EXPECT *inputs) +{ + struct dns_lookup_x_expectation *pe = + (struct dns_lookup_x_expectation *) expect; + struct dns_lookup_x_expectation *pi = + (struct dns_lookup_x_expectation *) inputs; + + return (strcmp(STR_OR_NULL(pe->name), + STR_OR_NULL(pi->name)) == 0 + && pe->type == pi->type + && pe->flags == pi->flags + && pe->lflags == pi->lflags); +} + +/* assign_dns_lookup_x - assign expected output */ + +static void assign_dns_lookup_x(const MOCK_EXPECT *expect, + void *targets) +{ + struct dns_lookup_x_expectation *pe = + (struct dns_lookup_x_expectation *) expect; + struct dns_lookup_x_targets *pt = + (struct dns_lookup_x_targets *) targets; + + if (pe->retval == DNS_OK) { + if (pt->rrlist) + *(pt->rrlist) = copy_dns_rrlist(pe->rrlist); + if (pt->fqdn && pe->fqdn) + vstring_strcpy(pt->fqdn, STR(pe->fqdn)); + } else { + if (pt->why && pe->why) + vstring_strcpy(pt->why, STR(pe->why)); + } + if (pt->rcode) + *pt->rcode = pe->rcode; + *pt->retval = pe->retval; + *pt->herrval = pe->herrval; +} + +/* print_dns_lookup_x - print expected inputs */ + +static char *print_dns_lookup_x(const MOCK_EXPECT *expect, + VSTRING *buf) +{ + struct dns_lookup_x_expectation *pe = + (struct dns_lookup_x_expectation *) expect; + + vstring_sprintf(buf, "\"%s\", %s, %d, (ptr), (ptr), (ptr), (ptr), %d", + STR_OR_NULL(pe->name), dns_strtype(pe->type), + pe->flags, pe->lflags); + return (STR(buf)); +} + +/* free_dns_lookup_x - destructor */ + +static void free_dns_lookup_x(MOCK_EXPECT *expect) +{ + struct dns_lookup_x_expectation *pe = + (struct dns_lookup_x_expectation *) expect; + + myfree(pe->name); + if (pe->retval == DNS_OK) { + if (pe->rrlist) + dns_rr_free(pe->rrlist); + if (pe->fqdn) + vstring_free(pe->fqdn); + } else { + if (pe->why) + vstring_free(pe->why); + } + pmock_expect_free(expect); +} + + /* + * The mock name and its helper callbacks in one place. + */ +static const MOCK_APPL_SIG dns_lookup_x_sig = { + "dns_lookup_x", + match_dns_lookup_x, + assign_dns_lookup_x, + print_dns_lookup_x, + free_dns_lookup_x, +}; + +/* _expect_dns_lookup_x - set up expectation */ + +void _expect_dns_lookup_x(const char *file, int line, int calls_expected, + int herrval, int retval, + const char *name, unsigned type, unsigned flags, + DNS_RR *rrlist, VSTRING *fqdn, VSTRING *why, + int rcode, unsigned lflags) +{ + struct dns_lookup_x_expectation *pe; + + pe = (struct dns_lookup_x_expectation *) + pmock_expect_create(&dns_lookup_x_sig, + file, line, calls_expected, sizeof(*pe)); + + /* + * Inputs. + */ + pe->name = MYSTRDUP_OR_NULL(name); + pe->type = type; + pe->flags = flags; + pe->lflags = lflags; + + /* + * Outputs. + */ + pe->herrval = herrval; + pe->retval = retval; + if (pe->retval == DNS_OK) { + pe->rrlist = copy_dns_rrlist(rrlist); + pe->fqdn = VSTRDUP_OR_NULL(fqdn); + } else { + pe->why = VSTRDUP_OR_NULL(why); + } + pe->rcode = rcode; +} + +/* dns_lookup_x - answer the call with prepared responses */ + +int dns_lookup_x(const char *name, unsigned type, unsigned flags, + DNS_RR **rrlist, VSTRING *fqdn, VSTRING *why, + int *rcode, unsigned lflags) +{ + struct dns_lookup_x_expectation inputs; + struct dns_lookup_x_targets targets; + int retval = DNS_FAIL; + + /* + * Bundle the arguments to simplify handling. + */ + inputs.name = (char *) name; + inputs.type = type; + inputs.flags = flags; + inputs.lflags = lflags; + + targets.herrval = &global_herrval; + targets.retval = &retval; + targets.rrlist = rrlist; + targets.fqdn = fqdn; + targets.why = why; + targets.rcode = rcode; + + /* + * Aargh. + */ + if (rrlist) + *rrlist = 0; + + /* + * TODO: bail out if there was no match? + */ + (void) pmock_expect_apply(&dns_lookup_x_sig, + &inputs.mock_expect, (void *) &targets); + return (retval); +} + +/* dns_get_h_errno - return prepared answer */ + +int dns_get_h_errno(void) +{ + return (global_herrval); +} + +/* dns_set_h_errno - return prepared answer */ + +void dns_set_h_errno(int herrval) +{ + global_herrval = herrval; +} diff --git a/postfix/src/testing/mock_dns_lookup_test.c b/postfix/src/testing/mock_dns_lookup_test.c new file mode 100644 index 000000000..bdc0a995d --- /dev/null +++ b/postfix/src/testing/mock_dns_lookup_test.c @@ -0,0 +1,206 @@ + /* + * Test program to exercise mocks including logging. See comments in + * ptest_main.h and pmock_expect_test.c for a documented example. + */ + + /* + * System library. + */ +#include +#include + + /* + * Utility library. + */ +#include +#include + + /* + * Test library. + */ +#include +#include +#include + +typedef struct PTEST_CASE { + const char *testname; /* Human-readable description */ + void (*action) (PTEST_CTX *t, const struct PTEST_CASE *); +} PTEST_CASE; + +#define NO_RES_FLAGS 0 + +static void test_dns_lookup_x_success(PTEST_CTX *t, const PTEST_CASE *unused) +{ + VSTRING *got_fqdn = vstring_alloc(100), *want_fqdn = vstring_alloc(100); + int got_st, want_st = DNS_OK; + int got_herrval, want_herrval = 0; + DNS_RR *got_rr = 0, *want_rr; + int got_rcode, want_rcode = NOERROR; + struct in_addr sin_addr; + const char *localhost = "localhost"; + + /* + * Set up expectations. + */ + vstring_strcpy(want_fqdn, localhost); + if (inet_pton(AF_INET, "127.0.0.1", &sin_addr) != 1) + ptest_fatal(t, "inet_pton(AF_INET, \"127.0.0.1\", (ptr)): bad address"); + want_rr = make_dns_rr(localhost, localhost, T_A, C_IN, + 10, 0, 0, &sin_addr, sizeof(sin_addr)); + expect_dns_lookup_x(1, want_herrval, want_st, localhost, T_A, NO_RES_FLAGS, + want_rr, want_fqdn, (VSTRING *) 0, + want_rcode, DNS_REQ_FLAG_NONE); + + /* + * Invoke the mock and verify results. + */ + got_st = dns_lookup_x("localhost", T_A, NO_RES_FLAGS, &got_rr, + got_fqdn, (VSTRING *) 0, &got_rcode, + DNS_REQ_FLAG_NONE); + if (got_st != want_st) { + ptest_error(t, "dns_lookup_x: got result %d, want %d", got_st, want_st); + } else if (eq_dns_rr(t, "dns_lookup_x", got_rr, want_rr) == 0) { + /* warning is already logged */ ; + } else if (strcmp(vstring_str(got_fqdn), vstring_str(want_fqdn)) != 0) { + ptest_error(t, "dns_lookup_x: got fqdn '%s', want '%s'", + vstring_str(got_fqdn), vstring_str(want_fqdn)); + } else if (got_rcode != want_rcode) { + ptest_error(t, "dns_lookup_x: got rcode %d, want %d", got_rcode, want_rcode); + } + got_herrval = dns_get_h_errno(); + if (got_herrval != want_herrval) + ptest_error(t, "dns_get_h_errno: got %d, want %d", + got_herrval, want_herrval); + + /* + * Clean up. + */ + vstring_free(got_fqdn); + vstring_free(want_fqdn); + dns_rr_free(want_rr); + if (got_rr) + dns_rr_free(got_rr); +} + +static void test_dns_lookup_x_notexist(PTEST_CTX *t, const PTEST_CASE *unused) +{ + VSTRING *got_why = vstring_alloc(100), *want_why = vstring_alloc(100); + int got_st, want_st = DNS_NOTFOUND; + int got_herrval, want_herrval = HOST_NOT_FOUND; + int got_rcode, want_rcode = NXDOMAIN; + + /* + * Set up expectations. + */ + vstring_strcpy(want_why, "Host or domain name not found." + " Name service error for name=notexist type=A: Host not found"); + expect_dns_lookup_x(1, want_herrval, want_st, "notexist", T_A, NO_RES_FLAGS, + (DNS_RR *) 0, (VSTRING *) 0, want_why, want_rcode, + DNS_REQ_FLAG_NONE); + + /* + * Invoke the mock and verify results. + */ + got_st = dns_lookup_x("notexist", T_A, NO_RES_FLAGS, (DNS_RR **) 0, + (VSTRING *) 0, got_why, &got_rcode, + DNS_REQ_FLAG_NONE); + if (got_st != want_st) { + ptest_error(t, "dns_lookup_x: got result %d, want %d", got_st, want_st); + } else if (got_rcode != want_rcode) { + ptest_error(t, "dns_lookup_x: got rcode %d, want %d", got_rcode, want_rcode); + } else if (strcmp(vstring_str(got_why), vstring_str(want_why)) != 0) { + ptest_error(t, "dns_lookup_x: got why '%s', want '%s'", + vstring_str(got_why), vstring_str(want_why)); + } + got_herrval = dns_get_h_errno(); + if (got_herrval != want_herrval) + ptest_error(t, "dns_get_h_errno: got %d, want %d", + got_herrval, want_herrval); + + /* + * Clean up. + */ + vstring_free(got_why); + vstring_free(want_why); +} + +static void test_dns_lookup_x_unused(PTEST_CTX *t, const PTEST_CASE *unused) +{ + + /* + * Create an expectation, without calling it. I does not matter what the + * expectation is, so we use the one from test_dns_lookup_x_notexist(). + */ + expect_dns_lookup_x(1, HOST_NOT_FOUND, NXDOMAIN, "notexist", T_A, NO_RES_FLAGS, + (DNS_RR *) 0, (VSTRING *) 0, (VSTRING *) 0, 0, + DNS_REQ_FLAG_NONE); + + /* + * We expect that there will be a 'missing call' error. If the error does + * not happen then the test fails. + */ + expect_ptest_error(t, "got 0 calls for dns_lookup_x(\"notexist\", A, " + "0, (ptr), (ptr), (ptr), (ptr), 0), want 1"); +} + +static void test_dns_set_h_errno_success(PTEST_CTX *t, const PTEST_CASE *unused) +{ + static int want_herrval[] = {12345, 54321}; + int got_herrval; + int n; + + for (n = 0; n < 2; n++) { + dns_set_h_errno(want_herrval[n]); + got_herrval = dns_get_h_errno(); + if (got_herrval != want_herrval[n]) + ptest_error(t, "dns_get_h_errno: got %d, want %d", + got_herrval, want_herrval[n]); + } +} + +static void test_eq_dns_rr_differ(PTEST_CTX *t, const PTEST_CASE *unused) +{ + DNS_RR *got_rr, *want_rr; + struct in_addr sin_addr; + const char *localhost = "localhost"; + + if (inet_pton(AF_INET, "127.0.0.1", &sin_addr) != 1) + ptest_fatal(t, "inet_pton(AF_INET, \"127.0.0.1\", (ptr)): bad address"); + want_rr = make_dns_rr(localhost, localhost, T_A, C_IN, + 10, 0, 0, &sin_addr, sizeof(sin_addr)); + + if (inet_pton(AF_INET, "127.0.0.2", &sin_addr) != 1) + ptest_fatal(t, "inet_pton(AF_INET, \"127.0.0.2\", (ptr)): bad address"); + got_rr = make_dns_rr(localhost, localhost, T_A, C_IN, + 10, 0, 0, &sin_addr, sizeof(sin_addr)); + + expect_ptest_error(t, "eq_dns_rr: got data 7F:00:00:02, want 7F:00:00:01"); + if (eq_dns_rr(t, "eq_dns_rr", got_rr, want_rr)) + ptest_error(t, "eq_dns_rr: Unexpected match"); + dns_rr_free(got_rr); + dns_rr_free(want_rr); +} + + /* + * Test cases. The "success" tests exercise the expectation match and apply + * helpers, and "unused" tests exercise the print helpers. + */ +const PTEST_CASE ptestcases[] = { + { + "test_dns_lookup_x success", test_dns_lookup_x_success, + }, + { + "test_dns_lookup_x notexist", test_dns_lookup_x_notexist, + }, + { + "test_dns_lookup_x unused", test_dns_lookup_x_unused, + }, + { + "dns_set_h_errno success", test_dns_set_h_errno_success, + }, + { + "test_eq_dns_rr differ", test_eq_dns_rr_differ, + }, +}; + +#include diff --git a/postfix/src/testing/mock_getaddrinfo.c b/postfix/src/testing/mock_getaddrinfo.c new file mode 100644 index 000000000..41fc941a9 --- /dev/null +++ b/postfix/src/testing/mock_getaddrinfo.c @@ -0,0 +1,487 @@ +/*++ +/* NAME +/* mock_getaddrinfo 3 +/* SUMMARY +/* mock getaddrinfo/getnameinfo for hermetic tests +/* SYNOPSIS +/* #include +/* +/* int getaddrinfo( +/* const char *hostname, +/* const char *servname, +/* const struct addrinfo *hints, +/* struct addrinfo **result, +/* +/* int getnameinfo( +/* const struct sockaddr *sa, +/* size_t salen, +/* char *host, +/* size_t hostlen, +/* char *serv, +/* size_t servlen +/* int flags) +/* EXPECTATION SETUP +/* void expect_getaddrinfo( +/* int calls_expected, +/* int retval, +/* const char *hostname, +/* const char *servname, +/* const struct addrinfo *hints, +/* struct addrinfo *result) +/* +/* void expect_getnameinfo( +/* int calls_expected, +/* int retval, +/* const struct sockaddr *sa, +/* size_t salen, +/* char *host, +/* size_t hostlen, +/* char *serv, +/* size_t servlen +/* int flags) +/* TEST DATA +/* struct addrinfo *make_addrinfo( +/* const struct addrinfo *hints, +/* const char *name, +/* const char *addr, +/* int port) +/* +/* struct addrinfo *copy_addrinfo(const struct addrinfo *ai) +/* +/* void freeaddrinfo(struct addrinfo *ai) +/* +/* struct sockaddr *make_sockaddr( +/* int family, +/* const char *addr, +/* int port) +/* +/* void free_sockaddr(struct sockaddr *sa) +/* MATCHERS +/* int eq_addrinfo( +/* PTEST_CTX * t, +/* const char *what, +/* struct addrinfo got, +/* struct addrinfo want) +/* +/* int eq_sockaddr +/* PTEST_CTX * t, +/* const char *what, +/* const struct sockaddr *got, +/* size_t gotlen, +/* const struct sockaddr *want, +/* size_t wantlen) +/* DESCRIPTION +/* This module implements mock system library functions that +/* produce prepared outputs in response to expected inputs. +/* This supports hermetic tests, i.e. tests that do not depend +/* on host configuration or on network access. +/* +/* The "expect_" functions take expected inputs and corresponding +/* outputs. They make deep copies of their arguments, including +/* "struct addrinfo *" linked lists. The retval argument +/* specifies a prepared result value. The calls_expected +/* argument specifies the expected number of calls (zero means +/* one or more calls, not: zero calls). +/* +/* make_addrinfo() creates one addrinfo structure. To create +/* linked list, manually append make_addrinfo() results. +/* +/* copy_addrinfo() makes a deep copy of a linked list of +/* addrinfo structures. +/* +/* freeaddrinfo() deletes a linked list of addrinfo structures. +/* This function must be used for addrinfo structures created +/* with make_addrinfo() and copy_addrinfo(). +/* +/* make_sockaddr() creates a sockaddr structure from the string +/* representation of an IP address. +/* +/* free_sockaddr() exists to make program code more explicit. +/* +/* eq_addrinfo() compares addrinfo linked lists and reports +/* differences with ptest_error(). The what argument provides +/* context. Specify a null test context for silent operation. +/* +/* eq_sockaddr() compares sockaddr instances and reports +/* differences with ptest_error(). The what argument provides +/* context. Specify a null test context for silent operation. +/* +/* append_addrinfo_to_string() appends a textual representation +/* of the referenced addrinfo to the specified buffer. +/* +/* addrinfo_hints_to_string() writes a textual representation of +/* the referenced getaddrinfo() hints object. +/* +/* sockaddr_to_string() writes a textual representation of the +/* referenced sockaddr object. +/* +/* pf_to_string(), af_to_string(), socktype_to_string, +/* ipprotocol_to_string, ai_flags_to_string(), ni_flags_to_string() +/* produce a textual representation of addrinfo properties or +/* getnameinfo() flags. +/* DIAGNOSTICS +/* If a mock is called unexpectedly (the call arguments do not +/* match any expectation, or they do match, but more calls are +/* made than were expected), a warning is logged, and the test +/* will be flagged as failed. For now the mock returns an error +/* result to the caller. TODO: consider aborting the test. +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* Google, Inc. +/* 111 8th Avenue +/* New York, NY 10011, USA +/*--*/ + + /* + * System library. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /* sprintf/snprintf */ + + /* + * Utility library. + */ +#include +#include +#include + + /* + * Test library. + */ +#include +#include +#include + +#define MYSTRDUP_OR_NULL(x) ((x) ? mystrdup(x) : 0) +#define STR_OR_NULL(s) ((s) ? (s) : "(null)") + +#define STR vstring_str + + /* + * Manage getaddrinfo() expectations and responses. We use this structure + * for deep copies of expect_getaddrinfo() expected inputs and prepared + * responses, and for shallow copies of getaddrinfo() inputs, so that we can + * reuse the print_getaddrinfo() helper. + */ +struct getaddrinfo_expectation { + MOCK_EXPECT mock_expect; /* generic fields */ + int retval; /* result value */ + char *node; /* inputs */ + char *service; + struct addrinfo *hints; + struct addrinfo *res; /* outputs */ +}; + + /* + * Pointers to getaddrinfo() outputs. + */ +struct getaddrinfo_targets { + struct addrinfo **res; + int *retval; +}; + +/* match_getaddrinfo - match inputs against expectation */ + +static int match_getaddrinfo(const MOCK_EXPECT *expect, + const MOCK_EXPECT *inputs) +{ + struct getaddrinfo_expectation *pe = + (struct getaddrinfo_expectation *) expect; + struct getaddrinfo_expectation *pi = + (struct getaddrinfo_expectation *) inputs; + + return (strcmp(STR_OR_NULL(pe->node), STR_OR_NULL(pi->node)) == 0 + && strcmp(STR_OR_NULL(pe->service), STR_OR_NULL(pi->service)) == 0 + && eq_addrinfo((PTEST_CTX *) 0, (char *) 0, pe->hints, pi->hints)); +} + +/* assign_getaddrinfo - assign expected output */ + +static void assign_getaddrinfo(const MOCK_EXPECT *expect, void *targets) +{ + struct getaddrinfo_expectation *pe = + (struct getaddrinfo_expectation *) expect; + struct getaddrinfo_targets *pt = + (struct getaddrinfo_targets *) targets; + + if (pe->retval == 0) + *(pt->res) = copy_addrinfo(pe->res); + *pt->retval = pe->retval; +} + +/* print_getaddrinfo - print expected inputs */ + +static char *print_getaddrinfo(const MOCK_EXPECT *expect, VSTRING *buf) +{ + struct getaddrinfo_expectation *pe = + (struct getaddrinfo_expectation *) expect; + VSTRING *hints_buf = vstring_alloc(100); + + vstring_sprintf(buf, "\"%s\", \"%s\", %s, (ptr)", + STR_OR_NULL(pe->node), + STR_OR_NULL(pe->service), + addrinfo_hints_to_string(hints_buf, pe->hints)); + vstring_free(hints_buf); + return (vstring_str(buf)); +} + +/* free_getaddrinfo - destructor */ + +static void free_getaddrinfo(MOCK_EXPECT *expect) +{ + struct getaddrinfo_expectation *pe = + (struct getaddrinfo_expectation *) expect; + + if (pe->node) + myfree(pe->node); + if (pe->service) + myfree(pe->service); + if (pe->hints) + myfree(pe->hints); + if (pe->retval == 0) + freeaddrinfo(pe->res); + pmock_expect_free(expect); +} + + /* + * The mock name and its helper callbacks in one place. + */ +static const MOCK_APPL_SIG getaddrinfo_sig = { + "getaddrinfo", + match_getaddrinfo, + assign_getaddrinfo, + print_getaddrinfo, + free_getaddrinfo, +}; + +/* _expect_getaddrinfo - set up expectation */ + +void _expect_getaddrinfo(const char *file, int line, + int calls_expected, int retval, + const char *node, + const char *service, + const struct addrinfo *hints, + struct addrinfo *res) +{ + struct getaddrinfo_expectation *pe; + + pe = (struct getaddrinfo_expectation *) + pmock_expect_create(&getaddrinfo_sig, + file, line, calls_expected, sizeof(*pe)); + pe->retval = retval; + pe->node = MYSTRDUP_OR_NULL(node); + pe->service = MYSTRDUP_OR_NULL(service); + pe->hints = copy_addrinfo(hints); + if (pe->retval == 0) + pe->res = copy_addrinfo(res); +} + +/* getaddrinfo - mock getaddrinfo */ + +int getaddrinfo(const char *node, + const char *service, + const struct addrinfo *hints, + struct addrinfo **res) +{ + struct getaddrinfo_expectation inputs; + struct getaddrinfo_targets targets; + int retval = EAI_FAIL; + + /* + * Bundle the arguments to simplify handling. + */ + inputs.node = (char *) node; + inputs.service = (char *) service; + inputs.hints = (struct addrinfo *) hints; + + targets.res = res; + targets.retval = &retval; + + /* + * TODO: bail out if there was no match? + */ + (void) pmock_expect_apply(&getaddrinfo_sig, + &inputs.mock_expect, (void *) &targets); + return (retval); +} + + /* + * Manage getnameinfo() expectations and responses. We use this structure + * for deep copies of expect_getnameinfo() expected inputs and prepared + * responses, and for shallow copies of getnameinfo() inputs, so that we can + * reuse the print_getnameinfo() helper. + */ +struct getnameinfo_expectation { + MOCK_EXPECT mock_expect; /* generic fields */ + int retval; /* result value */ + struct sockaddr *sa; /* inputs */ + size_t salen; + char *host; /* outputs */ + size_t hostlen; + char *serv; + size_t servlen; + int flags; /* other input */ +}; + + /* + * Pointers to getnameinfo() outputs. + */ +struct getnameinfo_targets { + char *host; + size_t hostlen; + char *serv; + size_t servlen; + int *retval; +}; + +/* match_getnameinfo - match inputs against expectation */ + +static int match_getnameinfo(const MOCK_EXPECT *expect, + const MOCK_EXPECT *inputs) +{ + struct getnameinfo_expectation *pe = + (struct getnameinfo_expectation *) expect; + struct getnameinfo_expectation *pi = + (struct getnameinfo_expectation *) inputs; + + return (eq_sockaddr((PTEST_CTX *) 0, (char *) 0, + pe->sa, pe->salen, pi->sa, pi->salen) + && pe->flags == pi->flags); +} + +/* assign_getnameinfo - assign expected output */ + +static void assign_getnameinfo(const MOCK_EXPECT *expect, void *targets) +{ + struct getnameinfo_expectation *pe = + (struct getnameinfo_expectation *) expect; + struct getnameinfo_targets *pt = + (struct getnameinfo_targets *) targets; + +#define MIN_OF(x,y) ((x) < (y) ? (x) : (y)) + + if (pe->retval == 0) { + if (pt->host && pe->host) { + strncpy(pt->host, pe->host, MIN_OF(pt->hostlen, pe->hostlen)); + pt->host[pt->hostlen - 1] = 0; + } + if (pt->serv && pe->serv) { + strncpy(pt->serv, pe->serv, MIN_OF(pt->servlen, pe->servlen)); + pt->serv[pt->servlen - 1] = 0; + } + } + *pt->retval = pe->retval; +} + +/* print_getnameinfo - print inputs */ + +static char *print_getnameinfo(const MOCK_EXPECT *expect, VSTRING *buf) +{ + struct getnameinfo_expectation *pe = + (struct getnameinfo_expectation *) expect; + VSTRING *sockaddr_buf = vstring_alloc(100); + VSTRING *flags_buf = vstring_alloc(100); + + vstring_sprintf(buf, "%s, %ld, (ptr), (len), (ptr), (len), %s", + sockaddr_to_string(sockaddr_buf, pe->sa, pe->salen), + (long) pe->salen, + ni_flags_to_string(flags_buf, pe->flags)); + vstring_free(sockaddr_buf); + vstring_free(flags_buf); + return (STR(buf)); +} + +/* free_getnameinfo - destructor */ + +static void free_getnameinfo(MOCK_EXPECT *expect) +{ + struct getnameinfo_expectation *pe = + (struct getnameinfo_expectation *) expect; + + if (pe->sa) + myfree(pe->sa); + if (pe->host) + myfree(pe->host); + if (pe->serv) + myfree(pe->serv); + pmock_expect_free(expect); +} + + /* + * The mock name and its helper callbacks in one place. + */ +static const MOCK_APPL_SIG getnameinfo_sig = { + "getnameinfo", + match_getnameinfo, + assign_getnameinfo, + print_getnameinfo, + free_getnameinfo, +}; + +/* _expect_getnameinfo - set up expectation */ + +void _expect_getnameinfo(const char *file, int line, + int calls_expected, int retval, + const struct sockaddr *sa, size_t salen, + const char *host, size_t hostlen, + const char *serv, size_t servlen, + int flags) +{ + struct getnameinfo_expectation *pe; + + pe = (struct getnameinfo_expectation *) + pmock_expect_create(&getnameinfo_sig, + file, line, calls_expected, sizeof(*pe)); + pe->retval = retval; + pe->sa = (struct sockaddr *) mymalloc(salen); + memcpy(pe->sa, sa, salen); + pe->salen = salen; + pe->host = MYSTRDUP_OR_NULL(host); + pe->hostlen = hostlen; + pe->serv = MYSTRDUP_OR_NULL(serv); + pe->servlen = servlen; + pe->flags = flags; +} + +/* getnameinfo - mock getnameinfo */ + +int getnameinfo(const struct sockaddr *sa, socklen_t salen, + char *host, size_t hostlen, + char *serv, size_t servlen, int flags) +{ + struct getnameinfo_expectation inputs; + struct getnameinfo_targets targets; + int retval = EAI_FAIL; + + /* + * Bundle the arguments to simplify handling. + */ + inputs.sa = (struct sockaddr *) sa; + inputs.salen = salen; + inputs.flags = flags; + + targets.host = host; + targets.hostlen = hostlen; + targets.serv = serv; + targets.servlen = servlen; + targets.retval = &retval; + + /* + * TODO: bail out if there was no match? + */ + (void) pmock_expect_apply(&getnameinfo_sig, + &inputs.mock_expect, (void *) &targets); + return (retval); +} diff --git a/postfix/src/testing/mock_getaddrinfo.h b/postfix/src/testing/mock_getaddrinfo.h new file mode 100644 index 000000000..b1333a4b7 --- /dev/null +++ b/postfix/src/testing/mock_getaddrinfo.h @@ -0,0 +1,73 @@ +#ifndef _MOCK_GETADDRINFO_H_INCLUDED_ +#define _MOCK_GETADDRINFO_H_INCLUDED_ + +/*++ +/* NAME +/* mock_getaddrinfo 3h +/* SUMMARY +/* getaddrinfo/getnameinfo mock for hermetic tests +/* SYNOPSIS +/* #include +/* DESCRIPTION +/* .nf + + /* + * System library. + */ +#include +#include + + /* + * Utility library. + */ +#include /* MAI_HOSTNAME_STR, etc. */ + + /* + * Test library. + */ +#include +#include +#include +#include +#include + + /* + * Manage expectations and responses. Capture the source file name and line + * number for better diagnostics. + */ +#define expect_getaddrinfo(exp_calls, retval, node, service, \ + hints, res) \ + _expect_getaddrinfo(__FILE__, __LINE__, (exp_calls), (retval), \ + (node), (service), \ + (hints), (res)) + +extern void _expect_getaddrinfo(const char *, int, int, int, + const char *, const char *, + const struct addrinfo *, + struct addrinfo *); + +#define expect_getnameinfo(exp_calls, retval, sa, salen, \ + host, hostlen, \ + serv, servlen, flags) \ + _expect_getnameinfo(__FILE__, __LINE__, (exp_calls), (retval), \ + (sa), (salen), \ + (host), (hostlen), \ + (serv), (servlen), (flags)) + +extern void _expect_getnameinfo(const char *, int, int, int, + const struct sockaddr *, size_t, + const char *, size_t, + const char *, size_t, int); + +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* Google, Inc. +/* 111 8th Avenue +/* New York, NY 10011, USA +/*--*/ + +#endif diff --git a/postfix/src/testing/mock_getaddrinfo_test.c b/postfix/src/testing/mock_getaddrinfo_test.c new file mode 100644 index 000000000..23c1b5653 --- /dev/null +++ b/postfix/src/testing/mock_getaddrinfo_test.c @@ -0,0 +1,220 @@ + /* + * Test program for the mock_getaddrinfo module. See comments in + * ptest_main.h and pmock_expect_test.c for a documented example. + */ + + /* + * System library. + */ +#include +#include + + /* + * Utility library. + */ +#include + + /* + * Test library. + */ +#include +#include +#include + +#define STR vstring_str + +typedef struct PTEST_CASE { + const char *testname; /* Human-readable description */ + void (*action) (PTEST_CTX *t, const struct PTEST_CASE *); +} PTEST_CASE; + +static void test_getaddrinfo_success(PTEST_CTX *t, const PTEST_CASE *unused) +{ + struct addrinfo hints; + struct addrinfo *got_addrinfo; + struct addrinfo *want_addrinfo; + int got_st, want_st = 0; + + /* + * Set up expectations. + */ + memset(&hints, 0, sizeof(hints)); + hints.ai_family = PF_INET; + hints.ai_socktype = SOCK_STREAM; + want_addrinfo = make_addrinfo(&hints, "localhost", "127.0.0.1", 25); + expect_getaddrinfo(1, want_st, "localhost", "smtp", &hints, want_addrinfo); + + /* + * Invoke the mock and verify results. + */ + got_st = getaddrinfo("localhost", "smtp", &hints, &got_addrinfo); + if (got_st != want_st) { + ptest_error(t, "getaddrinfo: got %d, want %d", got_st, want_st); + } else if (eq_addrinfo(t, "getaddrinfo", got_addrinfo, + want_addrinfo) == 0) { + VSTRING *got_buf = vstring_alloc(100); + VSTRING *want_buf = vstring_alloc(100); + + ptest_error(t, "getaddrinfo: got %s, want %s", + append_addrinfo_to_string(got_buf, got_addrinfo), + append_addrinfo_to_string(want_buf, want_addrinfo)); + vstring_free(got_buf); + vstring_free(want_buf); + } + + /* + * Clean up. + */ + freeaddrinfo(want_addrinfo); + if (got_addrinfo) + freeaddrinfo(got_addrinfo); +} + +static void test_getaddrinfo_failure(PTEST_CTX *t, const PTEST_CASE *unused) +{ + struct addrinfo hints; + struct addrinfo *got_addrinfo = 0; + struct addrinfo *want_addrinfo = 0; + int got_st, want_st = EAI_FAIL; + VSTRING *event_buf = vstring_alloc(100); + VSTRING *hints_buf = vstring_alloc(100); + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = PF_INET; + hints.ai_socktype = SOCK_STREAM; + + /* + * The missing expectation is intentional. Do not count this as an error. + */ + vstring_sprintf(event_buf, "unexpected call: " + "getaddrinfo(\"notexist\", \"smtp\", %s, (ptr))", + addrinfo_hints_to_string(hints_buf, &hints)); + expect_ptest_error(t, STR(event_buf)); + vstring_free(event_buf); + vstring_free(hints_buf); + + /* + * Invoke the mock and verify results. + */ + got_st = getaddrinfo("notexist", "smtp", &hints, &got_addrinfo); + if (got_st != want_st) { + ptest_error(t, "getaddrinfo: got %d, want %d", got_st, want_st); + } else if (eq_addrinfo(t, "getaddrinfo", got_addrinfo, + want_addrinfo) == 0) { + VSTRING *got_buf = vstring_alloc(100); + + ptest_error(t, "getaddrinfo: got %s, want (null)", + append_addrinfo_to_string(got_buf, got_addrinfo)); + vstring_free(got_buf); + } + + /* + * Clean up. + */ + if (got_addrinfo) + freeaddrinfo(got_addrinfo); +} + +static void test_getnameinfo_numeric_success(PTEST_CTX *t, + const PTEST_CASE *unused) +{ + struct sockaddr *req_sockaddr = make_sockaddr(AF_INET, "127.0.0.1", 25); + size_t req_sockaddrlen = sizeof(struct sockaddr_in); + int got_st, want_st = 0; + MAI_HOSTADDR_STR want_hostaddr = {"127.0.0.1"}; + MAI_SERVPORT_STR want_servport = {"25"}; + MAI_HOSTADDR_STR got_hostaddr; + MAI_SERVPORT_STR got_servport; + int req_flags = NI_NUMERICHOST | NI_NUMERICSERV; + + /* + * Set up expectations. + */ + expect_getnameinfo(1, want_st, req_sockaddr, req_sockaddrlen, + want_hostaddr.buf, sizeof(want_hostaddr), + want_servport.buf, sizeof(want_servport), + req_flags); + + /* + * Invoke the mock and verify results. + */ + got_st = getnameinfo(req_sockaddr, req_sockaddrlen, + got_hostaddr.buf, sizeof(got_hostaddr), + got_servport.buf, sizeof(got_servport), + req_flags); + + if (got_st != want_st) { + ptest_error(t, "getnameinfo: got %d, want %d", got_st, want_st); + } else if (strcmp(got_hostaddr.buf, want_hostaddr.buf) != 0) { + ptest_error(t, "getnameinfo hostaddr: got '%s', want '%s'", + got_hostaddr.buf, want_hostaddr.buf); + } else if (strcmp(got_servport.buf, want_servport.buf) != 0) { + ptest_error(t, "getnameinfo servport: got '%s', want '%s'", + got_servport.buf, want_servport.buf); + } + + /* + * Clean up. + */ + free_sockaddr(req_sockaddr); +} + +static void test_getnameinfo_numeric_failure(PTEST_CTX *t, + const PTEST_CASE *unused) +{ + struct sockaddr *req_sockaddr = make_sockaddr(AF_INET, "127.0.0.1", 25); + size_t req_sockaddrlen = sizeof(struct sockaddr_in); + int req_flags = NI_NUMERICHOST | NI_NUMERICSERV; + int got_st, want_st = EAI_FAIL; + VSTRING *event_buf = vstring_alloc(100); + VSTRING *ni_flags_buf = vstring_alloc(100); + + /* + * The missing expectation is intentional. Do not count this as an error. + */ + vstring_sprintf(event_buf, "unexpected call: " + "getnameinfo({AF_INET, 127.0.0.1, 25}, %ld, " + "(ptr), (len), (ptr), (len), %s", + (long) req_sockaddrlen, + ni_flags_to_string(ni_flags_buf, req_flags)); + expect_ptest_error(t, STR(event_buf)); + + /* + * Invoke the mock and verify results. + */ + got_st = getnameinfo(req_sockaddr, req_sockaddrlen, + (char *) 0, (size_t) 0, + (char *) 0, (size_t) 0, + req_flags); + if (got_st != want_st) + ptest_error(t, "getnameinfo return: got %d, want %d", got_st, want_st); + + /* + * Clean up. + */ + vstring_free(event_buf); + vstring_free(ni_flags_buf); + free_sockaddr(req_sockaddr); +} + + /* + * Test cases. The "success" tests exercise the expectation match and apply + * helpers, and "failure" tests exercise the print helpers. All tests + * exercise the expectation free helpers. + */ +const PTEST_CASE ptestcases[] = { + { + "getaddrinfo success", test_getaddrinfo_success, + }, + { + "getaddrinfo failure", test_getaddrinfo_failure, + }, + { + "getnameinfo_numeric success", test_getnameinfo_numeric_success, + }, + { + "getnameinfo_numeric failure", test_getnameinfo_numeric_failure, + }, +}; + +#include diff --git a/postfix/src/testing/mock_myaddrinfo.c b/postfix/src/testing/mock_myaddrinfo.c new file mode 100644 index 000000000..aeae3cd26 --- /dev/null +++ b/postfix/src/testing/mock_myaddrinfo.c @@ -0,0 +1,776 @@ +/*++ +/* NAME +/* mock_myaddrinfo 3 +/* SUMMARY +/* myaddrinfo mock for hermetic tests +/* SYNOPSIS +/* #include +/* +/* int hostname_to_sockaddr_pf( +/* const char *hostname, +/* int pf, +/* const char *service, +/* int socktype, +/* struct addrinfo **result, +/* +/* int hostaddr_to_sockaddr( +/* const char *hostaddr, +/* const char *service, +/* int socktype, +/* struct addrinfo **result) +/* +/* int sockaddr_to_hostaddr( +/* const struct sockaddr *sa, +/* SOCKADDR_SIZE salen, +/* MAI_HOSTADDR_STR *hostaddr, +/* MAI_SERVPORT_STR *portnum, +/* int socktype) +/* +/* int sockaddr_to_hostname( +/* const struct sockaddr *sa, +/* SOCKADDR_SIZE salen, +/* MAI_HOSTNAME_STR *hostname, +/* MAI_SERVNAME_STR *service, +/* int socktype) +/* EXPECTATION SETUP +/* void expect_hostname_to_sockaddr_pf( +/* int calls_expected, +/* int retval, +/* const char *hostname, +/* int pf, +/* const char *service, +/* int socktype, +/* struct addrinfo *result) +/* +/* void expect_hostaddr_to_sockaddr( +/* int calls_expected, +/* int retval, +/* const char *hostaddr, +/* const char *service, +/* int socktype, +/* struct addrinfo *result) +/* +/* void expect_sockaddr_to_hostaddr( +/* int calls_expected, +/* int retval, +/* const struct sockaddr *sa, +/* SOCKADDR_SIZE salen, +/* MAI_HOSTADDR_STR *hostaddr, +/* MAI_SERVPORT_STR *portnum, +/* int socktype) +/* +/* void expect_sockaddr_to_hostname( +/* int calls_expected, +/* int retval, +/* const struct sockaddr *sa, +/* SOCKADDR_SIZE salen, +/* MAI_HOSTNAME_STR *hostname, +/* MAI_SERVNAME_STR *service, +/* int socktype) +/* TEST DATA +/* struct addrinfo *make_addrinfo( +/* const struct addrinfo *hints, +/* const char *name, +/* const char *addr) +/* +/* struct addrinfo *copy_addrinfo(const struct addrinfo *ai) +/* +/* void freeaddrinfo(struct addrinfo *ai) +/* +/* struct sockaddr *make_sockaddr( +/* const char *addr, +/* int port) +/* +/* void free_sockaddr(struct sockaddr *sa) +/* MATCHERS +/* int eq_addrinfo( +/* PTEST_CTX *t, +/* const char *what, +/* struct addrinfo *got, +/* struct addrinfo *want) +/* DESCRIPTION +/* This module implements mock myaddrinfo() lookup and conversion +/* functions that produce prepared outputs in response to +/* expected inputs. This supports hermetic tests, i.e. tests +/* that do not depend on host configuration or on network +/* access. +/* +/* This module also provides a mock freeaddrinfo() function. +/* This is needed because the mock_myaddrinfo library and the +/* system library may use different memory allocation strategies. +/* +/* The "expect_" functions take expected inputs and corresponding +/* outputs. They make deep copies of their arguments, including +/* the "struct addrinfo *" linked lists. The retval argument +/* specifies a prepared result value. The calls_expected argument +/* specifies the expected number of calls (zero means one or +/* more calls, not: zero calls). +/* +/* make_addrinfo() creates one addrinfo structure. To create +/* linked list, manually append make_addrinfo() results. +/* +/* copy_addrinfo() makes a deep copy of a linked list of +/* addrinfo structures. +/* +/* freeaddrinfo() deletes a linked list of addrinfo structures. +/* This function must be used for addrinfo structures created +/* with make_addrinfo() and copy_addrinfo(). +/* +/* make_sockaddr() creates a sockaddr structure from the string +/* representation of an IP address. +/* +/* free_sockaddr() exists to make program code more explicit. +/* +/* eq_addrinfo() compares addrinfo linked lists and reports +/* differences with ptest_error(). The what argument provides +/* context. Specify a null test context for silent operation. +/* DIAGNOSTICS +/* If a mock is called unexpectedly (the call arguments do not +/* match the expectation, or more calls are made than expected), +/* a warning is logged, and the test will be flagged as failed. +/* For now the mock returns an error result to the caller. +/* TODO: consider aborting the test. +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* Google, Inc. +/* 111 8th Avenue +/* New York, NY 10011, USA +/*--*/ + + /* + * System library. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /* sprintf */ + + /* + * Utility library. + */ +#include +#include +#include + + /* + * Test library. + */ +#include +#include +#include +#include + +#define MYSTRDUP_OR_NULL(x) ((x) ? mystrdup(x) : 0) +#define STR_OR_NULL(s) ((s) ? (s) : "(null)") + + /* + * Manage hostname_to_sockaddr_pf() expectations and responses. We use this + * structure for deep copies pf expect_hostname_to_sockaddr_pf() expected + * inputs and prepared responses, and for shallow copies of + * hostname_to_sockaddr_pf() inputs, so that we can reuse the + * print_hostname_to_sockaddr_pf() helper. + */ +struct hostname_to_sockaddr_pf_expectation { + MOCK_EXPECT mock_expect; /* generic fields */ + int retval; /* result value */ + char *hostname; /* inputs */ + int pf; + char *service; + int socktype; + struct addrinfo *res; /* other outputs */ +}; + + /* + * Pointers to hostname_to_sockaddr_pf() outputs. + */ +struct hostname_to_sockaddr_pf_targets { + struct addrinfo **res; + int *retval; +}; + +/* match_hostname_to_sockaddr_pf - match inputs against expectation */ + +static int match_hostname_to_sockaddr_pf(const MOCK_EXPECT *expect, + const MOCK_EXPECT *inputs) +{ + struct hostname_to_sockaddr_pf_expectation *pe = + (struct hostname_to_sockaddr_pf_expectation *) expect; + struct hostname_to_sockaddr_pf_expectation *pi = + (struct hostname_to_sockaddr_pf_expectation *) inputs; + + return (strcmp(STR_OR_NULL(pe->hostname), + STR_OR_NULL(pi->hostname)) == 0 + && pe->pf == pi->pf + && strcmp(STR_OR_NULL(pe->service), + STR_OR_NULL(pi->service)) == 0 + && pe->socktype == pi->socktype); +} + +/* assign_hostname_to_sockaddr_pf - assign expected output */ + +static void assign_hostname_to_sockaddr_pf(const MOCK_EXPECT *expect, + void *targets) +{ + struct hostname_to_sockaddr_pf_expectation *pe = + (struct hostname_to_sockaddr_pf_expectation *) expect; + struct hostname_to_sockaddr_pf_targets *pt = + (struct hostname_to_sockaddr_pf_targets *) targets; + + if (pe->retval == 0) + *(pt->res) = copy_addrinfo(pe->res); + *pt->retval = pe->retval; +} + +/* print_hostname_to_sockaddr_pf - print expected inputs */ + +static char *print_hostname_to_sockaddr_pf(const MOCK_EXPECT *expect, + VSTRING *buf) +{ + struct hostname_to_sockaddr_pf_expectation *pe = + (struct hostname_to_sockaddr_pf_expectation *) expect; + + vstring_sprintf(buf, "\"%s\", %d, \"%s\", %d, (ptr)", + STR_OR_NULL(pe->hostname), pe->pf, + STR_OR_NULL(pe->service), pe->socktype); + return (vstring_str(buf)); +} + +/* free_hostname_to_sockaddr_pf - destructor */ + +static void free_hostname_to_sockaddr_pf(MOCK_EXPECT *expect) +{ + struct hostname_to_sockaddr_pf_expectation *pe = + (struct hostname_to_sockaddr_pf_expectation *) expect; + + if (pe->hostname) + myfree(pe->hostname); + if (pe->service) + myfree(pe->service); + if (pe->retval == 0) + freeaddrinfo(pe->res); + pmock_expect_free(expect); +} + + /* + * The mock name and its helper callbacks in one place. + */ +static const MOCK_APPL_SIG hostname_to_sockaddr_pf_sig = { + "hostname_to_sockaddr_pf", + match_hostname_to_sockaddr_pf, + assign_hostname_to_sockaddr_pf, + print_hostname_to_sockaddr_pf, + free_hostname_to_sockaddr_pf, +}; + +/* _expect_hostname_to_sockaddr_pf - set up expectation */ + +void _expect_hostname_to_sockaddr_pf(const char *file, int line, + int calls_expected, int retval, + const char *hostname, int pf, + const char *service, + int socktype, + struct addrinfo *res) +{ + struct hostname_to_sockaddr_pf_expectation *pe; + + pe = (struct hostname_to_sockaddr_pf_expectation *) + pmock_expect_create(&hostname_to_sockaddr_pf_sig, + file, line, calls_expected, sizeof(*pe)); + pe->retval = retval; + pe->hostname = MYSTRDUP_OR_NULL(hostname); + pe->pf = pf; + pe->service = MYSTRDUP_OR_NULL(service); + pe->socktype = socktype; + if (pe->retval == 0) + pe->res = copy_addrinfo(res); +} + +/* hostname_to_sockaddr_pf - mock hostname_to_sockaddr_pf */ + +int hostname_to_sockaddr_pf(const char *hostname, int pf, + const char *service, int socktype, + struct addrinfo **res) +{ + struct hostname_to_sockaddr_pf_expectation inputs; + struct hostname_to_sockaddr_pf_targets targets; + int retval = EAI_FAIL; + + /* + * Bundle the arguments to simplify handling. + */ + inputs.hostname = (char *) hostname; + inputs.pf = pf; + inputs.service = (char *) service; + inputs.socktype = socktype; + + targets.res = res; + targets.retval = &retval; + + /* + * TODO: bail out if there was no match? + */ + (void) pmock_expect_apply(&hostname_to_sockaddr_pf_sig, + &inputs.mock_expect, (void *) &targets); + return (retval); +} + + /* + * Manage hostaddr_to_sockaddr() expectations and responses. We use this + * structure for deep copies of expect_hostaddr_to_sockaddr() expected + * inputs and prepared responses, and for shallow copies + * hostaddr_to_sockaddr() inputs, so that we can reuse the + * print_hostaddr_to_sockaddr() helper. + */ +struct hostaddr_to_sockaddr_expectation { + MOCK_EXPECT mock_expect; /* generic fields */ + int retval; /* result value */ + char *hostaddr; /* inputs */ + char *service; + int socktype; + struct addrinfo *res; /* other outputs */ +}; + + /* + * Pointers to hostaddr_to_sockaddr() outputs. + */ +struct hostaddr_to_sockaddr_targets { + struct addrinfo **res; + int *retval; +}; + +/* match_hostaddr_to_sockaddr - match inputs against expectation */ + +static int match_hostaddr_to_sockaddr(const MOCK_EXPECT *expect, + const MOCK_EXPECT *inputs) +{ + struct hostaddr_to_sockaddr_expectation *pe = + (struct hostaddr_to_sockaddr_expectation *) expect; + struct hostaddr_to_sockaddr_expectation *pi = + (struct hostaddr_to_sockaddr_expectation *) inputs; + + return (strcmp(STR_OR_NULL(pe->hostaddr), + STR_OR_NULL(pi->hostaddr)) == 0 + && strcmp(STR_OR_NULL(pe->service), + STR_OR_NULL(pi->service)) == 0 + && pe->socktype == pi->socktype); +} + +/* assign_hostaddr_to_sockaddr - assign expected output */ + +static void assign_hostaddr_to_sockaddr(const MOCK_EXPECT *expect, + void *targets) +{ + struct hostaddr_to_sockaddr_expectation *pe = + (struct hostaddr_to_sockaddr_expectation *) expect; + struct hostaddr_to_sockaddr_targets *pt = + (struct hostaddr_to_sockaddr_targets *) targets; + + if (pe->retval == 0) + *(pt->res) = copy_addrinfo(pe->res); + *pt->retval = pe->retval; +} + +/* print_hostaddr_to_sockaddr - print expected inputs */ + +static char *print_hostaddr_to_sockaddr(const MOCK_EXPECT *expect, + VSTRING *buf) +{ + struct hostaddr_to_sockaddr_expectation *pe = + (struct hostaddr_to_sockaddr_expectation *) expect; + + vstring_sprintf(buf, "\"%s\", \"%s\", %d, (ptr)", + STR_OR_NULL(pe->hostaddr), + STR_OR_NULL(pe->service), pe->socktype); + return (vstring_str(buf)); +} + +/* free_hostname_to_sockaddr_pf - destructor */ + +static void free_hostaddr_to_sockaddr(MOCK_EXPECT *expect) +{ + struct hostaddr_to_sockaddr_expectation *pe = + (struct hostaddr_to_sockaddr_expectation *) expect; + + if (pe->hostaddr) + myfree(pe->hostaddr); + if (pe->service) + myfree(pe->service); + if (pe->retval == 0) + freeaddrinfo(pe->res); + pmock_expect_free(expect); +} + + /* + * The mock name and its helper callbacks in one place. + */ +static const MOCK_APPL_SIG hostaddr_to_sockaddr_sig = { + "hostaddr_to_sockaddr", + match_hostaddr_to_sockaddr, + assign_hostaddr_to_sockaddr, + print_hostaddr_to_sockaddr, + free_hostaddr_to_sockaddr, +}; + +/* _expect_hostaddr_to_sockaddr - set up expectation */ + +void _expect_hostaddr_to_sockaddr(const char *file, int line, + int calls_expected, int retval, + const char *hostaddr, + const char *service, int socktype, + struct addrinfo *res) +{ + struct hostaddr_to_sockaddr_expectation *pe; + + pe = (struct hostaddr_to_sockaddr_expectation *) + pmock_expect_create(&hostaddr_to_sockaddr_sig, + file, line, calls_expected, sizeof(*pe)); + pe->retval = retval; + pe->hostaddr = MYSTRDUP_OR_NULL(hostaddr); + pe->service = MYSTRDUP_OR_NULL(service); + pe->socktype = socktype; + if (pe->retval == 0) + pe->res = copy_addrinfo(res); +} + +/* hostaddr_to_sockaddr - mock hostaddr_to_sockaddr */ + +int hostaddr_to_sockaddr(const char *hostaddr, const char *service, + int socktype, struct addrinfo **res) +{ + struct hostaddr_to_sockaddr_expectation inputs; + struct hostaddr_to_sockaddr_targets targets; + int retval = EAI_FAIL; + + /* + * Bundle the arguments to simplify handling. + */ + inputs.hostaddr = (char *) hostaddr; + inputs.service = (char *) service; + inputs.socktype = socktype; + + targets.res = res; + targets.retval = &retval; + + /* + * TODO: bail out if there was no match? + */ + (void) pmock_expect_apply(&hostaddr_to_sockaddr_sig, + &inputs.mock_expect, (void *) &targets); + return (retval); +} + + /* + * Manage sockaddr_to_hostaddr() expectations and responses. We use this + * structure for deep copies of expect_sockaddr_to_hostaddr() expected + * inputs and prepared responses, and for shallow copies of + * sockaddr_to_hostaddr() inputs, so that we can reuse the + * print_sockaddr_to_hostaddr() helper. + */ +struct sockaddr_to_hostaddr_expectation { + MOCK_EXPECT mock_expect; /* generic fields */ + int retval; /* result value */ + struct sockaddr_storage sa; /* inputs */ + SOCKADDR_SIZE salen; + int socktype; + MAI_HOSTADDR_STR *hostaddr; /* other outputs */ + MAI_SERVPORT_STR *portnum; + MAI_HOSTADDR_STR hostaddr_storage; + MAI_SERVPORT_STR portnum_storage; +}; + + /* + * Pointers to sockaddr_to_hostaddr() outputs. + */ +struct sockaddr_to_hostaddr_targets { + MAI_HOSTADDR_STR *hostaddr; + MAI_SERVPORT_STR *portnum; + int *retval; +}; + +/* match_sockaddr_to_hostaddr - match inputs against expectation */ + +static int match_sockaddr_to_hostaddr(const MOCK_EXPECT *expect, + const MOCK_EXPECT *inputs) +{ + struct sockaddr_to_hostaddr_expectation *pe = + (struct sockaddr_to_hostaddr_expectation *) expect; + struct sockaddr_to_hostaddr_expectation *pi = + (struct sockaddr_to_hostaddr_expectation *) inputs; + + return (pe->salen == pi->salen + && memcmp(&pe->sa, &pi->sa, pe->salen) == 0 + && pe->socktype == pi->socktype); +} + +/* assign_sockaddr_to_hostaddr - assign expected output */ + +static void assign_sockaddr_to_hostaddr(const MOCK_EXPECT *expect, + void *targets) +{ + struct sockaddr_to_hostaddr_expectation *pe = + (struct sockaddr_to_hostaddr_expectation *) expect; + struct sockaddr_to_hostaddr_targets *pt = + (struct sockaddr_to_hostaddr_targets *) targets; + + if (pe->retval == 0) { + if (pe->hostaddr && pt->hostaddr) + *pt->hostaddr = *pe->hostaddr; + if (pe->portnum && pt->portnum) + *pt->portnum = *pe->portnum; + } + *pt->retval = pe->retval; +} + +/* print_sockaddr_to_hostaddr - print expected inputs */ + +static char *print_sockaddr_to_hostaddr(const MOCK_EXPECT *expect, + VSTRING *buf) +{ + struct sockaddr_to_hostaddr_expectation *pe = + (struct sockaddr_to_hostaddr_expectation *) expect; + VSTRING *sockaddr_buf = vstring_alloc(100); + + vstring_sprintf(buf, "%s, %ld, (ptr), (ptr)", + sockaddr_to_string(sockaddr_buf, + (struct sockaddr *) &pe->sa, + pe->salen), + (long) pe->salen); + vstring_free(sockaddr_buf); + return (vstring_str(buf)); +} + + /* + * The mock name and its helper callbacks in one place. + */ +static const MOCK_APPL_SIG sockaddr_to_hostaddr_sig = { + "sockaddr_to_hostaddr", + match_sockaddr_to_hostaddr, + assign_sockaddr_to_hostaddr, + print_sockaddr_to_hostaddr, + pmock_expect_free, +}; + +/* _expect_sockaddr_to_hostaddr - binary address to printable address form */ + +void _expect_sockaddr_to_hostaddr(const char *file, int line, + int calls_expected, int retval, + const struct sockaddr *sa, + SOCKADDR_SIZE salen, + MAI_HOSTADDR_STR *hostaddr, + MAI_SERVPORT_STR *portnum, + int socktype) +{ + struct sockaddr_to_hostaddr_expectation *pe; + + pe = (struct sockaddr_to_hostaddr_expectation *) + pmock_expect_create(&sockaddr_to_hostaddr_sig, + file, line, calls_expected, sizeof(*pe)); + pe->retval = retval; + memcpy((void *) &pe->sa, (void *) sa, salen); + pe->salen = salen; + if (pe->retval == 0 && hostaddr) { + *(pe->hostaddr = &pe->hostaddr_storage) = *hostaddr; + } else { + pe->hostaddr = 0; + } + if (pe->retval == 0 && portnum) { + *(pe->portnum = &pe->portnum_storage) = *portnum; + } else { + pe->portnum = 0; + } + pe->socktype = socktype; +} + +/* sockaddr_to_hostaddr - mock sockaddr_to_hostaddr */ + +int sockaddr_to_hostaddr(const struct sockaddr *sa, + SOCKADDR_SIZE salen, + MAI_HOSTADDR_STR *hostaddr, + MAI_SERVPORT_STR *portnum, + int socktype) +{ + struct sockaddr_to_hostaddr_expectation inputs; + struct sockaddr_to_hostaddr_targets targets; + int retval = EAI_FAIL; + + /* + * Bundle the arguments to simplify handling. + */ + memcpy((void *) &inputs.sa, (void *) sa, salen); + inputs.salen = salen; + inputs.socktype = socktype; + + targets.hostaddr = hostaddr; + targets.portnum = portnum; + targets.retval = &retval; + + /* + * TODO: bail out if there was no match? + */ + (void) pmock_expect_apply(&sockaddr_to_hostaddr_sig, + &inputs.mock_expect, (void *) &targets); + return (retval); +} + + /* + * Manage sockaddr_to_hostname() expectations and responses. We use this + * structure for deep copies of expect_sockaddr_to_hostname() expected + * inputs and prepared responses, and for shallow copies of + * sockaddr_to_hostname() inputs, so that we can reuse the + * print_sockaddr_to_hostname() helper. + */ +struct sockaddr_to_hostname_expectation { + MOCK_EXPECT mock_expect; /* generic fields */ + int retval; /* result value */ + struct sockaddr_storage sa; /* inputs */ + SOCKADDR_SIZE salen; + int socktype; + MAI_HOSTNAME_STR *hostname; /* other outputs */ + MAI_SERVNAME_STR *service; + MAI_HOSTNAME_STR hostname_storage; + MAI_SERVNAME_STR service_storage; +}; + + /* + * Pointers to sockaddr_to_hostname() outputs. + */ +struct sockaddr_to_hostname_targets { + MAI_HOSTNAME_STR *hostname; + MAI_SERVNAME_STR *service; + int *retval; +}; + +/* match_sockaddr_to_hostname - match inputs against expectation */ + +static int match_sockaddr_to_hostname(const MOCK_EXPECT *expect, + const MOCK_EXPECT *inputs) +{ + struct sockaddr_to_hostname_expectation *pe = + (struct sockaddr_to_hostname_expectation *) expect; + struct sockaddr_to_hostname_expectation *pi = + (struct sockaddr_to_hostname_expectation *) inputs; + + return (pe->salen == pi->salen + && memcmp(&pe->sa, &pi->sa, pe->salen) == 0 + && pe->socktype == pi->socktype); +} + +/* assign_sockaddr_to_hostname - assign expected output */ + +static void assign_sockaddr_to_hostname(const MOCK_EXPECT *expect, + void *targets) +{ + struct sockaddr_to_hostname_expectation *pe = + (struct sockaddr_to_hostname_expectation *) expect; + struct sockaddr_to_hostname_targets *pt = + (struct sockaddr_to_hostname_targets *) targets; + + if (pe->retval == 0) { + if (pe->hostname && pt->hostname) + *pt->hostname = *pe->hostname; + if (pe->service && pt->service) + *pt->service = *pe->service; + } + *pt->retval = pe->retval; +} + +/* print_sockaddr_to_hostname - print expected inputs */ + +static char *print_sockaddr_to_hostname(const MOCK_EXPECT *expect, + VSTRING *buf) +{ + struct sockaddr_to_hostname_expectation *pe = + (struct sockaddr_to_hostname_expectation *) expect; + VSTRING *sockaddr_buf = vstring_alloc(100); + + vstring_sprintf(buf, "%s, %ld, (ptr), (ptr)", + sockaddr_to_string(sockaddr_buf, + (struct sockaddr *) &pe->sa, + pe->salen), + (long) pe->salen); + vstring_free(sockaddr_buf); + return (vstring_str(buf)); +} + + /* + * The mock name and its helper callbacks in one place. + */ +static const MOCK_APPL_SIG sockaddr_to_hostname_sig = { + "sockaddr_to_hostname", + match_sockaddr_to_hostname, + assign_sockaddr_to_hostname, + print_sockaddr_to_hostname, + pmock_expect_free, +}; + +/* _expect_sockaddr_to_hostname - set up expectations */ + +void _expect_sockaddr_to_hostname(const char *file, int line, + int calls_expected, int retval, + const struct sockaddr *sa, + SOCKADDR_SIZE salen, + MAI_HOSTNAME_STR *hostname, + MAI_SERVNAME_STR *service, + int socktype) +{ + struct sockaddr_to_hostname_expectation *pe; + + pe = (struct sockaddr_to_hostname_expectation *) + pmock_expect_create(&sockaddr_to_hostname_sig, + file, line, calls_expected, sizeof(*pe)); + pe->retval = retval; + memcpy((void *) &pe->sa, (void *) sa, salen); + pe->salen = salen; + if (retval == 0 && hostname) { + *(pe->hostname = &pe->hostname_storage) = *hostname; + } else { + pe->hostname = 0; + } + if (retval == 0 && service) { + *(pe->service = &pe->service_storage) = *service; + } else { + pe->service = 0; + } + pe->socktype = socktype; +} + +/* sockaddr_to_hostname - mock sockaddr_to_hostname */ + +int sockaddr_to_hostname(const struct sockaddr *sa, + SOCKADDR_SIZE salen, + MAI_HOSTNAME_STR *hostname, + MAI_SERVNAME_STR *service, + int socktype) +{ + struct sockaddr_to_hostname_expectation inputs; + struct sockaddr_to_hostname_targets targets; + int retval = EAI_FAIL; + + /* + * Bundle the arguments to simplify handling. + */ + memcpy((void *) &inputs.sa, (void *) sa, salen); + inputs.salen = salen; + inputs.socktype = socktype; + + targets.hostname = hostname; + targets.service = service; + targets.retval = &retval; + + /* + * TODO: bail out if there was no match? + */ + (void) pmock_expect_apply(&sockaddr_to_hostname_sig, + &inputs.mock_expect, (void *) &targets); + return (retval); +} diff --git a/postfix/src/testing/mock_myaddrinfo.h b/postfix/src/testing/mock_myaddrinfo.h new file mode 100644 index 000000000..3d4311d03 --- /dev/null +++ b/postfix/src/testing/mock_myaddrinfo.h @@ -0,0 +1,86 @@ +#ifndef _MOCK_MYADDRINFO_H_INCLUDED_ +#define _MOCK_MYADDRINFO_H_INCLUDED_ + +/*++ +/* NAME +/* mock_myaddrinfo 3h +/* SUMMARY +/* myaddrinfo mock for hermetic tests +/* SYNOPSIS +/* #include +/* DESCRIPTION +/* .nf + + /* + * Utility library. + */ +#include + + /* + * Test library. + */ +#include +#include +#include +#include +#include + + /* + * Manage expectations and responses. Capture the source file name and line + * number for better diagnostics. + */ +#define expect_hostname_to_sockaddr_pf(exp_calls, retval, hostname, pf, \ + service, socktype, res) \ + _expect_hostname_to_sockaddr_pf(__FILE__, __LINE__, (exp_calls), \ + (retval), (hostname), (pf), \ + (service), (socktype), (res)) +#define expect_hostaddr_to_sockaddr(exp_calls, retval, hostaddr, service, \ + socktype, res) \ + _expect_hostaddr_to_sockaddr(__FILE__, __LINE__, (exp_calls), \ + (retval), \ + (hostaddr), (service), (socktype), \ + (res)) +#define expect_sockaddr_to_hostaddr(exp_calls, retval, sa, salen, hostaddr, \ + portnum, socktype) \ + _expect_sockaddr_to_hostaddr(__FILE__, __LINE__, (exp_calls), \ + (retval), (sa), (salen), (hostaddr), \ + (portnum), (socktype)) +#define expect_sockaddr_to_hostname(exp_calls, retval, sa, salen, hostname, \ + service, socktype) \ + _expect_sockaddr_to_hostname(__FILE__, __LINE__, (exp_calls), \ + (retval), (sa), (salen), (hostname), \ + (service), (socktype)) + +extern void _expect_hostname_to_sockaddr_pf(const char *, int, int, int, + const char *, int, const char *, + int, struct addrinfo *); +extern void _expect_hostaddr_to_sockaddr(const char *, int, int, int, + const char *, const char *, + int, struct addrinfo *); +extern void _expect_sockaddr_to_hostaddr(const char *, int, int, int, + const struct sockaddr *, + SOCKADDR_SIZE, + MAI_HOSTADDR_STR *, + MAI_SERVPORT_STR *, int); +extern void _expect_sockaddr_to_hostname(const char *, int, int, int, + const struct sockaddr *, + SOCKADDR_SIZE, + MAI_HOSTNAME_STR *, + MAI_SERVNAME_STR *, int); + +#define expect_hostname_to_sockaddr(count, ret, host, serv, sock, res) \ + expect_hostname_to_sockaddr_pf((count), (ret), (host), PF_UNSPEC, \ + (serv), (sock), (res)) + +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* Google, Inc. +/* 111 8th Avenue +/* New York, NY 10011, USA +/*--*/ + +#endif diff --git a/postfix/src/testing/mock_myaddrinfo_test.c b/postfix/src/testing/mock_myaddrinfo_test.c new file mode 100644 index 000000000..4d4131044 --- /dev/null +++ b/postfix/src/testing/mock_myaddrinfo_test.c @@ -0,0 +1,343 @@ + /* + * Test program to exercise mocks including logging. See comments in + * ptest_main.h and pmock_expect_test.c for a documented example. + */ + + /* + * System library. + */ +#include + + /* + * Utility library. + */ +#include +#include + + /* + * Test library. + */ +#include +#include +#include + +typedef struct PTEST_CASE { + const char *testname; /* Human-readable description */ + void (*action) (PTEST_CTX *t, const struct PTEST_CASE *); +} PTEST_CASE; + +static void test_hostname_to_sockaddr_success(PTEST_CTX *t, + const PTEST_CASE *unused) +{ + struct addrinfo hints; + struct addrinfo *got_addrinfo; + struct addrinfo *want_addrinfo; + int got_st, want_st = 0; + + /* + * Set up expectations. + */ + memset(&hints, 0, sizeof(hints)); + hints.ai_family = PF_INET; + hints.ai_socktype = SOCK_STREAM; + want_addrinfo = make_addrinfo(&hints, "localhost", "127.0.0.1", 25); + expect_hostname_to_sockaddr_pf(1, want_st, "localhost", PF_UNSPEC, "smtp", + SOCK_STREAM, want_addrinfo); + + /* + * Invoke the mock and verify results. + */ + got_st = hostname_to_sockaddr_pf("localhost", PF_UNSPEC, "smtp", + SOCK_STREAM, &got_addrinfo); + if (got_st != want_st) { + ptest_error(t, "hostname_to_sockaddr: got %d, want %d", got_st, want_st); + } else if (eq_addrinfo(t, "hostname_to_sockaddr", got_addrinfo, + want_addrinfo) == 0) { + ptest_error(t, "hostname_to_sockaddr: unexpected result mismatch"); + } + + /* + * Clean up. + */ + freeaddrinfo(want_addrinfo); + if (got_addrinfo) + freeaddrinfo(got_addrinfo); +} + +static void test_hostname_to_sockaddr_failure(PTEST_CTX *t, + const PTEST_CASE *unused) +{ + struct addrinfo *got_addrinfo = 0; + struct addrinfo *want_addrinfo = 0; + int got_st, want_st = EAI_FAIL; + + /* + * The missing expectation is intentional. Do not count this as an error. + */ + expect_ptest_error(t, "unexpected call: " + "hostname_to_sockaddr_pf(\"notexist\", 0, \"smtp\", 1, (ptr))"); + + /* + * Invoke the mock and verify results. + */ + got_st = hostname_to_sockaddr_pf("notexist", PF_UNSPEC, "smtp", + SOCK_STREAM, &got_addrinfo); + if (got_st != want_st) { + ptest_error(t, "hostname_to_sockaddr: got %d, want %d", got_st, want_st); + } else if (eq_addrinfo(t, "hostname_to_sockaddr", got_addrinfo, + want_addrinfo) == 0) { + ptest_error(t, "hostname_to_sockaddr: unexpected result mismatch"); + } + + /* + * Clean up. + */ + if (got_addrinfo) + freeaddrinfo(got_addrinfo); +} + +static void test_hostaddr_to_sockaddr_success(PTEST_CTX *t, + const PTEST_CASE *unused) +{ + struct addrinfo hints; + struct addrinfo *got_addrinfo = 0; + struct addrinfo *want_addrinfo; + int got_st, want_st = 0; + + /* + * Set up expectations. + */ + memset(&hints, 0, sizeof(hints)); + hints.ai_family = PF_INET; + hints.ai_socktype = SOCK_STREAM; + want_addrinfo = make_addrinfo(&hints, (char *) 0, "127.0.0.1", 25); + expect_hostaddr_to_sockaddr(1, want_st, "127.0.0.1", "25", SOCK_STREAM, + want_addrinfo); + + /* + * Invoke the mock and verify results. + */ + got_st = hostaddr_to_sockaddr("127.0.0.1", "25", SOCK_STREAM, + &got_addrinfo); + if (got_st != want_st) { + ptest_error(t, "hostaddr_to_sockaddr: got %d, want %d", got_st, want_st); + } else if (eq_addrinfo(t, "hostaddr_to_sockaddr", got_addrinfo, + want_addrinfo) == 0) { + ptest_error(t, "hostname_to_sockaddr: unexpected result mismatch"); + } + + /* + * Clean up. + */ + freeaddrinfo(want_addrinfo); + if (got_addrinfo) + freeaddrinfo(got_addrinfo); +} + +static void test_hostaddr_to_sockaddr_failure(PTEST_CTX *t, + const PTEST_CASE *unused) +{ + struct addrinfo *got_addrinfo = 0; + struct addrinfo *want_addrinfo = 0; + int got_st, want_st = EAI_FAIL; + + /* + * The missing expectation is intentional. Do not count this as an error. + */ + expect_ptest_error(t, "unexpected call: " + "hostaddr_to_sockaddr(\"127.0.0.1\", \"25\", " + "1, (ptr))"); + + /* + * Invoke the mock and verify results. + */ + got_st = hostaddr_to_sockaddr("127.0.0.1", "25", SOCK_STREAM, + &got_addrinfo); + if (got_st != want_st) { + ptest_error(t, "hostaddr_to_sockaddr: got %d, want %d", got_st, want_st); + } else if (eq_addrinfo(t, "hostaddr_to_sockaddr", got_addrinfo, + want_addrinfo) == 0) { + ptest_error(t, "hostname_to_sockaddr: unexpected result mismatch"); + } + + /* + * Clean up. + */ + if (got_addrinfo) + freeaddrinfo(got_addrinfo); +} + +static void test_sockaddr_to_hostaddr_success(PTEST_CTX *t, + const PTEST_CASE *unused) +{ + struct sockaddr *sa; + SOCKADDR_SIZE salen; + int got_st, want_st = 0; + MAI_HOSTADDR_STR want_hostaddr; + MAI_SERVPORT_STR want_portnum; + MAI_HOSTADDR_STR got_hostaddr; + MAI_SERVPORT_STR got_portnum; + + /* + * Set up expectations. + */ + sa = make_sockaddr(AF_INET, "127.0.0.1", 25); + salen = sizeof(struct sockaddr_in); + strncpy(want_hostaddr.buf, "127.0.0.1", sizeof(want_hostaddr.buf)); + strncpy(want_portnum.buf, "25", sizeof(want_portnum.buf)); + expect_sockaddr_to_hostaddr(1, want_st, sa, salen, + &want_hostaddr, &want_portnum, 0); + + /* + * Invoke the mock and verify results. + */ + got_st = sockaddr_to_hostaddr(sa, salen, &got_hostaddr, &got_portnum, 0); + if (got_st != want_st) { + ptest_error(t, "sockaddr_to_hostaddr ret: got %d, want %d", got_st, want_st); + } else if (strcmp(got_hostaddr.buf, want_hostaddr.buf) != 0) { + ptest_error(t, "sockaddr_to_hostaddr hostaddr.buf: got %s, want %s", + got_hostaddr.buf, want_hostaddr.buf); + } else if (strcmp(got_portnum.buf, want_portnum.buf) != 0) { + ptest_error(t, "sockaddr_to_hostaddr portnum.buf: got %s, want %s", + got_portnum.buf, want_portnum.buf); + } + + /* + * Clean up. + */ + free_sockaddr(sa); +} + +static void test_sockaddr_to_hostaddr_failure(PTEST_CTX *t, + const PTEST_CASE *unused) +{ + struct sockaddr *sa; + SOCKADDR_SIZE salen; + int got_st, want_st = EAI_FAIL; + + /* + * The missing expectation is intentional. Do not count this as an error. + */ + expect_ptest_error(t, "unexpected call: " + "sockaddr_to_hostaddr({AF_INET, 127.0.0.1, 25}, 16, " + "(ptr), (ptr))"); + + /* + * Invoke the mock and verify results. + */ + sa = make_sockaddr(AF_INET, "127.0.0.1", 25); + salen = sizeof(struct sockaddr_in); + got_st = sockaddr_to_hostaddr(sa, salen, (MAI_HOSTADDR_STR *) 0, + (MAI_SERVPORT_STR *) 0, 0); + if (got_st != want_st) + ptest_error(t, "sockaddr_to_hostaddr ret: got %d, want %d", got_st, want_st); + + /* + * Clean up. + */ + free_sockaddr(sa); +} + +static void test_sockaddr_to_hostname_success(PTEST_CTX *t, + const PTEST_CASE *unused) +{ + struct sockaddr *sa; + SOCKADDR_SIZE salen; + int got_st, want_st = 0; + MAI_HOSTNAME_STR want_hostname; + MAI_SERVNAME_STR want_service; + MAI_HOSTNAME_STR got_hostname; + MAI_SERVNAME_STR got_service; + + /* + * Set up expectations. + */ + sa = make_sockaddr(AF_INET, "127.0.0.1", 25); + salen = sizeof(struct sockaddr_in); + strncpy(want_hostname.buf, "localhost", sizeof(want_hostname.buf)); + strncpy(want_service.buf, "smtp", sizeof(want_service.buf)); + expect_sockaddr_to_hostname(1, want_st, sa, salen, &want_hostname, &want_service, 0); + + /* + * Invoke the mock and verify results. + */ + got_st = sockaddr_to_hostname(sa, salen, &got_hostname, &got_service, 0); + if (got_st != want_st) { + ptest_error(t, "sockaddr_to_hostname ret: got %d, want %d", got_st, want_st); + } else if (strcmp(got_hostname.buf, want_hostname.buf) != 0) { + ptest_error(t, "sockaddr_to_hostname hostname.buf: got %s, want %s", + got_hostname.buf, want_hostname.buf); + } else if (strcmp(got_service.buf, want_service.buf) != 0) { + ptest_error(t, "sockaddr_to_hostname service.buf: got %s, want %s", + got_service.buf, want_service.buf); + } + + /* + * Clean up. + */ + free_sockaddr(sa); +} + +static void test_sockaddr_to_hostname_failure(PTEST_CTX *t, + const PTEST_CASE *unused) +{ + struct sockaddr *sa; + SOCKADDR_SIZE salen; + int got_st, want_st = EAI_FAIL; + + /* + * The missing expectation is intentional. Do not count this as an error. + */ + expect_ptest_error(t, "unexpected call: " + "sockaddr_to_hostname({AF_INET, 127.0.0.1, 0}, 16, " + "(ptr), (ptr))"); + + /* + * Invoke the mock and verify results. + */ + sa = make_sockaddr(AF_INET, "127.0.0.1", 65536); + salen = sizeof(struct sockaddr_in); + got_st = sockaddr_to_hostname(sa, salen, (MAI_HOSTNAME_STR *) 0, + (MAI_SERVNAME_STR *) 0, 0); + if (got_st != want_st) + ptest_error(t, "sockaddr_to_hostname ret: got %d, want %d", got_st, want_st); + + /* + * Clean up. + */ + free_sockaddr(sa); +} + + /* + * Test cases. The "success" tests exercise the expectation match and apply + * helpers, and "failure" tests exercise the print helpers. All tests + * exercise the expectation free helpers. + */ +const PTEST_CASE ptestcases[] = { + { + "hostname_to_sockaddr success", test_hostname_to_sockaddr_success, + }, + { + "hostname_to_sockaddr failure", test_hostname_to_sockaddr_failure, + }, + { + "hostaddr_to_sockaddr success", test_hostaddr_to_sockaddr_success, + }, + { + "hostaddr_to_sockaddr failure", test_hostaddr_to_sockaddr_failure, + }, + { + "sockaddr_to_hostaddr success", test_sockaddr_to_hostaddr_success, + }, + { + "sockaddr_to_hostaddr failure", test_sockaddr_to_hostaddr_failure, + }, + { + "sockaddr_to_hostname success", test_sockaddr_to_hostname_success, + }, + { + "sockaddr_to_hostname failure", test_sockaddr_to_hostname_failure, + }, +}; + +#include diff --git a/postfix/src/testing/mock_servent.c b/postfix/src/testing/mock_servent.c new file mode 100644 index 000000000..599c24165 --- /dev/null +++ b/postfix/src/testing/mock_servent.c @@ -0,0 +1,474 @@ +/*++ +/* NAME +/* mock_servent 3 +/* SUMMARY +/* getservbyname mock for hermetic tests +/* SYNOPSIS +/* #include +/* +/* struct servent *getservbyname( +/* const char *name, +/* const char *proto) +/* +/* void setservent( +/* int stayopen) +/* +/* void endservent(void) +/* EXPECTATION SETUP +/* void expect_getservbyname( +/* int calls_expected, +/* int retval, +/* const char *name, +/* const char *proto) +/* +/* void expect_setservent( +/* int calls_expected, +/* int stayopen) +/* +/* void expect_endservent( +/* int calls_expected) +/* +/* struct servent *make_servent( +/* const char *name, +/* int port, +/* const char *proto) +/* +/* void free_servent( +/* struct servent *ent) +/* MATCHERS +/* int eq_servent( +/* PTEST_CTX *t, +/* const char *what, +/* struct servent *got, +/* struct servent *want) +/* DESCRIPTION +/* This module implements a partial mock getservent(3) module +/* that produces prepared outputs in response to expected +/* inputs. This supports hermetic tests, i.e. tests that do +/* not depend on host configuration or on network access. +/* +/* expect_getservbyname() makes deep copies of its input +/* arguments. The retval argument specifies a prepared result +/* value. The calls_expected argument specifies the expected +/* number of calls (zero means one or more calls, not: zero +/* calls). +/* +/* Note: getservbyname() maintains ownership of the struct +/* servent result that is returned by the mock getservbyname() +/* function. This is for consistency with the real getservbyname() +/* which also maintains ownership of the result. +/* +/* expect_setservent() copies its stayopen argument. The +/* calls_expected argument specifies the expected number of +/* calls (zero means one or more calls, not: zero calls). +/* +/* expect_endservent() has no expected inputs. The calls_expected +/* argument specifies the expected number of calls (zero means +/* one or more calls, not: zero calls). +/* +/* make_servent() returns a pointer to a minimal struct servent +/* instance. Use free_servent() to destroy it. +/* +/* eq_servent() is a predicate that compares its arguments for +/* equality. The what argument is used in logging when the +/* inputs differ. Specify a null test context for silent +/* operation. +/* DIAGNOSTICS +/* If a mock is called unexpectedly (the call arguments do not +/* match an expectation, or more calls are made than expected), +/* a warning is logged, and the test will be flagged as failed. +/* For now the mock returns an error result to the caller. +/* TODO: consider aborting the test. +/* SEE ALSO +/* dns_lookup(3), domain name service lookup +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* Google, Inc. +/* 111 8th Avenue +/* New York, NY 10011, USA +/*--*/ + + /* + * System library. + */ +#include +#include +#include + + /* + * Utility library. + */ +#include +#include + + /* + * Test library. + */ +#include +#include + + /* + * Helpers. + */ +#define MYSTRDUP_OR_NULL(x) ((x) ? mystrdup(x) : 0) +#define STR_OR_NULL(s) ((s) ? (s) : "(null)") + +#define STR(x) vstring_str(x) + +/* copy_servent - deep copy */ + +static struct servent *copy_servent(struct servent * src) +{ + struct servent *dst; + + dst = (struct servent *) mymalloc(sizeof(*dst)); + dst->s_name = MYSTRDUP_OR_NULL(src->s_name); + dst->s_aliases = mymalloc(sizeof(*dst->s_aliases)); + dst->s_aliases[0] = 0; + dst->s_port = src->s_port; + dst->s_proto = MYSTRDUP_OR_NULL(src->s_proto); + return (dst); +} + +/* make_servent - create mock servent instance */ + +struct servent *make_servent(const char *name, int port, const char *proto) +{ + struct servent *dst; + + dst = (struct servent *) mymalloc(sizeof(*dst)); + dst->s_name = MYSTRDUP_OR_NULL(name); + dst->s_aliases = mymalloc(sizeof(*dst->s_aliases)); + dst->s_aliases[0] = 0; + dst->s_port = htons(port); + dst->s_proto = MYSTRDUP_OR_NULL(proto); + return (dst); +} + +/* free_servent - destructor */ + +void free_servent(struct servent * ent) +{ + if (ent->s_name) + myfree(ent->s_name); + if (ent->s_aliases) + myfree((char *) ent->s_aliases); + if (ent->s_proto) + myfree(ent->s_proto); + myfree(ent); +} + +/* eq_aliases - equality predicate */ + +static int eq_aliases(PTEST_CTX *t, const char *file, int line, const char *what, + char **got, char **want) +{ + if (got[0] == 0 && want[0] == 0) + return (1); + if (got[0] == 0 || want[0] == 0) { + if (t) + ptest_error(t, "%s: got alias %s, want %s", + what, got[0] ? got[0] : "(null)", + want[0] ? want[0] : "(null)"); + return (0); + } + if (strcmp(got[0], want[0]) != 0) { + if (t) + ptest_error(t, "%s: got alias '%s', want '%s'", + what, got[0], want[0]); + return (0); + } + return (1); +} + +/* _eq_servent - equality predicate */ + +int _eq_servent(PTEST_CTX *t, const char *file, int line, + const char *what, + struct servent * got, struct servent * want) +{ + if (got == 0 && want == 0) + return (1); + if (got == 0 || want == 0) { + if (t) + ptest_error(t, "%s: got %s, want %s", + what, got ? "(struct servent *)" : "(null)", + want ? "(struct servent *)" : "(null)"); + return (0); + } + if (strcmp(got->s_name, want->s_name) != 0) { + if (t) + ptest_error(t, "%s: got name '%s', want '%s'", + what, got->s_name, want->s_name); + return (0); + } + if (!eq_aliases(t, file, line, what, got->s_aliases, want->s_aliases)) + return (0); + if (got->s_port != want->s_port) { + if (t) + ptest_error(t, "%s: got port %d, want %d", + what, ntohs(got->s_port), ntohs(want->s_port)); + return (0); + } + if (strcmp(got->s_proto, want->s_proto) != 0) { + if (t) + ptest_error(t, "%s: got proto '%s', want '%s'", + what, got->s_proto, want->s_proto); + return (0); + } + return (1); +} + + /* + * Manage getservbyname() expectations and responses. We use this structure + * for deep copies of expect_getservbyname() expected inputs and prepared + * responses, and for shallow copies of getservbyname() inputs. + */ +struct getservbyname_expectation { + MOCK_EXPECT mock_expect; /* generic fields */ + char *name; /* inputs */ + char *proto; + struct servent *retval; /* outputs */ +}; + + /* + * Pointers to getservbyname() outputs. + */ +struct getservbyname_targets { + struct servent **retval; +}; + +/* match_getservbyname - match inputs against expectation */ + +static int match_getservbyname(const MOCK_EXPECT *expect, + const MOCK_EXPECT *inputs) +{ + struct getservbyname_expectation *pe = + (struct getservbyname_expectation *) expect; + struct getservbyname_expectation *pi = + (struct getservbyname_expectation *) inputs; + + return (strcmp(STR_OR_NULL(pe->name), + STR_OR_NULL(pi->name)) == 0 + && strcmp(STR_OR_NULL(pe->proto), + STR_OR_NULL(pi->proto)) == 0); +} + +/* assign_getservbyname - assign expected output */ + +static void assign_getservbyname(const MOCK_EXPECT *expect, + void *targets) +{ + struct getservbyname_expectation *pe = + (struct getservbyname_expectation *) expect; + struct getservbyname_targets *pt = + (struct getservbyname_targets *) targets; + + /* Not a deep copy. */ + *pt->retval = pe->retval; +} + +/* print_getservbyname - print expected inputs */ + +static char *print_getservbyname(const MOCK_EXPECT *expect, + VSTRING *buf) +{ + struct getservbyname_expectation *pe = + (struct getservbyname_expectation *) expect; + + vstring_sprintf(buf, "\"%s\", \"%s\"", + STR_OR_NULL(pe->name), STR_OR_NULL(pe->proto)); + return (STR(buf)); +} + +/* free_getservbyname - destructor */ + +static void free_getservbyname(MOCK_EXPECT *expect) +{ + struct getservbyname_expectation *pe = + (struct getservbyname_expectation *) expect; + + if (pe->name) + myfree(pe->name); + if (pe->proto) + myfree(pe->proto); + if (pe->retval) + free_servent(pe->retval); + pmock_expect_free(expect); +} + + /* + * The mock name and its helper callbacks in one place. + */ +static const MOCK_APPL_SIG getservbyname_sig = { + "getservbyname", + match_getservbyname, + assign_getservbyname, + print_getservbyname, + free_getservbyname, +}; + +/* _expect_getservbyname - set up expectation */ + +void _expect_getservbyname(const char *file, int line, + int calls_expected, struct servent * retval, + const char *name, const char *proto) +{ + struct getservbyname_expectation *pe; + + pe = (struct getservbyname_expectation *) + pmock_expect_create(&getservbyname_sig, + file, line, calls_expected, sizeof(*pe)); + /* Inputs. */ + pe->name = MYSTRDUP_OR_NULL(name); + pe->proto = MYSTRDUP_OR_NULL(proto); + /* Outputs. */ + pe->retval = retval ? copy_servent(retval) : retval; +} + +/* getservbyname - answer the call with prepared responses */ + +struct servent *getservbyname(const char *name, const char *proto) +{ + struct getservbyname_expectation inputs; + struct getservbyname_targets targets; + struct servent *retval = 0; + + /* + * Bundle the arguments to simplify handling. + */ + inputs.name = (char *) name; + inputs.proto = (char *) proto; + + targets.retval = &retval; + + /* + * TODO: bail out if there was no match? + */ + (void) pmock_expect_apply(&getservbyname_sig, + &inputs.mock_expect, (void *) &targets); + return (retval); +} + + /* + * Manage setservent() expectations. This function has no outputs. + */ +struct setservent_expectation { + MOCK_EXPECT mock_expect; /* generic fields */ + int stayopen; /* input */ +}; + +static int match_setservent(const MOCK_EXPECT *expect, + const MOCK_EXPECT *inputs) +{ + struct setservent_expectation *pe = + (struct setservent_expectation *) expect; + struct setservent_expectation *pi = + (struct setservent_expectation *) inputs; + + return (pe->stayopen == pi->stayopen); +} + +/* print_setservent - print expected inputs */ + +static char *print_setservent(const MOCK_EXPECT *expect, + VSTRING *buf) +{ + struct setservent_expectation *pe = + (struct setservent_expectation *) expect; + + vstring_sprintf(buf, "%d", pe->stayopen); + return (STR(buf)); +} + + /* + * The mock name and its helper callbacks in one place. + */ +static const MOCK_APPL_SIG setservent_sig = { + "setservent", + match_setservent, + 0, /* no outputs to assign */ + print_setservent, + pmock_expect_free, +}; + +/* _expect_setservent - set up expectation */ + +void _expect_setservent(const char *file, int line, + int calls_expected, int stayopen) +{ + struct setservent_expectation *pe; + + pe = (struct setservent_expectation *) + pmock_expect_create(&setservent_sig, + file, line, calls_expected, sizeof(*pe)); + /* Inputs. */ + pe->stayopen = stayopen; +} + +/* setservent - answer the call */ + +void setservent(int stayopen) +{ + struct setservent_expectation inputs; + + /* + * Bundle the arguments to simplify handling. + */ + inputs.stayopen = stayopen; + + /* + * TODO: bail out if there was no match? + */ + (void) pmock_expect_apply(&setservent_sig, + &inputs.mock_expect, (void *) 0); +} + + /* + * Manage endservent() expectations. This function has no arguments (all + * calls will match), and no outputs. + */ + +/* print_endservent - print expected inputs */ + +static char *print_endservent(const MOCK_EXPECT *expect, + VSTRING *buf) +{ + VSTRING_RESET(buf); + VSTRING_TERMINATE(buf); + return (STR(buf)); +} + + /* + * The mock name and its helper callbacks in one place. + */ +static const MOCK_APPL_SIG endservent_sig = { + "endservent", + 0, /* no inputs to match */ + 0, /* no outputs to assign */ + print_endservent, + pmock_expect_free, +}; + +/* _expect_endservent - set up expectation */ + +void _expect_endservent(const char *file, int line, int calls_expected) +{ + (void) pmock_expect_create(&endservent_sig, + file, line, calls_expected, sizeof(MOCK_EXPECT)); +} + +/* endservent - return no answer */ + +void endservent(void) +{ + + /* + * TODO: bail out if there was no match? + */ + (void) pmock_expect_apply(&endservent_sig, (MOCK_EXPECT *) 0, (void *) 0); +} diff --git a/postfix/src/testing/mock_servent.h b/postfix/src/testing/mock_servent.h new file mode 100644 index 000000000..04b99dbce --- /dev/null +++ b/postfix/src/testing/mock_servent.h @@ -0,0 +1,75 @@ +#ifndef _MOCK_SERVENT_H_INCLUDED_ +#define _MOCK_SERVENT_H_INCLUDED_ + +/*++ +/* NAME +/* mock_servent 3h +/* SUMMARY +/* getservbyname mock for hermetic tests +/* SYNOPSIS +/* #include +/* DESCRIPTION +/* .nf + + /* + * System library. + */ +#include +#include + + /* + * Test library. + */ +#include + + /* + * Manage expectations and responses. Capture the source file name and line + * number for better diagnostics. + */ +#define expect_getservbyname(exp_calls, retval, name, proto) \ + _expect_getservbyname(__FILE__, __LINE__, (exp_calls), (retval), \ + (name), (proto)) + +extern void _expect_getservbyname(const char *, int, int, struct servent *, + const char *, const char *); + +#define expect_setservent(exp_calls, stayopen) \ + _expect_setservent(__FILE__, __LINE__, (exp_calls), (stayopen)) + +extern void _expect_setservent(const char *, int, int, int); + +#define expect_endservent(exp_calls) \ + _expect_endservent(__FILE__, __LINE__, (exp_calls)) + +extern void _expect_endservent(const char *, int, int); + + /* + * Matcher predicates. Capture the source file name and line number for + * better diagnostics. + */ +#define eq_servent(t, what, got, want) _eq_servent((t), __FILE__, __LINE__, \ + (what), (got), (want)) + +extern int _eq_servent(PTEST_CTX *, const char *, int, + const char *, struct servent *, + struct servent *); + + /* + * Helper to create test data. + */ +extern struct servent *make_servent(const char *, int, const char *); + +extern void free_servent(struct servent *); + +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* Google, Inc. +/* 111 8th Avenue +/* New York, NY 10011, USA +/*--*/ + +#endif diff --git a/postfix/src/testing/mock_servent_test.c b/postfix/src/testing/mock_servent_test.c new file mode 100644 index 000000000..29ffde89f --- /dev/null +++ b/postfix/src/testing/mock_servent_test.c @@ -0,0 +1,241 @@ + /* + * Test program to exercise mocks including logging. See pmock_expect_test.c + * and ptest_main.h for a documented example. + */ + + /* + * System library. + */ +#include +#include + + /* + * Utility library. + */ +#include + + /* + * Test library. + */ +#include +#include +#include + +typedef struct PTEST_CASE { + const char *testname; /* Human-readable description */ + void (*action) (PTEST_CTX *, const struct PTEST_CASE *); +} PTEST_CASE; + +static void test_getservbyname_success(PTEST_CTX *t, const PTEST_CASE *unused) +{ + struct servent *got_ent = 0, *want_ent; + + /* + * Set up expectations. + */ + want_ent = make_servent("smtp", 25, "tcp"); + expect_getservbyname(1, want_ent, "smtp", "tcp"); + + /* + * Invoke the mock and verify results. + */ + got_ent = getservbyname("smtp", "tcp"); + if (eq_servent(t, "getservbyname", got_ent, want_ent) == 0) + ptest_error(t, "getservbyname: unexpected result mismatch"); + + /* + * Clean up. + */ + free_servent(want_ent); +} + +static void test_getservbyname_notexist(PTEST_CTX *t, const PTEST_CASE *unused) +{ + struct servent *got_ent, *want_ent = 0; + + /* + * Set up expectations. + */ + expect_getservbyname(1, want_ent, "noservice", "noproto"); + + /* + * Invoke the mock and verify results. + */ + got_ent = getservbyname("noservice", "noproto"); + if (eq_servent(t, "getservbyname", got_ent, want_ent) == 0) + ptest_error(t, "getservbyname: unexpected result mismatch"); + + /* + * Clean up. + */ + if (got_ent) + free_servent(got_ent); +} + +static void test_getservbyname_unused(PTEST_CTX *t, const PTEST_CASE *unused) +{ + struct servent *want_ent = 0; + + /* + * Create an expectation, without calling it. I does not matter what the + * expectation is, so we use the one from test_getservbyname_notexist(). + */ + expect_getservbyname(1, want_ent, "noservice", "noproto"); + + /* + * We expect that there will be a 'missing call' error. If there is none + * then the test will fail. + */ + expect_ptest_error(t, "got 0 calls for getservbyname(\"noservice\", " + "\"noproto\"), want 1"); +} + +static void test_eq_servent_differ(PTEST_CTX *t, const PTEST_CASE *unused) +{ + + struct servent *got_ent, *want_ent = 0; + struct probes { + const char *name; + int port; + const char *proto; + const char *want_error; + }; + static struct probes probes[4] = { + {"abc", 42, "def"}, + {"cba", 42, "def", "eq_servent: got name 'cba', want 'abc'"}, + {"abc", 24, "def", "eq_servent: got port 24, want 42"}, + {"abc", 42, "fed", "eq_servent: got proto 'fed', want 'def'"}, + }; + struct probes *pp; + int want_eq; + + pp = probes; + want_ent = make_servent(pp->name, pp->port, pp->proto); + for (pp = probes; pp < probes + sizeof(probes) / sizeof(probes[0]); pp++) { + got_ent = make_servent(pp->name, pp->port, pp->proto); + want_eq = !pp->want_error; + if (pp->want_error) + expect_ptest_error(t, pp->want_error); + if (eq_servent(t, "eq_servent", got_ent, want_ent) != want_eq) + ptest_error(t, "unexpected eq_servent result mismatch"); + free_servent(got_ent); + } + free_servent(want_ent); +} + +static void test_setservent_match(PTEST_CTX *t, const PTEST_CASE *unused) +{ + + /* + * Set up expectations. + */ + expect_setservent(1, 1); + + /* + * Invoke the mock and verify results. + */ + setservent(1); +} + +static void test_setservent_nomatch(PTEST_CTX *t, const PTEST_CASE *unused) +{ + + /* + * Set up expectations. + */ + expect_setservent(1, 1); + + /* + * These errors are intentional. If they don't happen then the test + * fails. + */ + expect_ptest_error(t, "unexpected call: setservent(2)"); + expect_ptest_error(t, "got 0 calls for setservent(1), want 1"); + + /* + * Invoke the mock and verify results. + */ + setservent(2); +} + +static void test_endservent_match(PTEST_CTX *t, const PTEST_CASE *unused) +{ + + /* + * Set up expectations. + */ + expect_endservent(1); + + /* + * Invoke the mock and verify results. + */ + endservent(); +} + +static void test_endservent_unused(PTEST_CTX *t, const PTEST_CASE *unused) +{ + + /* + * Set up expectations without making a call. + */ + expect_endservent(1); + + /* + * This error is intentional. If it does not happen the test fails. + */ + expect_ptest_error(t, "got 0 calls for endservent(), want 1"); + + /* + * Verify results (this happens in the test infrastructure). + */ +} + + /* + * Test cases. The "success" tests exercise the expectation match and apply + * helpers, and "unused" tests exercise the print helpers. + */ +const PTEST_CASE ptestcases[] = { + + /* + * getservbyname() + */ + { + "test getservbyname success", test_getservbyname_success, + }, + { + "test getservbyname notexist", test_getservbyname_notexist, + }, + { + "test getservbyname unused", test_getservbyname_unused, + }, + + /* + * eq_servent() + */ + { + "test eq_servent differ", test_eq_servent_differ, + }, + + /* + * setservent() + */ + { + "test setservent match", test_setservent_match, + }, + { + "test setservent nomatch", test_setservent_nomatch, + }, + + /* + * endservent() + */ + { + "test endservent match", test_endservent_match, + }, + { + "test endservent unused", test_endservent_unused, + }, + +}; + +#include diff --git a/postfix/src/tls/Makefile.in b/postfix/src/tls/Makefile.in index 948afab9f..f06817d54 100644 --- a/postfix/src/tls/Makefile.in +++ b/postfix/src/tls/Makefile.in @@ -154,6 +154,7 @@ tls_bio_ops.o: ../../include/sys_defs.h tls_bio_ops.o: ../../include/vbuf.h tls_bio_ops.o: ../../include/vstream.h tls_bio_ops.o: ../../include/vstring.h +tls_bio_ops.o: ../../include/wrap_netdb.h tls_bio_ops.o: tls.h tls_bio_ops.o: tls_bio_ops.c tls_certkey.o: ../../include/argv.h @@ -169,6 +170,7 @@ tls_certkey.o: ../../include/sys_defs.h tls_certkey.o: ../../include/vbuf.h tls_certkey.o: ../../include/vstream.h tls_certkey.o: ../../include/vstring.h +tls_certkey.o: ../../include/wrap_netdb.h tls_certkey.o: tls.h tls_certkey.o: tls_certkey.c tls_client.o: ../../include/argv.h @@ -190,6 +192,7 @@ tls_client.o: ../../include/sys_defs.h tls_client.o: ../../include/vbuf.h tls_client.o: ../../include/vstream.h tls_client.o: ../../include/vstring.h +tls_client.o: ../../include/wrap_netdb.h tls_client.o: tls.h tls_client.o: tls_client.c tls_client.o: tls_mgr.h @@ -216,6 +219,7 @@ tls_dane.o: ../../include/timecmp.h tls_dane.o: ../../include/vbuf.h tls_dane.o: ../../include/vstream.h tls_dane.o: ../../include/vstring.h +tls_dane.o: ../../include/wrap_netdb.h tls_dane.o: tls.h tls_dane.o: tls_dane.c tls_dh.o: ../../include/argv.h @@ -233,6 +237,7 @@ tls_dh.o: ../../include/sys_defs.h tls_dh.o: ../../include/vbuf.h tls_dh.o: ../../include/vstream.h tls_dh.o: ../../include/vstring.h +tls_dh.o: ../../include/wrap_netdb.h tls_dh.o: tls.h tls_dh.o: tls_dh.c tls_fprint.o: ../../include/argv.h @@ -250,6 +255,7 @@ tls_fprint.o: ../../include/sys_defs.h tls_fprint.o: ../../include/vbuf.h tls_fprint.o: ../../include/vstream.h tls_fprint.o: ../../include/vstring.h +tls_fprint.o: ../../include/wrap_netdb.h tls_fprint.o: tls.h tls_fprint.o: tls_fprint.c tls_level.o: ../../include/argv.h @@ -263,6 +269,7 @@ tls_level.o: ../../include/sys_defs.h tls_level.o: ../../include/vbuf.h tls_level.o: ../../include/vstream.h tls_level.o: ../../include/vstring.h +tls_level.o: ../../include/wrap_netdb.h tls_level.o: tls.h tls_level.o: tls_level.c tls_mgr.o: ../../include/argv.h @@ -306,6 +313,7 @@ tls_misc.o: ../../include/valid_hostname.h tls_misc.o: ../../include/vbuf.h tls_misc.o: ../../include/vstream.h tls_misc.o: ../../include/vstring.h +tls_misc.o: ../../include/wrap_netdb.h tls_misc.o: tls.h tls_misc.o: tls_misc.c tls_prng_dev.o: ../../include/connect.h @@ -353,6 +361,7 @@ tls_proxy_client_misc.o: ../../include/sys_defs.h tls_proxy_client_misc.o: ../../include/vbuf.h tls_proxy_client_misc.o: ../../include/vstream.h tls_proxy_client_misc.o: ../../include/vstring.h +tls_proxy_client_misc.o: ../../include/wrap_netdb.h tls_proxy_client_misc.o: tls.h tls_proxy_client_misc.o: tls_proxy.h tls_proxy_client_misc.o: tls_proxy_client_misc.c @@ -374,6 +383,7 @@ tls_proxy_client_print.o: ../../include/sys_defs.h tls_proxy_client_print.o: ../../include/vbuf.h tls_proxy_client_print.o: ../../include/vstream.h tls_proxy_client_print.o: ../../include/vstring.h +tls_proxy_client_print.o: ../../include/wrap_netdb.h tls_proxy_client_print.o: tls.h tls_proxy_client_print.o: tls_proxy.h tls_proxy_client_print.o: tls_proxy_client_print.c @@ -395,6 +405,7 @@ tls_proxy_client_scan.o: ../../include/sys_defs.h tls_proxy_client_scan.o: ../../include/vbuf.h tls_proxy_client_scan.o: ../../include/vstream.h tls_proxy_client_scan.o: ../../include/vstring.h +tls_proxy_client_scan.o: ../../include/wrap_netdb.h tls_proxy_client_scan.o: tls.h tls_proxy_client_scan.o: tls_proxy.h tls_proxy_client_scan.o: tls_proxy_client_scan.c @@ -419,6 +430,7 @@ tls_proxy_clnt.o: ../../include/sys_defs.h tls_proxy_clnt.o: ../../include/vbuf.h tls_proxy_clnt.o: ../../include/vstream.h tls_proxy_clnt.o: ../../include/vstring.h +tls_proxy_clnt.o: ../../include/wrap_netdb.h tls_proxy_clnt.o: tls.h tls_proxy_clnt.o: tls_proxy.h tls_proxy_clnt.o: tls_proxy_clnt.c @@ -437,6 +449,7 @@ tls_proxy_context_print.o: ../../include/sys_defs.h tls_proxy_context_print.o: ../../include/vbuf.h tls_proxy_context_print.o: ../../include/vstream.h tls_proxy_context_print.o: ../../include/vstring.h +tls_proxy_context_print.o: ../../include/wrap_netdb.h tls_proxy_context_print.o: tls.h tls_proxy_context_print.o: tls_proxy.h tls_proxy_context_print.o: tls_proxy_context_print.c @@ -456,6 +469,7 @@ tls_proxy_context_scan.o: ../../include/sys_defs.h tls_proxy_context_scan.o: ../../include/vbuf.h tls_proxy_context_scan.o: ../../include/vstream.h tls_proxy_context_scan.o: ../../include/vstring.h +tls_proxy_context_scan.o: ../../include/wrap_netdb.h tls_proxy_context_scan.o: tls.h tls_proxy_context_scan.o: tls_proxy.h tls_proxy_context_scan.o: tls_proxy_context_scan.c @@ -474,6 +488,7 @@ tls_proxy_server_print.o: ../../include/sys_defs.h tls_proxy_server_print.o: ../../include/vbuf.h tls_proxy_server_print.o: ../../include/vstream.h tls_proxy_server_print.o: ../../include/vstring.h +tls_proxy_server_print.o: ../../include/wrap_netdb.h tls_proxy_server_print.o: tls.h tls_proxy_server_print.o: tls_proxy.h tls_proxy_server_print.o: tls_proxy_server_print.c @@ -492,6 +507,7 @@ tls_proxy_server_scan.o: ../../include/sys_defs.h tls_proxy_server_scan.o: ../../include/vbuf.h tls_proxy_server_scan.o: ../../include/vstream.h tls_proxy_server_scan.o: ../../include/vstring.h +tls_proxy_server_scan.o: ../../include/wrap_netdb.h tls_proxy_server_scan.o: tls.h tls_proxy_server_scan.o: tls_proxy.h tls_proxy_server_scan.o: tls_proxy_server_scan.c @@ -525,6 +541,7 @@ tls_seed.o: ../../include/sys_defs.h tls_seed.o: ../../include/vbuf.h tls_seed.o: ../../include/vstream.h tls_seed.o: ../../include/vstring.h +tls_seed.o: ../../include/wrap_netdb.h tls_seed.o: tls.h tls_seed.o: tls_mgr.h tls_seed.o: tls_scache.h @@ -548,6 +565,7 @@ tls_server.o: ../../include/sys_defs.h tls_server.o: ../../include/vbuf.h tls_server.o: ../../include/vstream.h tls_server.o: ../../include/vstring.h +tls_server.o: ../../include/wrap_netdb.h tls_server.o: tls.h tls_server.o: tls_mgr.h tls_server.o: tls_scache.h @@ -566,6 +584,7 @@ tls_session.o: ../../include/sys_defs.h tls_session.o: ../../include/vbuf.h tls_session.o: ../../include/vstream.h tls_session.o: ../../include/vstring.h +tls_session.o: ../../include/wrap_netdb.h tls_session.o: tls.h tls_session.o: tls_session.c tls_stream.o: ../../include/argv.h @@ -581,6 +600,7 @@ tls_stream.o: ../../include/sys_defs.h tls_stream.o: ../../include/vbuf.h tls_stream.o: ../../include/vstream.h tls_stream.o: ../../include/vstring.h +tls_stream.o: ../../include/wrap_netdb.h tls_stream.o: tls.h tls_stream.o: tls_stream.c tls_verify.o: ../../include/argv.h @@ -597,5 +617,6 @@ tls_verify.o: ../../include/sys_defs.h tls_verify.o: ../../include/vbuf.h tls_verify.o: ../../include/vstream.h tls_verify.o: ../../include/vstring.h +tls_verify.o: ../../include/wrap_netdb.h tls_verify.o: tls.h tls_verify.o: tls_verify.c diff --git a/postfix/src/tlsmgr/Makefile.in b/postfix/src/tlsmgr/Makefile.in index 8e7aab6b0..223d91da1 100644 --- a/postfix/src/tlsmgr/Makefile.in +++ b/postfix/src/tlsmgr/Makefile.in @@ -97,4 +97,5 @@ tlsmgr.o: ../../include/vstream.h tlsmgr.o: ../../include/vstring.h tlsmgr.o: ../../include/vstring_vstream.h tlsmgr.o: ../../include/warn_stat.h +tlsmgr.o: ../../include/wrap_netdb.h tlsmgr.o: tlsmgr.c diff --git a/postfix/src/tlsproxy/Makefile.in b/postfix/src/tlsproxy/Makefile.in index c72aa8128..8bb00459d 100644 --- a/postfix/src/tlsproxy/Makefile.in +++ b/postfix/src/tlsproxy/Makefile.in @@ -89,6 +89,7 @@ tlsproxy.o: ../../include/tls_proxy.h tlsproxy.o: ../../include/vbuf.h tlsproxy.o: ../../include/vstream.h tlsproxy.o: ../../include/vstring.h +tlsproxy.o: ../../include/wrap_netdb.h tlsproxy.o: tlsproxy.c tlsproxy.o: tlsproxy.h tlsproxy_state.o: ../../include/argv.h @@ -113,5 +114,6 @@ tlsproxy_state.o: ../../include/tls_proxy.h tlsproxy_state.o: ../../include/vbuf.h tlsproxy_state.o: ../../include/vstream.h tlsproxy_state.o: ../../include/vstring.h +tlsproxy_state.o: ../../include/wrap_netdb.h tlsproxy_state.o: tlsproxy.h tlsproxy_state.o: tlsproxy_state.c diff --git a/postfix/src/util/Makefile.in b/postfix/src/util/Makefile.in index cdc671cff..f234f40ce 100644 --- a/postfix/src/util/Makefile.in +++ b/postfix/src/util/Makefile.in @@ -34,7 +34,7 @@ SRCS = alldig.c allprint.c argv.c argv_split.c attr_clnt.c attr_print0.c \ allascii.c load_file.c killme_after.c vstream_tweak.c \ pass_trigger.c edit_file.c inet_windowsize.c \ unix_pass_fd_fix.c dict_cache.c valid_utf8_string.c dict_thash.c \ - ip_match.c nbbio.c base32_code.c dict_test.c \ + ip_match.c nbbio.c base32_code.c dict_cli.c \ dict_fail.c msg_rate_delay.c dict_surrogate.c warn_stat.c \ dict_sockmap.c line_number.c recv_pass_attr.c pass_accept.c \ poll_fd.c timecmp.c slmdb.c dict_pipe.c dict_random.c \ @@ -44,7 +44,8 @@ SRCS = alldig.c allprint.c argv.c argv_split.c attr_clnt.c attr_print0.c \ msg_logger.c logwriter.c unix_dgram_connect.c unix_dgram_listen.c \ byte_mask.c known_tcp_ports.c argv_split_at.c dict_stream.c \ sane_strtol.c hash_fnv.c ldseed.c mkmap_cdb.c mkmap_db.c mkmap_dbm.c \ - mkmap_fail.c mkmap_lmdb.c mkmap_open.c mkmap_sdbm.c + mkmap_fail.c mkmap_lmdb.c mkmap_open.c mkmap_sdbm.c \ + find_inet_service.c wrap_netdb.c OBJS = alldig.o allprint.o argv.o argv_split.o attr_clnt.o attr_print0.o \ attr_print64.o attr_print_plain.o attr_scan0.o attr_scan64.o \ attr_scan_plain.o auto_clnt.o base64_code.o basename.o binhash.o \ @@ -80,7 +81,7 @@ OBJS = alldig.o allprint.o argv.o argv_split.o attr_clnt.o attr_print0.o \ allascii.o load_file.o killme_after.o vstream_tweak.o \ pass_trigger.o edit_file.o inet_windowsize.o \ unix_pass_fd_fix.o dict_cache.o valid_utf8_string.o dict_thash.o \ - ip_match.o nbbio.o base32_code.o dict_test.o \ + ip_match.o nbbio.o base32_code.o dict_cli.o \ dict_fail.o msg_rate_delay.o dict_surrogate.o warn_stat.o \ dict_sockmap.o line_number.o recv_pass_attr.o pass_accept.o \ poll_fd.o timecmp.o $(NON_PLUGIN_MAP_OBJ) dict_pipe.o dict_random.o \ @@ -90,12 +91,16 @@ OBJS = alldig.o allprint.o argv.o argv_split.o attr_clnt.o attr_print0.o \ msg_logger.o logwriter.o unix_dgram_connect.o unix_dgram_listen.o \ byte_mask.o known_tcp_ports.o argv_split_at.o dict_stream.o \ sane_strtol.o hash_fnv.o ldseed.o mkmap_db.o mkmap_dbm.o \ - mkmap_fail.o mkmap_open.o + mkmap_fail.o mkmap_open.o find_inet_service.o wrap_netdb.o # MAP_OBJ is for maps that may be dynamically loaded with dynamicmaps.cf. # When hard-linking these, makedefs sets NON_PLUGIN_MAP_OBJ=$(MAP_OBJ), # otherwise it sets the PLUGIN_* macros. MAP_OBJ = dict_pcre.o dict_cdb.o dict_lmdb.o dict_sdbm.o slmdb.o \ mkmap_cdb.o mkmap_lmdb.o mkmap_sdbm.o +TEST_OBJ = dict_pipe_test.o dict_union_test.o find_inet_service_test.o \ + known_tcp_ports_test.o msg_output_test.o myaddrinfo_test.o \ + mymalloc_test.o mystrtok_test.o unescape_test.o hash_fnv_test.o \ + argv_test.o dict_stream_test.o HDRS = argv.h attr.h attr_clnt.h auto_clnt.h base64_code.h binhash.h \ chroot_uid.h cidr_match.h clean_env.h connect.h ctable.h dict.h \ dict_cdb.h dict_cidr.h dict_db.h dict_dbm.h dict_env.h dict_ht.h \ @@ -121,9 +126,14 @@ HDRS = argv.h attr.h attr_clnt.h auto_clnt.h base64_code.h binhash.h \ slmdb.h compat_va_copy.h dict_pipe.h dict_random.h \ valid_utf8_hostname.h midna_domain.h dict_union.h dict_inline.h \ check_arg.h argv_attr.h msg_logger.h logwriter.h byte_mask.h \ - known_tcp_ports.h sane_strtol.h hash_fnv.h ldseed.h mkmap.h + known_tcp_ports.h sane_strtol.h hash_fnv.h ldseed.h mkmap.h \ + find_inet_service.h wrap_netdb.h TESTSRC = fifo_open.c fifo_rdwr_bug.c fifo_rdonly_bug.c select_bug.c \ - stream_test.c dup2_pass_on_exec.c + stream_test.c dup2_pass_on_exec.c argv_test.c dict_pipe_test.c \ + dict_stream_test.c dict_cli.c dict_union_test.c \ + find_inet_service_test.c hash_fnv_test.c known_tcp_ports_test.c \ + msg_output_test.c myaddrinfo_test.c mymalloc_test.c mystrtok_test.c \ + unescape_test.c DEFS = -I. -D$(SYSTYPE) CFLAGS = $(DEBUG) $(OPT) $(DEFS) FILES = Makefile $(SRCS) $(HDRS) @@ -132,9 +142,9 @@ LIB = lib$(LIB_PREFIX)util$(LIB_SUFFIX) TESTPROG= dict_open dup2_pass_on_exec events exec_command fifo_open \ fifo_rdonly_bug fifo_rdwr_bug fifo_trigger fsspace fullname \ inet_addr_host inet_addr_local mac_parse make_dirs msg_syslog \ - mystrtok sigdelay translit valid_hostname vstream_popen \ + mystrtok_test sigdelay translit valid_hostname vstream_popen \ vstring vstring_vstream doze select_bug stream_test mac_expand \ - watchdog unescape hex_quote name_mask rand_sleep sane_time ctable \ + watchdog unescape_test hex_quote name_mask rand_sleep sane_time ctable \ inet_addr_list attr_print64 attr_scan64 base64_code attr_print0 \ attr_scan0 host_port attr_scan_plain attr_print_plain htable \ unix_recv_fd unix_send_fd stream_recv_fd stream_send_fd hex_code \ @@ -142,10 +152,13 @@ TESTPROG= dict_open dup2_pass_on_exec events exec_command fifo_open \ valid_utf8_string ip_match base32_code msg_rate_delay netstring \ vstream timecmp dict_cache midna_domain casefold strcasecmp_utf8 \ vbuf_print split_qnameval vstream msg_logger byte_mask \ - known_tcp_ports dict_stream find_inet binhash hash_fnv argv + known_tcp_ports_test dict_stream_test binhash hash_fnv_test \ + argv_test find_inet_service_test mymalloc_test msg_output_test \ + dict_union_test dict_pipe_test myaddrinfo_test PLUGIN_MAP_SO = $(LIB_PREFIX)pcre$(LIB_SUFFIX) $(LIB_PREFIX)lmdb$(LIB_SUFFIX) \ $(LIB_PREFIX)cdb$(LIB_SUFFIX) $(LIB_PREFIX)sdbm$(LIB_SUFFIX) HTABLE_FIX = NORANDOMIZE=1 +TEST_LIB= ../../lib/libtesting.a ../../lib/libptest.a LIB_DIR = ../../lib INC_DIR = ../../include @@ -153,7 +166,7 @@ INC_DIR = ../../include all: $(LIB) $(PLUGIN_MAP_SO_MAKE) $(PLUGIN_MAP_OBJ) -$(OBJS) $(PLUGIN_MAP_OBJ): ../../conf/makedefs.out +$(OBJS) $(PLUGIN_MAP_OBJ) $(TEST_OBJ): ../../conf/makedefs.out Makefile: Makefile.in cat ../../conf/makedefs.out $? >$@ @@ -293,11 +306,6 @@ sigdelay: $(LIB) $(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(SYSLIBS) mv junk $@.o -mystrtok: $(LIB) - mv $@.o junk - $(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(SYSLIBS) - mv junk $@.o - fifo_rdwr_bug: fifo_rdwr_bug.c $(LIB) $(CC) $(CFLAGS) -o $@ $@.c $(LIB) $(SYSLIBS) @@ -357,11 +365,6 @@ watchdog: $(LIB) $(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(SYSLIBS) mv junk $@.o -unescape: $(LIB) - mv $@.o junk - $(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(SYSLIBS) - mv junk $@.o - hex_quote: $(LIB) mv $@.o junk $(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(SYSLIBS) @@ -497,12 +500,7 @@ sane_basename: $(LIB) $(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(SYSLIBS) mv junk $@.o -find_inet: $(LIB) - mv $@.o junk - $(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(SYSLIBS) - mv junk $@.o - -stream_test: stream_test.c $(LIB) +test_sunos5_stream: test_sunos5_stream.c $(LIB) $(CC) $(CFLAGS) -o $@ $@.c $(LIB) $(SYSLIBS) gcctest: gccw.c gccw.ref @@ -581,10 +579,11 @@ split_qnameval: $(LIB) $(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(SYSLIBS) mv junk $@.o -known_tcp_ports: $(LIB) - mv $@.o junk - $(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(SYSLIBS) - mv junk $@.o +known_tcp_ports_test: known_tcp_ports_test.o $(TEST_LIB) $(LIB) + $(CC) $(CFLAGS) -o $@ $@.o $(TEST_LIB) $(LIB) $(SYSLIBS) + +test_known_tcp_ports: update known_tcp_ports_test + $(SHLIB_ENV) ${VALGRIND} ./known_tcp_ports_test dict_stream: $(LIB) mv $@.o junk @@ -596,23 +595,23 @@ argv: $(LIB) $(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(SYSLIBS) mv junk $@.o -tests: all valid_hostname_test mac_expand_test dict_test unescape_test \ +tests: all valid_hostname_test mac_expand_test dict_test test_unescape \ hex_quote_test ctable_test inet_addr_list_test base64_code_test \ attr_scan64_test attr_scan0_test host_port_test dict_tests \ - attr_scan_plain_test htable_test hex_code_test myaddrinfo_test \ + attr_scan_plain_test htable_test hex_code_test test_myaddrinfo\ format_tv_test ip_match_test name_mask_tests base32_code_test \ surrogate_test timecmp_test midna_domain_test casefold_test \ strcasecmp_utf8_test vbuf_print_test miss_endif_cidr_test \ miss_endif_regexp_test split_qnameval_test vstring_test \ - vstream_test byte_mask_tests mystrtok_test known_tcp_ports_test \ - binhash_test argv_test + vstream_test byte_mask_tests test_mystrtok test_known_tcp_ports \ + binhash_test test_argv test_find_inet_service test_mymalloc dict_tests: all dict_test \ dict_pcre_tests dict_cidr_test dict_thash_test dict_static_test \ - dict_inline_test dict_utf8_test dict_regexp_test dict_union_test \ - dict_pipe_test dict_regexp_file_test dict_cidr_file_test \ + dict_inline_test dict_utf8_test dict_regexp_test test_dict_union \ + test_dict_pipe dict_regexp_file_test dict_cidr_file_test \ dict_static_file_test dict_random_test dict_random_file_test \ - dict_inline_file_test dict_stream_test dict_inline_regexp_test \ + dict_inline_file_test test_dict_stream dict_inline_regexp_test \ dict_inline_cidr_test dict_pcre_tests: dict_pcre_test miss_endif_pcre_test dict_pcre_file_test \ @@ -630,12 +629,11 @@ mac_expand_test: mac_expand mac_expand.in mac_expand.ref diff mac_expand.ref mac_expand.tmp rm -f mac_expand.tmp -unescape_test: unescape unescape.in unescape.ref - $(SHLIB_ENV) ${VALGRIND} ./unescape unescape.tmp - diff -b unescape.ref unescape.tmp -# $(SHLIB_ENV) ${VALGRIND} ./unescape unescape.tmp -# diff unescape.in unescape.tmp - rm -f unescape.tmp +unescape_test: unescape_test.o $(TEST_LIB) $(LIB) + $(CC) $(CFLAGS) -o $@ $@.o $(TEST_LIB) $(LIB) $(SYSLIBS) + +test_unescape: update unescape_test + $(SHLIB_ENV) ${VALGRIND} ./unescape_test hex_quote_test: hex_quote $(SHLIB_ENV) ${VALGRIND} ./hex_quote hex_quote.tmp @@ -767,8 +765,12 @@ htable_test: htable /usr/share/dict/words binhash_test: binhash /usr/share/dict/words $(SHLIB_ENV) ${VALGRIND} ./binhash < /usr/share/dict/words -hash_fnv_test: hash_fnv - $(SHLIB_ENV) ${VALGRIND} ./hash_fnv +hash_fnv_test: hash_fnv_test.o $(TEST_LIB) $(LIB) + $(CC) $(CFLAGS) -o $@ $@.o $(LIB_DIR)/mock_getaddrinfo.o \ + $(TEST_LIB) $(LIB) $(SYSLIBS) + +test_hash_fnv: update hash_fnv_test + $(SHLIB_ENV) ${VALGRIND} ./hash_fnv_test hex_code_test: hex_code $(SHLIB_ENV) ${VALGRIND} ./hex_code @@ -776,20 +778,13 @@ hex_code_test: hex_code timecmp_test: timecmp $(SHLIB_ENV) ${VALGRIND} ./timecmp -myaddrinfo_test: myaddrinfo myaddrinfo.ref myaddrinfo.ref2 - $(SHLIB_ENV) ${VALGRIND} ./myaddrinfo all belly.porcupine.org 168.100.3.2 >myaddrinfo.tmp 2>&1 - diff myaddrinfo.ref myaddrinfo.tmp - rm -f myaddrinfo.tmp - $(SHLIB_ENV) ${VALGRIND} ./myaddrinfo all null.porcupine.org 10.0.0.0 >myaddrinfo.tmp 2>&1 - diff myaddrinfo.ref2 myaddrinfo.tmp - rm -f myaddrinfo.tmp - -myaddrinfo4_test: myaddrinfo4 myaddrinfo4.ref myaddrinfo4.ref2 - $(SHLIB_ENV) ${VALGRIND} ./myaddrinfo4 all belly.porcupine.org 168.100.3.2 >myaddrinfo4.tmp 2>&1 - diff myaddrinfo4.ref myaddrinfo4.tmp - $(SHLIB_ENV) ${VALGRIND} ./myaddrinfo4 all null.porcupine.org 10.0.0.0 >myaddrinfo4.tmp 2>&1 - diff myaddrinfo4.ref2 myaddrinfo4.tmp - rm -f myaddrinfo4.tmp +myaddrinfo_test: myaddrinfo_test.o $(LIB_DIR)/mock_getaddrinfo.o \ + $(TEST_LIB) $(LIB) + $(CC) $(CFLAGS) -o $@ $@.o $(LIB_DIR)/mock_getaddrinfo.o \ + $(TEST_LIB) $(LIB) $(SYSLIBS) + +test_myaddrinfo: update myaddrinfo_test + $(SHLIB_ENV) ${VALGRIND} ./myaddrinfo_test format_tv_test: format_tv format_tv.in format_tv.ref $(SHLIB_ENV) ${VALGRIND} ./format_tv format_tv.tmp @@ -1002,15 +997,17 @@ vbuf_print_test: vbuf_print vbuf_print_test.in vbuf_print_test.ref diff vbuf_print_test.ref vbuf_print_test.tmp rm -f vbuf_print_test.tmp -dict_union_test: dict_open dict_union_test.in dict_union_test.ref - $(SHLIB_ENV) ${VALGRIND} sh -x dict_union_test.in >dict_union_test.tmp 2>&1 - diff dict_union_test.ref dict_union_test.tmp - rm -f dict_union_test.tmp +dict_union_test: dict_union_test.o $(TEST_LIB) $(LIB) + $(CC) $(CFLAGS) -o $@ $@.o $(TEST_LIB) $(LIB) $(SYSLIBS) + +test_dict_union: update dict_union_test + $(SHLIB_ENV) ${VALGRIND} ./dict_union_test -dict_pipe_test: dict_open dict_pipe_test.in dict_pipe_test.ref - $(SHLIB_ENV) ${VALGRIND} sh -x dict_pipe_test.in >dict_pipe_test.tmp 2>&1 - diff dict_pipe_test.ref dict_pipe_test.tmp - rm -f dict_pipe_test.tmp +dict_pipe_test: dict_pipe_test.o $(TEST_LIB) $(LIB) + $(CC) $(CFLAGS) -o $@ $@.o $(TEST_LIB) $(LIB) $(SYSLIBS) + +test_dict_pipe: update dict_pipe_test + $(SHLIB_ENV) ${VALGRIND} ./dict_pipe_test vstring_test: dict_open vstring vstring_test.ref $(SHLIB_ENV) ${VALGRIND} ./vstring one two three >vstring_test.tmp 2>&1 @@ -1022,20 +1019,17 @@ vstream_test: vstream vstream_test.in vstream_test.ref diff vstream_test.ref vstream_test.tmp rm -f vstream_test.tmp -mystrtok_test: mystrtok mystrtok.ref - $(SHLIB_ENV) ${VALGRIND} ./mystrtok >mystrtok.tmp 2>&1 - diff mystrtok.ref mystrtok.tmp - rm -f mystrtok.tmp +mystrtok_test: mystrtok_test.o $(TEST_LIB) $(LIB) + $(CC) $(CFLAGS) -o $@ $@.o $(TEST_LIB) $(LIB) $(SYSLIBS) -known_tcp_ports_test: known_tcp_ports known_tcp_ports.ref - $(SHLIB_ENV) ${VALGRIND} ./known_tcp_ports >known_tcp_ports.tmp 2>&1 - diff known_tcp_ports.ref known_tcp_ports.tmp - rm -f known_tcp_ports.tmp +test_mystrtok: update mystrtok_test + $(SHLIB_ENV) ${VALGRIND} ./mystrtok_test -dict_stream_test: dict_stream dict_stream.ref - $(SHLIB_ENV) ${VALGRIND} ./dict_stream >dict_stream.tmp 2>&1 - diff dict_stream.ref dict_stream.tmp - rm -f dict_stream.tmp +dict_stream_test: dict_stream_test.o $(TEST_LIB) $(LIB) + $(CC) $(CFLAGS) -o $@ $@.o $(TEST_LIB) $(LIB) $(SYSLIBS) + +test_dict_stream: update dict_stream_test + $(SHLIB_ENV) ${VALGRIND} ./dict_stream_test dict_inline_pcre_test: dict_open dict_inline_pcre.ref (echo get foo; echo get bar) | \ @@ -1059,13 +1053,31 @@ dict_inline_cidr_test: dict_open dict_inline_cidr.ref diff dict_inline_cidr.ref dict_inline_cidr.tmp rm -f dict_inline_cidr.tmp -find_inet_test: find_inet find_inet.ref - $(SHLIB_ENV) ${VALGRIND} ./find_inet >find_inet.tmp 2>&1 - diff find_inet.ref find_inet.tmp - rm -f find_inet.tmp - -argv_test: argv - $(SHLIB_ENV) ${VALGRIND} ./argv +find_inet_service_test: find_inet_service_test.o $(LIB_DIR)/mock_servent.o \ + $(TEST_LIB) $(LIB) + $(CC) $(CFLAGS) -o $@ $@.o find_inet_service.o \ + $(LIB_DIR)/mock_servent.o $(TEST_LIB) $(LIB) $(SYSLIBS) + +test_find_inet_service: update find_inet_service_test + $(SHLIB_ENV) ${VALGRIND} ./find_inet_service_test + +mymalloc_test: mymalloc_test.o $(TEST_LIB) $(LIB) + $(CC) $(CFLAGS) -o $@ $@.o $(TEST_LIB) $(LIB) $(SYSLIBS) + +test_mymalloc: update mymalloc_test + $(SHLIB_ENV) ${VALGRIND} ./mymalloc_test + +msg_output_test: msg_output_test.o $(TEST_LIB) $(LIB) + $(CC) $(CFLAGS) -o $@ $@.o $(TEST_LIB) $(LIB) $(SYSLIBS) + +test_msg_output: update msg_output_test + $(SHLIB_ENV) ${VALGRIND} ./msg_output_test + +argv_test: argv_test.o $(TEST_LIB) $(LIB) + $(CC) $(CFLAGS) -o $@ $@.o $(TEST_LIB) $(LIB) $(SYSLIBS) + +test_argv: update argv_test + $(SHLIB_ENV) ${VALGRIND} ./argv_test depend: $(MAKES) (sed '1,/^# do not edit/!d' Makefile.in; \ @@ -1160,6 +1172,20 @@ argv_splitq.o: stringops.h argv_splitq.o: sys_defs.h argv_splitq.o: vbuf.h argv_splitq.o: vstring.h +argv_test.o: ../../include/pmock_expect.h +argv_test.o: ../../include/ptest.h +argv_test.o: ../../include/ptest_main.h +argv_test.o: argv.h +argv_test.o: argv_test.c +argv_test.o: check_arg.h +argv_test.o: msg.h +argv_test.o: msg_output.h +argv_test.o: msg_vstream.h +argv_test.o: stringops.h +argv_test.o: sys_defs.h +argv_test.o: vbuf.h +argv_test.o: vstream.h +argv_test.o: vstring.h attr_clnt.o: attr.h attr_clnt.o: attr_clnt.c attr_clnt.o: attr_clnt.h @@ -1328,6 +1354,7 @@ cidr_match.o: stringops.h cidr_match.o: sys_defs.h cidr_match.o: vbuf.h cidr_match.o: vstring.h +cidr_match.o: wrap_netdb.h clean_env.o: argv.h clean_env.o: clean_env.c clean_env.o: clean_env.h @@ -1432,6 +1459,23 @@ dict_cidr.o: vbuf.h dict_cidr.o: vstream.h dict_cidr.o: vstring.h dict_cidr.o: warn_stat.h +dict_cidr.o: wrap_netdb.h +dict_cli.o: argv.h +dict_cli.o: check_arg.h +dict_cli.o: dict.h +dict_cli.o: dict_cli.c +dict_cli.o: dict_db.h +dict_cli.o: dict_lmdb.h +dict_cli.o: mkmap.h +dict_cli.o: msg.h +dict_cli.o: msg_vstream.h +dict_cli.o: myflock.h +dict_cli.o: stringops.h +dict_cli.o: sys_defs.h +dict_cli.o: vbuf.h +dict_cli.o: vstream.h +dict_cli.o: vstring.h +dict_cli.o: vstring_vstream.h dict_db.o: argv.h dict_db.o: check_arg.h dict_db.o: dict.h @@ -1644,6 +1688,24 @@ dict_pipe.o: sys_defs.h dict_pipe.o: vbuf.h dict_pipe.o: vstream.h dict_pipe.o: vstring.h +dict_pipe_test.o: ../../include/pmock_expect.h +dict_pipe_test.o: ../../include/ptest.h +dict_pipe_test.o: ../../include/ptest_main.h +dict_pipe_test.o: argv.h +dict_pipe_test.o: check_arg.h +dict_pipe_test.o: dict.h +dict_pipe_test.o: dict_pipe.h +dict_pipe_test.o: dict_pipe_test.c +dict_pipe_test.o: msg.h +dict_pipe_test.o: msg_output.h +dict_pipe_test.o: msg_vstream.h +dict_pipe_test.o: myflock.h +dict_pipe_test.o: mymalloc.h +dict_pipe_test.o: stringops.h +dict_pipe_test.o: sys_defs.h +dict_pipe_test.o: vbuf.h +dict_pipe_test.o: vstream.h +dict_pipe_test.o: vstring.h dict_random.o: argv.h dict_random.o: check_arg.h dict_random.o: dict.h @@ -1735,6 +1797,22 @@ dict_stream.o: sys_defs.h dict_stream.o: vbuf.h dict_stream.o: vstream.h dict_stream.o: vstring.h +dict_stream_test.o: ../../include/pmock_expect.h +dict_stream_test.o: ../../include/ptest.h +dict_stream_test.o: ../../include/ptest_main.h +dict_stream_test.o: argv.h +dict_stream_test.o: check_arg.h +dict_stream_test.o: dict.h +dict_stream_test.o: dict_stream_test.c +dict_stream_test.o: msg.h +dict_stream_test.o: msg_output.h +dict_stream_test.o: msg_vstream.h +dict_stream_test.o: myflock.h +dict_stream_test.o: stringops.h +dict_stream_test.o: sys_defs.h +dict_stream_test.o: vbuf.h +dict_stream_test.o: vstream.h +dict_stream_test.o: vstring.h dict_surrogate.o: argv.h dict_surrogate.o: check_arg.h dict_surrogate.o: compat_va_copy.h @@ -1764,22 +1842,6 @@ dict_tcp.o: vbuf.h dict_tcp.o: vstream.h dict_tcp.o: vstring.h dict_tcp.o: vstring_vstream.h -dict_test.o: argv.h -dict_test.o: check_arg.h -dict_test.o: dict.h -dict_test.o: dict_db.h -dict_test.o: dict_lmdb.h -dict_test.o: dict_test.c -dict_test.o: mkmap.h -dict_test.o: msg.h -dict_test.o: msg_vstream.h -dict_test.o: myflock.h -dict_test.o: stringops.h -dict_test.o: sys_defs.h -dict_test.o: vbuf.h -dict_test.o: vstream.h -dict_test.o: vstring.h -dict_test.o: vstring_vstream.h dict_thash.o: argv.h dict_thash.o: check_arg.h dict_thash.o: dict.h @@ -1811,6 +1873,24 @@ dict_union.o: sys_defs.h dict_union.o: vbuf.h dict_union.o: vstream.h dict_union.o: vstring.h +dict_union_test.o: ../../include/pmock_expect.h +dict_union_test.o: ../../include/ptest.h +dict_union_test.o: ../../include/ptest_main.h +dict_union_test.o: argv.h +dict_union_test.o: check_arg.h +dict_union_test.o: dict.h +dict_union_test.o: dict_union.h +dict_union_test.o: dict_union_test.c +dict_union_test.o: msg.h +dict_union_test.o: msg_output.h +dict_union_test.o: msg_vstream.h +dict_union_test.o: myflock.h +dict_union_test.o: mymalloc.h +dict_union_test.o: stringops.h +dict_union_test.o: sys_defs.h +dict_union_test.o: vbuf.h +dict_union_test.o: vstream.h +dict_union_test.o: vstring.h dict_unix.o: argv.h dict_unix.o: check_arg.h dict_unix.o: dict.h @@ -1927,6 +2007,35 @@ find_inet.o: stringops.h find_inet.o: sys_defs.h find_inet.o: vbuf.h find_inet.o: vstring.h +find_inet_service.o: check_arg.h +find_inet_service.o: find_inet_service.c +find_inet_service.o: find_inet_service.h +find_inet_service.o: known_tcp_ports.h +find_inet_service.o: msg.h +find_inet_service.o: sane_strtol.h +find_inet_service.o: stringops.h +find_inet_service.o: sys_defs.h +find_inet_service.o: vbuf.h +find_inet_service.o: vstring.h +find_inet_service.o: wrap_netdb.h +find_inet_service_test.o: ../../include/mock_servent.h +find_inet_service_test.o: ../../include/pmock_expect.h +find_inet_service_test.o: ../../include/ptest.h +find_inet_service_test.o: ../../include/ptest_main.h +find_inet_service_test.o: argv.h +find_inet_service_test.o: check_arg.h +find_inet_service_test.o: find_inet_service.h +find_inet_service_test.o: find_inet_service_test.c +find_inet_service_test.o: known_tcp_ports.h +find_inet_service_test.o: msg.h +find_inet_service_test.o: msg_output.h +find_inet_service_test.o: msg_vstream.h +find_inet_service_test.o: stringops.h +find_inet_service_test.o: sys_defs.h +find_inet_service_test.o: vbuf.h +find_inet_service_test.o: vstream.h +find_inet_service_test.o: vstring.h +find_inet_service_test.o: wrap_netdb.h format_tv.o: check_arg.h format_tv.o: format_tv.c format_tv.o: format_tv.h @@ -1962,6 +2071,21 @@ hash_fnv.o: hash_fnv.h hash_fnv.o: ldseed.h hash_fnv.o: msg.h hash_fnv.o: sys_defs.h +hash_fnv_test.o: ../../include/pmock_expect.h +hash_fnv_test.o: ../../include/ptest.h +hash_fnv_test.o: ../../include/ptest_main.h +hash_fnv_test.o: argv.h +hash_fnv_test.o: check_arg.h +hash_fnv_test.o: hash_fnv.h +hash_fnv_test.o: hash_fnv_test.c +hash_fnv_test.o: msg.h +hash_fnv_test.o: msg_output.h +hash_fnv_test.o: msg_vstream.h +hash_fnv_test.o: stringops.h +hash_fnv_test.o: sys_defs.h +hash_fnv_test.o: vbuf.h +hash_fnv_test.o: vstream.h +hash_fnv_test.o: vstring.h hex_code.o: check_arg.h hex_code.o: hex_code.c hex_code.o: hex_code.h @@ -2003,6 +2127,7 @@ inet_addr_host.o: myaddrinfo.h inet_addr_host.o: mymalloc.h inet_addr_host.o: sock_addr.h inet_addr_host.o: sys_defs.h +inet_addr_host.o: wrap_netdb.h inet_addr_list.o: inet_addr_list.c inet_addr_list.o: inet_addr_list.h inet_addr_list.o: msg.h @@ -2010,6 +2135,7 @@ inet_addr_list.o: myaddrinfo.h inet_addr_list.o: mymalloc.h inet_addr_list.o: sock_addr.h inet_addr_list.o: sys_defs.h +inet_addr_list.o: wrap_netdb.h inet_addr_local.o: check_arg.h inet_addr_local.o: hex_code.h inet_addr_local.o: inet_addr_list.h @@ -2023,6 +2149,7 @@ inet_addr_local.o: sock_addr.h inet_addr_local.o: sys_defs.h inet_addr_local.o: vbuf.h inet_addr_local.o: vstring.h +inet_addr_local.o: wrap_netdb.h inet_connect.o: connect.h inet_connect.o: host_port.h inet_connect.o: inet_connect.c @@ -2035,6 +2162,7 @@ inet_connect.o: sane_connect.h inet_connect.o: sock_addr.h inet_connect.o: sys_defs.h inet_connect.o: timed_connect.h +inet_connect.o: wrap_netdb.h inet_listen.o: host_port.h inet_listen.o: htable.h inet_listen.o: inet_listen.c @@ -2047,6 +2175,7 @@ inet_listen.o: mymalloc.h inet_listen.o: sane_accept.h inet_listen.o: sock_addr.h inet_listen.o: sys_defs.h +inet_listen.o: wrap_netdb.h inet_proto.o: check_arg.h inet_proto.o: inet_proto.c inet_proto.o: inet_proto.h @@ -2057,6 +2186,7 @@ inet_proto.o: name_mask.h inet_proto.o: sys_defs.h inet_proto.o: vbuf.h inet_proto.o: vstring.h +inet_proto.o: wrap_netdb.h inet_trigger.o: connect.h inet_trigger.o: events.h inet_trigger.o: inet_trigger.c @@ -2089,6 +2219,21 @@ known_tcp_ports.o: stringops.h known_tcp_ports.o: sys_defs.h known_tcp_ports.o: vbuf.h known_tcp_ports.o: vstring.h +known_tcp_ports_test.o: ../../include/pmock_expect.h +known_tcp_ports_test.o: ../../include/ptest.h +known_tcp_ports_test.o: ../../include/ptest_main.h +known_tcp_ports_test.o: argv.h +known_tcp_ports_test.o: check_arg.h +known_tcp_ports_test.o: known_tcp_ports.h +known_tcp_ports_test.o: known_tcp_ports_test.c +known_tcp_ports_test.o: msg.h +known_tcp_ports_test.o: msg_output.h +known_tcp_ports_test.o: msg_vstream.h +known_tcp_ports_test.o: stringops.h +known_tcp_ports_test.o: sys_defs.h +known_tcp_ports_test.o: vbuf.h +known_tcp_ports_test.o: vstream.h +known_tcp_ports_test.o: vstring.h ldseed.o: iostuff.h ldseed.o: ldseed.c ldseed.o: ldseed.h @@ -2113,8 +2258,6 @@ load_file.o: vbuf.h load_file.o: vstream.h load_file.o: warn_stat.h load_lib.o: load_lib.c -load_lib.o: load_lib.h -load_lib.o: msg.h load_lib.o: sys_defs.h logwriter.o: check_arg.h logwriter.o: iostuff.h @@ -2203,6 +2346,7 @@ match_ops.o: sys_defs.h match_ops.o: vbuf.h match_ops.o: vstream.h match_ops.o: vstring.h +match_ops.o: wrap_netdb.h midna_domain.o: check_arg.h midna_domain.o: ctable.h midna_domain.o: midna_domain.c @@ -2327,6 +2471,7 @@ msg_logger.o: vbuf.h msg_logger.o: vstream.h msg_logger.o: vstring.h msg_output.o: check_arg.h +msg_output.o: msg.h msg_output.o: msg_output.c msg_output.o: msg_output.h msg_output.o: msg_vstream.h @@ -2336,6 +2481,22 @@ msg_output.o: sys_defs.h msg_output.o: vbuf.h msg_output.o: vstream.h msg_output.o: vstring.h +msg_output_test.o: ../../include/match_basic.h +msg_output_test.o: ../../include/pmock_expect.h +msg_output_test.o: ../../include/ptest.h +msg_output_test.o: ../../include/ptest_main.h +msg_output_test.o: argv.h +msg_output_test.o: check_arg.h +msg_output_test.o: msg.h +msg_output_test.o: msg_output.h +msg_output_test.o: msg_output_test.c +msg_output_test.o: msg_vstream.h +msg_output_test.o: mymalloc.h +msg_output_test.o: stringops.h +msg_output_test.o: sys_defs.h +msg_output_test.o: vbuf.h +msg_output_test.o: vstream.h +msg_output_test.o: vstring.h msg_rate_delay.o: check_arg.h msg_rate_delay.o: events.h msg_rate_delay.o: msg.h @@ -2380,6 +2541,29 @@ myaddrinfo.o: sys_defs.h myaddrinfo.o: valid_hostname.h myaddrinfo.o: vbuf.h myaddrinfo.o: vstring.h +myaddrinfo.o: wrap_netdb.h +myaddrinfo_test.o: ../../include/addrinfo_to_string.h +myaddrinfo_test.o: ../../include/make_addr.h +myaddrinfo_test.o: ../../include/match_addr.h +myaddrinfo_test.o: ../../include/match_basic.h +myaddrinfo_test.o: ../../include/mock_getaddrinfo.h +myaddrinfo_test.o: ../../include/pmock_expect.h +myaddrinfo_test.o: ../../include/ptest.h +myaddrinfo_test.o: ../../include/ptest_main.h +myaddrinfo_test.o: argv.h +myaddrinfo_test.o: check_arg.h +myaddrinfo_test.o: inet_proto.h +myaddrinfo_test.o: msg.h +myaddrinfo_test.o: msg_output.h +myaddrinfo_test.o: msg_vstream.h +myaddrinfo_test.o: myaddrinfo.h +myaddrinfo_test.o: myaddrinfo_test.c +myaddrinfo_test.o: stringops.h +myaddrinfo_test.o: sys_defs.h +myaddrinfo_test.o: vbuf.h +myaddrinfo_test.o: vstream.h +myaddrinfo_test.o: vstring.h +myaddrinfo_test.o: wrap_netdb.h myflock.o: msg.h myflock.o: myflock.c myflock.o: myflock.h @@ -2388,6 +2572,21 @@ mymalloc.o: msg.h mymalloc.o: mymalloc.c mymalloc.o: mymalloc.h mymalloc.o: sys_defs.h +mymalloc_test.o: ../../include/pmock_expect.h +mymalloc_test.o: ../../include/ptest.h +mymalloc_test.o: ../../include/ptest_main.h +mymalloc_test.o: argv.h +mymalloc_test.o: check_arg.h +mymalloc_test.o: msg.h +mymalloc_test.o: msg_output.h +mymalloc_test.o: msg_vstream.h +mymalloc_test.o: mymalloc.h +mymalloc_test.o: mymalloc_test.c +mymalloc_test.o: stringops.h +mymalloc_test.o: sys_defs.h +mymalloc_test.o: vbuf.h +mymalloc_test.o: vstream.h +mymalloc_test.o: vstring.h myrand.o: myrand.c myrand.o: myrand.h myrand.o: sys_defs.h @@ -2397,6 +2596,21 @@ mystrtok.o: stringops.h mystrtok.o: sys_defs.h mystrtok.o: vbuf.h mystrtok.o: vstring.h +mystrtok_test.o: ../../include/pmock_expect.h +mystrtok_test.o: ../../include/ptest.h +mystrtok_test.o: ../../include/ptest_main.h +mystrtok_test.o: argv.h +mystrtok_test.o: check_arg.h +mystrtok_test.o: msg.h +mystrtok_test.o: msg_output.h +mystrtok_test.o: msg_vstream.h +mystrtok_test.o: mymalloc.h +mystrtok_test.o: mystrtok_test.c +mystrtok_test.o: stringops.h +mystrtok_test.o: sys_defs.h +mystrtok_test.o: vbuf.h +mystrtok_test.o: vstream.h +mystrtok_test.o: vstring.h name_code.o: name_code.c name_code.o: name_code.h name_code.o: sys_defs.h @@ -2671,17 +2885,6 @@ stream_send_fd.o: iostuff.h stream_send_fd.o: msg.h stream_send_fd.o: stream_send_fd.c stream_send_fd.o: sys_defs.h -stream_test.o: check_arg.h -stream_test.o: connect.h -stream_test.o: htable.h -stream_test.o: iostuff.h -stream_test.o: listen.h -stream_test.o: msg.h -stream_test.o: msg_vstream.h -stream_test.o: stream_test.c -stream_test.o: sys_defs.h -stream_test.o: vbuf.h -stream_test.o: vstream.h stream_trigger.o: connect.h stream_trigger.o: events.h stream_trigger.o: iostuff.h @@ -2692,6 +2895,17 @@ stream_trigger.o: sys_defs.h stream_trigger.o: trigger.h sys_compat.o: sys_compat.c sys_compat.o: sys_defs.h +test_sunos5_stream.o: check_arg.h +test_sunos5_stream.o: connect.h +test_sunos5_stream.o: htable.h +test_sunos5_stream.o: iostuff.h +test_sunos5_stream.o: listen.h +test_sunos5_stream.o: msg.h +test_sunos5_stream.o: msg_vstream.h +test_sunos5_stream.o: sys_defs.h +test_sunos5_stream.o: test_sunos5_stream.c +test_sunos5_stream.o: vbuf.h +test_sunos5_stream.o: vstream.h timecmp.o: timecmp.c timecmp.o: timecmp.h timed_connect.o: iostuff.h @@ -2731,6 +2945,20 @@ unescape.o: sys_defs.h unescape.o: unescape.c unescape.o: vbuf.h unescape.o: vstring.h +unescape_test.o: ../../include/pmock_expect.h +unescape_test.o: ../../include/ptest.h +unescape_test.o: ../../include/ptest_main.h +unescape_test.o: argv.h +unescape_test.o: check_arg.h +unescape_test.o: msg.h +unescape_test.o: msg_output.h +unescape_test.o: msg_vstream.h +unescape_test.o: stringops.h +unescape_test.o: sys_defs.h +unescape_test.o: unescape_test.c +unescape_test.o: vbuf.h +unescape_test.o: vstream.h +unescape_test.o: vstring.h unix_connect.o: connect.h unix_connect.o: iostuff.h unix_connect.o: msg.h @@ -2884,6 +3112,9 @@ watchdog.o: posix_signals.h watchdog.o: sys_defs.h watchdog.o: watchdog.c watchdog.o: watchdog.h +wrap_netdb.o: sys_defs.h +wrap_netdb.o: wrap_netdb.c +wrap_netdb.o: wrap_netdb.h write_buf.o: iostuff.h write_buf.o: msg.h write_buf.o: sys_defs.h diff --git a/postfix/src/util/argv.c b/postfix/src/util/argv.c index 4e05fd0b4..8c77a8041 100644 --- a/postfix/src/util/argv.c +++ b/postfix/src/util/argv.c @@ -378,344 +378,3 @@ void argv_delete(ARGV *argvp, ssize_t first, ssize_t how_many) argvp->argv[pos] = argvp->argv[pos + how_many]; argvp->argc -= how_many; } - -#ifdef TEST - - /* - * System library. - */ -#include - - /* - * Utility library. - */ -#include -#include - -#define ARRAY_LEN (10) - -typedef struct TEST_CASE { - const char *label; /* identifies test case */ - const char *inputs[ARRAY_LEN]; /* input strings */ - int terminate; /* terminate result */ - ARGV *(*populate_fn) (const struct TEST_CASE *, ARGV *); - const char *exp_panic_msg; /* expected panic */ - int exp_argc; /* expected array length */ - const char *exp_argv[ARRAY_LEN]; /* expected array content */ -} TEST_CASE; - -#define TERMINATE_ARRAY (1) - -#define PASS (0) -#define FAIL (1) - -VSTRING *test_panic_str; -jmp_buf test_panic_jbuf; - -/* test_msg_panic - does not return, and does not terminate */ - -void test_msg_panic(const char *fmt,...) -{ - va_list ap; - - va_start(ap, fmt); - test_panic_str = vstring_alloc(100); - vstring_vsprintf(test_panic_str, fmt, ap); - va_end(ap); - longjmp(test_panic_jbuf, 1); -} - -/* test_argv_populate - populate result, optionally terminate */ - -static ARGV *test_argv_populate(const TEST_CASE *tp, ARGV *argvp) -{ - const char *const * cpp; - - for (cpp = tp->inputs; *cpp; cpp++) - argv_add(argvp, *cpp, (char *) 0); - if (tp->terminate) - argv_terminate(argvp); - return (argvp); -} - -/* test_argv_sort - populate and sort result */ - -static ARGV *test_argv_sort(const TEST_CASE *tp, ARGV *argvp) -{ - test_argv_populate(tp, argvp); - argv_qsort(argvp, (ARGV_COMPAR_FN) 0); - return (argvp); -} - -/* test_argv_sort_uniq - populate, sort, uniq result */ - -static ARGV *test_argv_sort_uniq(const TEST_CASE *tp, ARGV *argvp) -{ - - /* - * This also tests argv_delete(). - */ - test_argv_sort(tp, argvp); - argv_uniq(argvp, (ARGV_COMPAR_FN) 0); - return (argvp); -} - -/* test_argv_good_truncate - populate and truncate to good size */ - -static ARGV *test_argv_good_truncate(const TEST_CASE *tp, ARGV *argvp) -{ - test_argv_populate(tp, argvp); - argv_truncate(argvp, tp->exp_argc); - return (argvp); -} - -/* test_argv_bad_truncate - populate and truncate to bad size */ - -static ARGV *test_argv_bad_truncate(const TEST_CASE *tp, ARGV *argvp) -{ - test_argv_populate(tp, argvp); - argv_truncate(argvp, -1); - return (argvp); -} - -/* test_argv_good_insert - populate and insert at good position */ - -static ARGV *test_argv_good_insert(const TEST_CASE *tp, ARGV *argvp) -{ - test_argv_populate(tp, argvp); - argv_insert_one(argvp, 1, "new"); - return (argvp); -} - -/* test_argv_bad_insert1 - populate and insert at bad position */ - -static ARGV *test_argv_bad_insert1(const TEST_CASE *tp, ARGV *argvp) -{ - test_argv_populate(tp, argvp); - argv_insert_one(argvp, -1, "new"); - return (argvp); -} - -/* test_argv_bad_insert2 - populate and insert at bad position */ - -static ARGV *test_argv_bad_insert2(const TEST_CASE *tp, ARGV *argvp) -{ - test_argv_populate(tp, argvp); - argv_insert_one(argvp, 100, "new"); - return (argvp); -} - -/* test_argv_good_replace - populate and replace at good position */ - -static ARGV *test_argv_good_replace(const TEST_CASE *tp, ARGV *argvp) -{ - test_argv_populate(tp, argvp); - argv_replace_one(argvp, 1, "new"); - return (argvp); -} - -/* test_argv_bad_replace1 - populate and replace at bad position */ - -static ARGV *test_argv_bad_replace1(const TEST_CASE *tp, ARGV *argvp) -{ - test_argv_populate(tp, argvp); - argv_replace_one(argvp, -1, "new"); - return (argvp); -} - -/* test_argv_bad_replace2 - populate and replace at bad position */ - -static ARGV *test_argv_bad_replace2(const TEST_CASE *tp, ARGV *argvp) -{ - test_argv_populate(tp, argvp); - argv_replace_one(argvp, 100, "new"); - return (argvp); -} - -/* test_argv_bad_delete1 - populate and delete at bad position */ - -static ARGV *test_argv_bad_delete1(const TEST_CASE *tp, ARGV *argvp) -{ - test_argv_populate(tp, argvp); - argv_delete(argvp, -1, 1); - return (argvp); -} - -/* test_argv_bad_delete2 - populate and delete at bad position */ - -static ARGV *test_argv_bad_delete2(const TEST_CASE *tp, ARGV *argvp) -{ - test_argv_populate(tp, argvp); - argv_delete(argvp, 0, -1); - return (argvp); -} - -/* test_argv_bad_delete3 - populate and delete at bad position */ - -static ARGV *test_argv_bad_delete3(const TEST_CASE *tp, ARGV *argvp) -{ - test_argv_populate(tp, argvp); - argv_delete(argvp, 100, 1); - return (argvp); -} - -/* test_argv_verify - verify result */ - -static int test_argv_verify(const TEST_CASE *tp, ARGV *argvp) -{ - int idx; - - if (tp->exp_panic_msg != 0) { - if (test_panic_str == 0) { - msg_warn("test case '%s': got no panic, want: '%s'", - tp->label, tp->exp_panic_msg); - return (FAIL); - } - if (strcmp(vstring_str(test_panic_str), tp->exp_panic_msg) != 0) { - msg_warn("test case '%s': got '%s', want: '%s'", - tp->label, vstring_str(test_panic_str), tp->exp_panic_msg); - return (FAIL); - } - return (PASS); - } - if (test_panic_str != 0) { - msg_warn("test case '%s': got '%s', want: no panic", - tp->label, vstring_str(test_panic_str)); - return (FAIL); - } - if (argvp->argc != tp->exp_argc) { - msg_warn("test case '%s': got argc: %ld, want: %d", - tp->label, (long) argvp->argc, tp->exp_argc); - return (FAIL); - } - if (argvp->argv[argvp->argc] != 0 && tp->terminate) { - msg_warn("test case '%s': got unterminated, want: terminated", tp->label); - return (FAIL); - } - for (idx = 0; idx < argvp->argc; idx++) { - if (strcmp(argvp->argv[idx], tp->exp_argv[idx]) != 0) { - msg_warn("test case '%s': index %d: got '%s', want: '%s'", - tp->label, idx, argvp->argv[idx], tp->exp_argv[idx]); - return (FAIL); - } - } - return (PASS); -} - - /* - * The test cases. TODO: argv_addn with good and bad string length. - */ -static const TEST_CASE test_cases[] = { - {"multiple strings, unterminated array", - {"foo", "baz", "bar", 0}, 0, test_argv_populate, - 0, 3, {"foo", "baz", "bar", 0} - }, - {"multiple strings, terminated array", - {"foo", "baz", "bar", 0}, TERMINATE_ARRAY, test_argv_populate, - 0, 3, {"foo", "baz", "bar", 0} - }, - {"distinct strings, sorted array", - {"foo", "baz", "bar", 0}, 0, test_argv_sort, - 0, 3, {"bar", "baz", "foo", 0} - }, - {"duplicate strings, sorted array", - {"foo", "baz", "baz", "bar", 0}, 0, test_argv_sort, - 0, 4, {"bar", "baz", "baz", "foo", 0} - }, - {"duplicate strings, sorted, uniqued-middle elements", - {"foo", "baz", "baz", "bar", 0}, 0, test_argv_sort_uniq, - 0, 3, {"bar", "baz", "foo", 0} - }, - {"duplicate strings, sorted, uniqued-first elements", - {"foo", "bar", "baz", "bar", 0}, 0, test_argv_sort_uniq, - 0, 3, {"bar", "baz", "foo", 0} - }, - {"duplicate strings, sorted, uniqued-last elements", - {"foo", "foo", "baz", "bar", 0}, 0, test_argv_sort_uniq, - 0, 3, {"bar", "baz", "foo", 0} - }, - {"multiple strings, truncate array by one", - {"foo", "baz", "bar", 0}, 0, test_argv_good_truncate, - 0, 2, {"foo", "baz", 0} - }, - {"multiple strings, truncate whole array", - {"foo", "baz", "bar", 0}, 0, test_argv_good_truncate, - 0, 0, {"foo", "baz", 0} - }, - {"multiple strings, bad truncate", - {"foo", "baz", "bar", 0}, 0, test_argv_bad_truncate, - "argv_truncate: bad length -1" - }, - {"multiple strings, insert one at good position", - {"foo", "baz", "bar", 0}, 0, test_argv_good_insert, - 0, 4, {"foo", "new", "baz", "bar", 0} - }, - {"multiple strings, insert one at bad position", - {"foo", "baz", "bar", 0}, 0, test_argv_bad_insert1, - "argv_insert_one bad position: -1" - }, - {"multiple strings, insert one at bad position", - {"foo", "baz", "bar", 0}, 0, test_argv_bad_insert2, - "argv_insert_one bad position: 100" - }, - {"multiple strings, replace one at good position", - {"foo", "baz", "bar", 0}, 0, test_argv_good_replace, - 0, 3, {"foo", "new", "bar", 0} - }, - {"multiple strings, replace one at bad position", - {"foo", "baz", "bar", 0}, 0, test_argv_bad_replace1, - "argv_replace_one bad position: -1" - }, - {"multiple strings, replace one at bad position", - {"foo", "baz", "bar", 0}, 0, test_argv_bad_replace2, - "argv_replace_one bad position: 100" - }, - {"multiple strings, delete one at negative position", - {"foo", "baz", "bar", 0}, 0, test_argv_bad_delete1, - "argv_delete bad range: (start=-1 count=1)" - }, - {"multiple strings, delete with bad range end", - {"foo", "baz", "bar", 0}, 0, test_argv_bad_delete2, - "argv_delete bad range: (start=0 count=-1)" - }, - {"multiple strings, delete at too large position", - {"foo", "baz", "bar", 0}, 0, test_argv_bad_delete3, - "argv_delete bad range: (start=100 count=1)" - }, - 0, -}; - -int main(int argc, char **argv) -{ - const TEST_CASE *tp; - int pass = 0; - int fail = 0; - - msg_vstream_init(sane_basename((VSTRING *) 0, argv[0]), VSTREAM_ERR); - - for (tp = test_cases; tp->label != 0; tp++) { - int test_failed; - ARGV *argvp; - - argvp = argv_alloc(1); - if (setjmp(test_panic_jbuf) == 0) - tp->populate_fn(tp, argvp); - test_failed = test_argv_verify(tp, argvp); - if (test_failed) { - msg_info("%s: FAIL", tp->label); - fail++; - } else { - msg_info("%s: PASS", tp->label); - pass++; - } - argv_free(argvp); - if (test_panic_str) { - vstring_free(test_panic_str); - test_panic_str = 0; - } - } - msg_info("PASS=%d FAIL=%d", pass, fail); - exit(fail != 0); -} - -#endif diff --git a/postfix/src/util/argv_test.c b/postfix/src/util/argv_test.c new file mode 100644 index 000000000..d604962f3 --- /dev/null +++ b/postfix/src/util/argv_test.c @@ -0,0 +1,316 @@ + /* + * Test program to exercise argv.c. See ptest_main.h for a documented + * example. + */ + + /* + * System library. + */ +#include +#include + + /* + * Utility library. + */ +#include + + /* + * Test library. + */ +#include + +#define ARRAY_LEN (10) + + /* + * All test data can fit in the same PTEST_CASE structure. + */ +typedef struct PTEST_CASE { + const char *testname; /* identifies test case */ + void (*action) (PTEST_CTX *t, const struct PTEST_CASE *tp); + const char *inputs[ARRAY_LEN]; /* input strings */ + int flags; /* see below */ + const char *want_panic_msg; /* expected panic */ + ssize_t want_argc; /* expected array length */ + const char *want_argv[ARRAY_LEN]; /* expected array content */ +} PTEST_CASE; + +#define ARGV_TEST_FLAG_TERMINATE (1<<0) + +/* verify_argv - verify result */ + +static void verify_argv(PTEST_CTX *t, const PTEST_CASE *tp, ARGV *argvp) +{ + int idx; + + /* + * We don't use eq_argv() here. We must at least once compare argv.c + * output against data that was created without using any argv.c code. + */ + if (argvp->argc != tp->want_argc) + ptest_fatal(t, "got argc %ld, want %ld", + (long) argvp->argc, (long) tp->want_argc); + if (argvp->argv[argvp->argc] != 0 && (tp->flags & ARGV_TEST_FLAG_TERMINATE)) + ptest_error(t, "got unterminated, want terminated"); + for (idx = 0; idx < argvp->argc; idx++) { + if (strcmp(argvp->argv[idx], tp->want_argv[idx]) != 0) { + ptest_error(t, "index %d: got '%s', want '%s'", + idx, argvp->argv[idx], tp->want_argv[idx]); + } + } +} + +/* wrap_argv_free - in case (void *) != (struct *) */ + +static void wrap_argv_free(void *context) +{ + argv_free((ARGV *) context); +} + +/* populate_argv - populate result, optionally terminate */ + +static ARGV *populate_argv(PTEST_CTX *t, const PTEST_CASE *tp) +{ + ARGV *argvp = argv_alloc(1); + const char *const * cpp; + + /* + * Some tests use longjmp instead of returning normally, so we use + * deferred execution to clean up allocated memory. + */ + ptest_defer(t, wrap_argv_free, (void *) argvp); + if (tp->want_panic_msg != 0) + expect_ptest_log_event(t, tp->want_panic_msg); + for (cpp = tp->inputs; *cpp; cpp++) + argv_add(argvp, *cpp, (char *) 0); + if (tp->flags & ARGV_TEST_FLAG_TERMINATE) + argv_terminate(argvp); + return (argvp); +} + +/* test_argv_basic - populate and verify */ + +static void test_argv_basic(PTEST_CTX *t, const PTEST_CASE *tp) +{ + ARGV *argvp = populate_argv(t, tp); + + verify_argv(t, tp, argvp); +} + +/* test_argv_sort - populate and sort result */ + +static void test_argv_sort(PTEST_CTX *t, const PTEST_CASE *tp) +{ + ARGV *argvp = populate_argv(t, tp); + + argv_qsort(argvp, (ARGV_COMPAR_FN) 0); + verify_argv(t, tp, argvp); +} + +/* test_argv_sort_uniq - populate, sort, uniq result */ + +static void test_argv_sort_uniq(PTEST_CTX *t, const PTEST_CASE *tp) +{ + + /* + * This also tests argv_delete(). + */ + ARGV *argvp = populate_argv(t, tp); + + argv_qsort(argvp, (ARGV_COMPAR_FN) 0); + argv_uniq(argvp, (ARGV_COMPAR_FN) 0); + verify_argv(t, tp, argvp); +} + +/* test_argv_good_truncate - populate and truncate to good size */ + +static void test_argv_good_truncate(PTEST_CTX *t, const PTEST_CASE *tp) +{ + ARGV *argvp = populate_argv(t, tp); + + argv_truncate(argvp, tp->want_argc); + verify_argv(t, tp, argvp); +} + +/* test_argv_bad_truncate - populate and truncate to bad size */ + +static void test_argv_bad_truncate(PTEST_CTX *t, const PTEST_CASE *tp) +{ + ARGV *argvp = populate_argv(t, tp); + + argv_truncate(argvp, -1); + ptest_fatal(t, "argv_delete() returned"); +} + +/* test_argv_good_insert - populate and insert at good position */ + +static void test_argv_good_insert(PTEST_CTX *t, const PTEST_CASE *tp) +{ + ARGV *argvp = populate_argv(t, tp); + + argv_insert_one(argvp, 1, "new"); + verify_argv(t, tp, argvp); +} + +/* test_argv_bad_insert1 - populate and insert at bad position */ + +static void test_argv_bad_insert1(PTEST_CTX *t, const PTEST_CASE *tp) +{ + ARGV *argvp = populate_argv(t, tp); + + argv_insert_one(argvp, -1, "new"); + ptest_fatal(t, "argv_delete() returned"); +} + +/* test_argv_bad_insert2 - populate and insert at bad position */ + +static void test_argv_bad_insert2(PTEST_CTX *t, const PTEST_CASE *tp) +{ + ARGV *argvp = populate_argv(t, tp); + + argv_insert_one(argvp, 100, "new"); + ptest_fatal(t, "argv_delete() returned"); +} + +/* test_argv_good_replace - populate and replace at good position */ + +static void test_argv_good_replace(PTEST_CTX *t, const PTEST_CASE *tp) +{ + ARGV *argvp = populate_argv(t, tp); + + argv_replace_one(argvp, 1, "new"); + verify_argv(t, tp, argvp); +} + +/* test_argv_bad_replace1 - populate and replace at bad position */ + +static void test_argv_bad_replace1(PTEST_CTX *t, const PTEST_CASE *tp) +{ + ARGV *argvp = populate_argv(t, tp); + + argv_replace_one(argvp, -1, "new"); + ptest_fatal(t, "argv_delete() returned"); +} + +/* test_argv_bad_replace2 - populate and replace at bad position */ + +static void test_argv_bad_replace2(PTEST_CTX *t, const PTEST_CASE *tp) +{ + ARGV *argvp = populate_argv(t, tp); + + argv_replace_one(argvp, 100, "new"); + ptest_fatal(t, "argv_delete() returned"); +} + +/* test_argv_bad_delete1 - populate and delete at bad position */ + +static void test_argv_bad_delete1(PTEST_CTX *t, const PTEST_CASE *tp) +{ + ARGV *argvp = populate_argv(t, tp); + + argv_delete(argvp, -1, 1); + ptest_fatal(t, "argv_delete() returned"); +} + +/* test_argv_bad_delete2 - populate and delete at bad position */ + +static void test_argv_bad_delete2(PTEST_CTX *t, const PTEST_CASE *tp) +{ + ARGV *argvp = populate_argv(t, tp); + + argv_delete(argvp, 0, -1); + ptest_fatal(t, "argv_delete() returned"); +} + +/* test_argv_bad_delete3 - populate and delete at bad position */ + +static void test_argv_bad_delete3(PTEST_CTX *t, const PTEST_CASE *tp) +{ + ARGV *argvp = populate_argv(t, tp); + + argv_delete(argvp, 100, 1); + ptest_fatal(t, "argv_delete() returned"); +} + + /* + * The test cases. TODO: argv_addn with good and bad string length. + */ +static const PTEST_CASE ptestcases[] = { + {"multiple strings, unterminated array", test_argv_basic, + {"foo", "baz", "bar", 0}, 0, + 0, 3, {"foo", "baz", "bar", 0} + }, + {"multiple strings, terminated array", test_argv_basic, + {"foo", "baz", "bar", 0}, ARGV_TEST_FLAG_TERMINATE, + 0, 3, {"foo", "baz", "bar", 0} + }, + {"distinct strings, sorted array", test_argv_sort, + {"foo", "baz", "bar", 0}, 0, + 0, 3, {"bar", "baz", "foo", 0} + }, + {"duplicate strings, sorted array", test_argv_sort, + {"foo", "baz", "baz", "bar", 0}, 0, + 0, 4, {"bar", "baz", "baz", "foo", 0} + }, + {"duplicate strings, sorted, uniqued-middle elements", test_argv_sort_uniq, + {"foo", "baz", "baz", "bar", 0}, 0, + 0, 3, {"bar", "baz", "foo", 0} + }, + {"duplicate strings, sorted, uniqued-first elements", test_argv_sort_uniq, + {"foo", "bar", "baz", "bar", 0}, 0, + 0, 3, {"bar", "baz", "foo", 0} + }, + {"duplicate strings, sorted, uniqued-last elements", test_argv_sort_uniq, + {"foo", "foo", "baz", "bar", 0}, 0, + 0, 3, {"bar", "baz", "foo", 0} + }, + {"multiple strings, truncate array by one", test_argv_good_truncate, + {"foo", "baz", "bar", 0}, 0, + 0, 2, {"foo", "baz", 0} + }, + {"multiple strings, truncate whole array", test_argv_good_truncate, + {"foo", "baz", "bar", 0}, 0, + 0, 0, {"foo", "baz", 0} + }, + {"multiple strings, bad truncate", test_argv_bad_truncate, + {"foo", "baz", "bar", 0}, 0, + "argv_truncate: bad length -1" + }, + {"multiple strings, insert one at good position", test_argv_good_insert, + {"foo", "baz", "bar", 0}, 0, + 0, 4, {"foo", "new", "baz", "bar", 0} + }, + {"multiple strings, insert one at bad position", test_argv_bad_insert1, + {"foo", "baz", "bar", 0}, 0, + "argv_insert_one bad position: -1" + }, + {"multiple strings, insert one at bad position", test_argv_bad_insert2, + {"foo", "baz", "bar", 0}, 0, + "argv_insert_one bad position: 100" + }, + {"multiple strings, replace one at good position", test_argv_good_replace, + {"foo", "baz", "bar", 0}, 0, + 0, 3, {"foo", "new", "bar", 0} + }, + {"multiple strings, replace one at bad position", test_argv_bad_replace1, + {"foo", "baz", "bar", 0}, 0, + "argv_replace_one bad position: -1" + }, + {"multiple strings, replace one at bad position", test_argv_bad_replace2, + {"foo", "baz", "bar", 0}, 0, + "argv_replace_one bad position: 100" + }, + {"multiple strings, delete one at negative position", test_argv_bad_delete1, + {"foo", "baz", "bar", 0}, 0, + "argv_delete bad range: (start=-1 count=1)" + }, + {"multiple strings, delete with bad range end", test_argv_bad_delete2, + {"foo", "baz", "bar", 0}, 0, + "argv_delete bad range: (start=0 count=-1)" + }, + {"multiple strings, delete at too large position", test_argv_bad_delete3, + {"foo", "baz", "bar", 0}, 0, + "argv_delete bad range: (start=100 count=1)" + }, +}; + +#include diff --git a/postfix/src/util/dict.h b/postfix/src/util/dict.h index 217deb7a6..15fa074fd 100644 --- a/postfix/src/util/dict.h +++ b/postfix/src/util/dict.h @@ -271,7 +271,7 @@ extern DICT *dict_utf8_activate(DICT *); /* * Driver for interactive or scripted tests. */ -void dict_test(int, char **); +void dict_cli(int, char **); /* * Behind-the-scenes support to continue execution with reduced diff --git a/postfix/src/util/dict_test.c b/postfix/src/util/dict_cli.c similarity index 99% rename from postfix/src/util/dict_test.c rename to postfix/src/util/dict_cli.c index ead61b20c..1c800c28a 100644 --- a/postfix/src/util/dict_test.c +++ b/postfix/src/util/dict_cli.c @@ -33,7 +33,7 @@ static NORETURN usage(char *myname) msg_fatal("usage: %s type:file read|write|create [flags...]", myname); } -void dict_test(int argc, char **argv) +void dict_cli(int argc, char **argv) { VSTRING *keybuf = vstring_alloc(1); VSTRING *inbuf = vstring_alloc(1); diff --git a/postfix/src/util/dict_open.c b/postfix/src/util/dict_open.c index c3b90d497..6e2e58ba0 100644 --- a/postfix/src/util/dict_open.c +++ b/postfix/src/util/dict_open.c @@ -610,7 +610,7 @@ void dict_type_override(DICT *dict, const char *type) */ int main(int argc, char **argv) { - dict_test(argc, argv); + dict_cli(argc, argv); return (0); } diff --git a/postfix/src/util/dict_pipe_test.c b/postfix/src/util/dict_pipe_test.c new file mode 100644 index 000000000..4a566ef33 --- /dev/null +++ b/postfix/src/util/dict_pipe_test.c @@ -0,0 +1,106 @@ + /* + * Test program to exercise dict_pipe.c. See ptest_main.h for a documented + * example. + */ + + /* + * System library. + */ +#include +#include + + /* + * Utility library. + */ +#include +#include +#include +#include + + /* + * Test library. + */ +#include + + /* + * The following needs to be large enough to include a null terminator in + * every ptestcase.want field. + */ +#define MAX_PROBE 5 + +struct probe { + const char *query; + const char *want_value; + int want_error; +}; + +typedef struct PTEST_CASE { + const char *testname; + void (*action) (PTEST_CTX *, const struct PTEST_CASE *); + const char *type_name; + const struct probe probes[MAX_PROBE]; +} PTEST_CASE; + +#define STR_OR_NULL(s) ((s) ? (s) : "null") + +static void test_dict_pipe(PTEST_CTX *t, const struct PTEST_CASE *tp) +{ + DICT *dict; + const struct probe *pp; + const char *got_value; + int got_error; + + if ((dict = dict_open(tp->type_name, O_RDONLY, 0)) == 0) + ptest_fatal(t, "dict_open(\"%s\", O_RDONLY, 0) failed: %m", + tp->type_name); + for (pp = tp->probes; pp < tp->probes + MAX_PROBE && pp->query != 0; pp++) { + got_value = dict_get(dict, pp->query); + got_error = dict->error; + if (got_value == 0 && pp->want_value == 0) + continue; + if (got_value == 0 || pp->want_value == 0) { + ptest_error(t, "dict_get(dict, \"%s\"): got '%s', want '%s'", + pp->query, STR_OR_NULL(got_value), + STR_OR_NULL(pp->want_value)); + break; + } + if (strcmp(got_value, pp->want_value) != 0) { + ptest_error(t, "dict_get(dict, \"%s\"): got '%s', want '%s'", + pp->query, got_value, pp->want_value); + } + if (got_error != pp->want_error) + ptest_error(t, "dict_get(dict,\"%s\") error: got %d, want %d", + pp->query, got_error, pp->want_error); + } + dict_free(dict); +} + +static const PTEST_CASE ptestcases[] = { + { + /* name */ "successful lookup: inline map + inline map", + /* action */ test_dict_pipe, + /* type_name */ "pipemap:{inline:{k1=v1,k2=v2},inline:{v2=v3}}", + /* probes */ { + {"k0", 0}, + {"k1", 0,}, + {"k2", "v3"}, + }, + }, { + /* name */ "error propagation: inline map + fail map", + /* action */ test_dict_pipe, + /* type_name */ "pipemap:{inline:{k1=v1},fail:fail}", + /* probes */ { + {"k0", 0, 0}, + {"k1", 0, DICT_STAT_ERROR}, + }, + }, { + /* name */ "error propagation: fail map + inline map", + /* action */ test_dict_pipe, + /* type_name */ "pipemap:{fail:fail,inline:{k1=v1}}", + /* probes */ { + {"k1", 0, DICT_STAT_ERROR}, + }, + }, +}; + +#include diff --git a/postfix/src/util/dict_pipe_test.in b/postfix/src/util/dict_pipe_test.in deleted file mode 100644 index 9626dcd3f..000000000 --- a/postfix/src/util/dict_pipe_test.in +++ /dev/null @@ -1,9 +0,0 @@ -${VALGRIND} ./dict_open 'pipemap:{inline:{k1=v1,k2=v2},inline:{v2=v3}}' read < get k0 -k0: not found -> get k1 -k1: not found -> get k2 -k2=v3 -+ ./dict_open pipemap:{inline:{k1=v1},fail:fail} read -owner=trusted (uid=2147483647) -> get k0 -k0: not found -> get k1 -k1: error diff --git a/postfix/src/util/dict_stream.c b/postfix/src/util/dict_stream.c index e28ad71fd..90ba03617 100644 --- a/postfix/src/util/dict_stream.c +++ b/postfix/src/util/dict_stream.c @@ -145,130 +145,3 @@ VSTREAM *dict_stream_open(const char *dict_type, const char *mapname, return (map_fp); } } - -#ifdef TEST - -#include - -int main(int argc, char **argv) -{ - struct testcase { - const char *title; - const char *mapname; /* starts with brace */ - const char *expect_err; /* null or message */ - const char *expect_cont; /* null or content */ - }; - -#define EXP_NOERR 0 -#define EXP_NOCONT 0 - -#define STRING_OR(s, text_if_null) ((s) ? (s) : (text_if_null)) -#define DICT_TYPE_TEST "test" - - const char rule_spec_error[] = DICT_TYPE_TEST " map: " - "syntax error after '}' in \"{blah blah}x\""; - const char inline_config_error[] = DICT_TYPE_TEST " map: " - "syntax error after '}' in \"{{foo bar}, {blah blah}}x\""; - struct testcase testcases[] = { - {"normal", - "{{foo bar}, {blah blah}}", EXP_NOERR, "foo bar\nblah blah\n" - }, - {"trims leading/trailing wsp around rule-text", - "{{ foo bar }, { blah blah }}", EXP_NOERR, "foo bar\nblah blah\n" - }, - {"trims leading/trailing comma-wsp around rule-spec", - "{, ,{foo bar}, {blah blah}, ,}", EXP_NOERR, "foo bar\nblah blah\n" - }, - {"empty inline-file", - "{, }", EXP_NOERR, "" - }, - {"propagates extpar error for inline-file", - "{{foo bar}, {blah blah}}x", inline_config_error, EXP_NOCONT - }, - {"propagates extpar error for rule-spec", - "{{foo bar}, {blah blah}x}", rule_spec_error, EXP_NOCONT - }, - 0, - }; - struct testcase *tp; - VSTRING *act_err = 0; - VSTRING *act_cont = vstring_alloc(100); - VSTREAM *fp; - struct stat st; - ssize_t exp_len; - ssize_t act_len; - int pass; - int fail; - - for (pass = fail = 0, tp = testcases; tp->title; tp++) { - int test_passed = 0; - - msg_info("RUN test case %ld %s", (long) (tp - testcases), tp->title); - -#if 0 - msg_info("title=%s", tp->title); - msg_info("mapname=%s", tp->mapname); - msg_info("expect_err=%s", STRING_OR_NULL(tp->expect_err)); - msg_info("expect_cont=%s", STRINGOR_NULL(tp->expect_cont)); -#endif - - if (act_err) - VSTRING_RESET(act_err); - fp = dict_stream_open(DICT_TYPE_TEST, tp->mapname, O_RDONLY, - 0, &st, &act_err); - if (fp) { - if (tp->expect_err) { - msg_warn("test case %s: got stream, expected error", tp->title); - } else if (!tp->expect_err && act_err && LEN(act_err) > 0) { - msg_warn("test case %s: got error '%s', expected noerror", - tp->title, STR(act_err)); - } else if (!tp->expect_cont) { - msg_warn("test case %s: got stream, expected nostream", - tp->title); - } else { - exp_len = strlen(tp->expect_cont); - if ((act_len = vstream_fread_buf(fp, act_cont, 2 * exp_len)) < 0) { - msg_warn("test case %s: content read error", tp->title); - } else { - VSTRING_TERMINATE(act_cont); - if (strcmp(tp->expect_cont, STR(act_cont)) != 0) { - msg_warn("test case %s: got content '%s', expected '%s'", - tp->title, STR(act_cont), tp->expect_cont); - } else { - test_passed = 1; - } - } - } - } else { - if (!tp->expect_err) { - msg_warn("test case %s: got nostream, expected noerror", - tp->title); - } else if (tp->expect_cont) { - msg_warn("test case %s: got nostream, expected stream", - tp->title); - } else if (strcmp(STR(act_err), tp->expect_err) != 0) { - msg_warn("test case %s: got error '%s', expected '%s'", - tp->title, STR(act_err), tp->expect_err); - } else { - test_passed = 1; - } - - } - if (test_passed) { - msg_info("PASS test %ld", (long) (tp - testcases)); - pass++; - } else { - msg_info("FAIL test %ld", (long) (tp - testcases)); - fail++; - } - if (fp) - vstream_fclose(fp); - } - if (act_err) - vstring_free(act_err); - vstring_free(act_cont); - msg_info("PASS=%d FAIL=%d", pass, fail); - return (fail > 0); -} - -#endif /* TEST */ diff --git a/postfix/src/util/dict_stream.ref b/postfix/src/util/dict_stream.ref deleted file mode 100644 index 87c30e59b..000000000 --- a/postfix/src/util/dict_stream.ref +++ /dev/null @@ -1,13 +0,0 @@ -unknown: RUN test case 0 normal -unknown: PASS test 0 -unknown: RUN test case 1 trims leading/trailing wsp around rule-text -unknown: PASS test 1 -unknown: RUN test case 2 trims leading/trailing comma-wsp around rule-spec -unknown: PASS test 2 -unknown: RUN test case 3 empty inline-file -unknown: PASS test 3 -unknown: RUN test case 4 propagates extpar error for inline-file -unknown: PASS test 4 -unknown: RUN test case 5 propagates extpar error for rule-spec -unknown: PASS test 5 -unknown: PASS=6 FAIL=0 diff --git a/postfix/src/util/dict_stream_test.c b/postfix/src/util/dict_stream_test.c new file mode 100644 index 000000000..47332b422 --- /dev/null +++ b/postfix/src/util/dict_stream_test.c @@ -0,0 +1,114 @@ + /* + * Test program to exercise dict_stream.c. See ptest_main.h for a documented + * example. + */ + + /* + * System library. + */ +#include +#include + + /* + * Utility library. + */ +#include +#include +#include + + /* + * Test library. + */ +#include + +typedef struct PTEST_CASE { + const char *testname; + void (*action) (PTEST_CTX *t, const struct PTEST_CASE *); + const char *mapname; /* starts with brace */ + const char *want_err; /* null or message */ + const char *want_cont; /* null or content */ +} PTEST_CASE; + +#define DICT_TYPE_TEST "test" +#define LEN(x) VSTRING_LEN(x) +#define STR(x) vstring_str(x) + +static void test_dict_stream(PTEST_CTX *t, const PTEST_CASE *tp) +{ + VSTRING *got_err = 0; + VSTRING *got_cont = vstring_alloc(100); + VSTREAM *fp; + struct stat st; + ssize_t want_len; + ssize_t got_len; + + fp = dict_stream_open(DICT_TYPE_TEST, tp->mapname, O_RDONLY, + 0, &st, &got_err); + if (fp) { + if (tp->want_err) { + ptest_error(t, "got stream, want error '%s'", tp->want_err); + } else if (!tp->want_err && got_err && LEN(got_err) > 0) { + ptest_error(t, "got error '%s', want noerror", STR(got_err)); + } else if (!tp->want_cont) { + ptest_error(t, "got stream, expected nostream"); + } else { + want_len = strlen(tp->want_cont); + if ((got_len = vstream_fread_buf(fp, got_cont, 2 * want_len)) < 0) { + ptest_error(t, "content read error"); + } else { + VSTRING_TERMINATE(got_cont); + if (strcmp(tp->want_cont, STR(got_cont)) != 0) { + ptest_error(t, "got content '%s', want '%s'", + STR(got_cont), tp->want_cont); + } + } + } + } else { + if (!tp->want_err) { + ptest_error(t, "got nostream, want noerror"); + } else if (tp->want_cont) { + ptest_error(t, "got nostream, want stream"); + } else if (strcmp(STR(got_err), tp->want_err) != 0) { + ptest_error(t, "got error '%s', want '%s'", + STR(got_err), tp->want_err); + } + } + if (fp) + vstream_fclose(fp); + if (got_err) + vstring_free(got_err); + vstring_free(got_cont); +} + + +#define WANT_NOERR 0 +#define WANT_NOCONT 0 + +const char rule_spec_error[] = DICT_TYPE_TEST " map: " +"syntax error after '}' in \"{blah blah}x\""; + +const char inline_config_error[] = DICT_TYPE_TEST " map: " +"syntax error after '}' in \"{{foo bar}, {blah blah}}x\""; + +static PTEST_CASE ptestcases[] = { + {"normal", test_dict_stream, + "{{foo bar}, {blah blah}}", WANT_NOERR, "foo bar\nblah blah\n" + }, + {"trims leading/trailing wsp around rule-text", test_dict_stream, + "{{ foo bar }, { blah blah }}", WANT_NOERR, "foo bar\nblah blah\n" + }, + {"trims leading/trailing comma-wsp around rule-spec", test_dict_stream, + "{, ,{foo bar}, {blah blah}, ,}", WANT_NOERR, "foo bar\nblah blah\n" + }, + {"empty inline-file", test_dict_stream, + "{, }", WANT_NOERR, "" + }, + {"propagates extpar error for inline-file", test_dict_stream, + "{{foo bar}, {blah blah}}x", inline_config_error, WANT_NOCONT + }, + {"propagates extpar error for rule-spec", test_dict_stream, + "{{foo bar}, {blah blah}x}", rule_spec_error, WANT_NOCONT + }, +}; + +#include diff --git a/postfix/src/util/dict_union_test.c b/postfix/src/util/dict_union_test.c new file mode 100644 index 000000000..845dc9b31 --- /dev/null +++ b/postfix/src/util/dict_union_test.c @@ -0,0 +1,104 @@ + /* + * Test program to exercise dict_union.c. See ptest_main.h for a documented + * example. + */ + + /* + * System library. + */ +#include +#include + + /* + * Utility library. + */ +#include +#include +#include +#include + + /* + * Test library. + */ +#include + + /* + * The following needs to be large enough to include a null terminator in + * every ptestcase.want field. + */ +#define MAX_PROBE 5 + +struct probe { + const char *query; + const char *want_value; + int want_error; +}; + +typedef struct PTEST_CASE { + const char *testname; + void (*action) (PTEST_CTX *, const struct PTEST_CASE *); + const char *type_name; + const struct probe probes[MAX_PROBE]; +} PTEST_CASE; + +#define STR_OR_NULL(s) ((s) ? (s) : "null") + +static void test_dict_union(PTEST_CTX *t, const struct PTEST_CASE *tp) +{ + DICT *dict; + const struct probe *pp; + const char *got_value; + int got_error; + + if ((dict = dict_open(tp->type_name, O_RDONLY, 0)) == 0) + ptest_fatal(t, "dict_open(\"%s\", O_RDONLY, 0) failed: %m", + tp->type_name); + for (pp = tp->probes; pp < tp->probes + MAX_PROBE && pp->query != 0; pp++) { + got_value = dict_get(dict, pp->query); + got_error = dict->error; + if (got_value == 0 && pp->want_value == 0) + continue; + if (got_value == 0 || pp->want_value == 0) { + ptest_error(t, "dict_get(dict, \"%s\"): got '%s', want '%s'", + pp->query, STR_OR_NULL(got_value), + STR_OR_NULL(pp->want_value)); + break; + } + if (strcmp(got_value, pp->want_value) != 0) { + ptest_error(t, "dict_get(dict, \"%s\"): got '%s', want '%s'", + pp->query, got_value, pp->want_value); + } + if (got_error != pp->want_error) + ptest_error(t, "dict_get(dict,\"%s\") error: got %d, want %d", + pp->query, got_error, pp->want_error); + } + dict_free(dict); +} + +static const PTEST_CASE ptestcases[] = { + { + /* name */ "successful lookup: static map + inline map", + /* action */ test_dict_union, + /* type_name */ "unionmap:{static:one,inline:{foo=two}}", + /* probes */ { + {"foo", "one,two"}, + {"bar", "one"}, + }, + }, { + /* name */ "error propagation: static map + fail map", + /* action */ test_dict_union, + /* type_name */ "unionmap:{static:one,fail:fail}", + /* probes */ { + {"foo", 0, DICT_STAT_ERROR}, + }, + }, { + /* name */ "error propagation: fail map + static map", + /* action */ test_dict_union, + /* type_name */ "unionmap:{fail:fail,static:one}", + /* probes */ { + {"foo", 0, DICT_STAT_ERROR}, + }, + }, +}; + +#include diff --git a/postfix/src/util/dict_union_test.in b/postfix/src/util/dict_union_test.in deleted file mode 100644 index 9d111d43e..000000000 --- a/postfix/src/util/dict_union_test.in +++ /dev/null @@ -1,7 +0,0 @@ -${VALGRIND} ./dict_open 'unionmap:{static:one,static:two,inline:{foo=three}}' read < get foo -foo=one,two,three -> get bar -bar=one,two -+ ./dict_open unionmap:{static:one,fail:fail} read -owner=trusted (uid=2147483647) -> get foo -foo: error diff --git a/postfix/src/util/find_inet_service.c b/postfix/src/util/find_inet_service.c new file mode 100644 index 000000000..29948c1b8 --- /dev/null +++ b/postfix/src/util/find_inet_service.c @@ -0,0 +1,68 @@ +/*++ +/* NAME +/* find_inet_service 3 +/* SUMMARY +/* TCP service lookup +/* SYNOPSIS +/* #include +/* +/* int find_inet_service(service, proto) +/* const char *service; +/* const char *proto; +/* DESCRIPTION +/* find_inet_service() looks up the numerical TCP/IP port (in +/* host byte order) for the specified service. If the service +/* is in numerical form, then that is converted instead. +/* +/* TCP services are mapped with known_tcp_ports(3). +/* DIAGNOSTICS +/* find_inet_service() returns -1 when the service is not +/* found, or when a numerical service is out of range. +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* Google, Inc. +/* 111 8th Avenue +/* New York, NY 10011, USA +/*--*/ + +/* System libraries. */ + +#include +#include +#include +#include +#include + +/* Application-specific. */ + +#include +#include +#include +#include +#include + +/* find_inet_service - translate numerical or symbolic service name */ + +int find_inet_service(const char *service, const char *protocol) +{ + struct servent *sp; + unsigned long lport; + char *cp; + + if (strcmp(protocol, "tcp") == 0) + service = filter_known_tcp_port(service); + if (alldig(service)) { + lport = sane_strtoul(service, &cp, 10); + if (*cp != '\0' || errno == ERANGE || lport > 65535) + return (-1); /* bad numerical service */ + return ((int) lport); + } else { + if ((sp = getservbyname(service, protocol)) == 0) + return (-1); /* bad symbolic service */ + return (ntohs(sp->s_port)); + } +} diff --git a/postfix/src/util/find_inet_service.h b/postfix/src/util/find_inet_service.h new file mode 100644 index 000000000..0f1169728 --- /dev/null +++ b/postfix/src/util/find_inet_service.h @@ -0,0 +1,30 @@ +#ifndef _FIND_TCP_PORT_H_INCLUDED_ +#define _FIND_TCP_PORT_H_INCLUDED_ + +/*++ +/* NAME +/* find_inet_service 3h +/* SUMMARY +/* TCP service lookup +/* SYNOPSIS +/* #include +/* DESCRIPTION +/* .nf + + /* + * External interface. + */ +extern int find_inet_service(const char *, const char *); + +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* Google, Inc. +/* 111 8th Avenue +/* New York, NY 10011, USA +/*--*/ + +#endif diff --git a/postfix/src/util/find_inet_service_test.c b/postfix/src/util/find_inet_service_test.c new file mode 100644 index 000000000..11b60b39c --- /dev/null +++ b/postfix/src/util/find_inet_service_test.c @@ -0,0 +1,119 @@ + /* + * Test program to exercise find_inet_service.c. See pmock_expect_test.c and + * ptest_main.h for a documented example. + */ + + /* + * System library. + */ +#include + + /* + * Utility library + */ +#include +#include +#include + + /* + * Test library. + */ +#include +#include + +struct association { + const char *lhs; /* service name */ + const char *rhs; /* service port */ +}; + +typedef struct PTEST_CASE { + const char *testname; /* Human-readable description */ + void (*action) (PTEST_CTX *, const struct PTEST_CASE *); + struct association associations[10]; + const char *service; + const char *proto; + int want_port; /* expected port, host byte order */ + int needs_mock; +} PTEST_CASE; + +static void test_find_inet_service(PTEST_CTX *t, const PTEST_CASE *tp) +{ + struct servent *want_ent = 0; + const struct association *ap; + int got_port; + const char *err; + + /* + * Set up expectations. Note that the test infrastructure will catch + * fatal errors and panics for us. + */ + clear_known_tcp_ports(); + for (err = 0, ap = tp->associations; err == 0 && ap->lhs != 0; ap++) + err = add_known_tcp_port(ap->lhs, ap->rhs); + if (err != 0) + ptest_fatal(t, "add_known_tcp_port: got err '%s'", err); + if (tp->needs_mock) { + if (tp->want_port != -1) + want_ent = make_servent(tp->service, tp->want_port, tp->proto); + else + want_ent = 0; + expect_getservbyname(1, want_ent, tp->service, tp->proto); + } + + /* + * Make the call and verify the result. If the call fails with a fatal + * error or panic, the test infrastructure will verify that the logging + * is as expected. + */ + got_port = find_inet_service(tp->service, tp->proto); + if (got_port != tp->want_port) { + ptest_error(t, "find_inet_service: got port %d, want %d", + got_port, tp->want_port); + } + if (want_ent) + free_servent(want_ent); +} + +const PTEST_CASE ptestcases[] = { + { + "good-symbolic", + test_find_inet_service, + /* association */ {{"foobar", "25252"}, 0}, + /* service */ "foobar", + /* proto */ "tcp", + /* want_port */ 25252, + /* needs mock */ 0, + }, + { + "good-numeric", + test_find_inet_service, + /* association */ {{"foobar", "25252"}, 0}, + /* service */ "25252", + /* proto */ "tcp", + /* want_port */ 25252, + /* needs mock */ 0, + }, + { + "bad-symbolic", + test_find_inet_service, + /* association */ {{"foobar", "25252"}, 0}, + /* service */ "an-impossible-name", + /* proto */ "tcp", + /* want_port */ -1, + /* needs mock */ 1, + }, + { + "bad-numeric", + test_find_inet_service, + /* association */ {{"foobar", "25252"}, 0}, + /* service */ "123456", + /* proto */ "tcp", + /* want_port */ -1, + /* needs mock */ 0, + }, +}; + + /* + * Test library. + */ +#include diff --git a/postfix/src/util/hash_fnv.c b/postfix/src/util/hash_fnv.c index b4d7b304b..6a7283255 100644 --- a/postfix/src/util/hash_fnv.c +++ b/postfix/src/util/hash_fnv.c @@ -137,101 +137,3 @@ HASH_FNV_T hash_fnvz(const char *src) } return (hash); } - -#ifdef TEST -#include -#include -#include - -int main(void) -{ - int pass = 0; - int fail = 0; - - /* - * Sanity check. - */ -#ifdef STRICT_FNV1A - msg_fatal("This test requires no STRICT_FNV1A"); -#endif - - /* - * Force unseeded hash, to make tests predictable. - */ - if (putenv("NORANDOMIZE=") != 0) - msg_fatal("putenv(\"NORANDOMIZE=\"): %m"); - - /* - * Test: hashing produces the expected results. - */ - { - struct testcase { - HASH_FNV_T hval; - const char *str; - }; - static struct testcase testcases[] = - { -#ifdef USE_FNV_32BIT - 0x1c00fc06UL, "overdeeply", - 0x1c00fc06UL, "undescript", - 0x1e1e52a4UL, "fanfold", - 0x1e1e52a4UL, "phrensied", -#else - 0xda19999ec0bda706ULL, "overdeeply", - 0xd7b9e43f26396a66ULL, "undescript", - 0xa50c585d385a2604ULL, "fanfold", - 0x1ec3ef9bb2b734a4ULL, "phrensied", -#endif - 0, - }; - struct testcase *tp; - HASH_FNV_T hval; - int test_failed; - - for (tp = testcases; tp->str; tp++) { - test_failed = 0; - if ((hval = hash_fnvz(tp->str)) != tp->hval) { - msg_warn("hash_fnv(\"%s\") want %lu, got: %lu", - tp->str, (unsigned long) tp->hval, - (unsigned long) hval); - test_failed = 1; - } - if (test_failed) { - fail += 1; - msg_info("FAIL: %s", tp->str); - } else { - pass += 1; - msg_info("PASS: %s", tp->str); - } - } - } - - /* - * Test: hash_fnvz(s) is equivalent to hash_fnv(s, strlen(s)). No need to - * verify the actual result; we already did that above. - */ - { - const char *strval = "foobar"; - HASH_FNV_T h1 = hash_fnv(strval, strlen(strval)); - HASH_FNV_T h2 = hash_fnvz(strval); - - if (h1 == h2) { - pass += 1; - msg_info("PASS: hash_fnvz(\"%s\") == hash_fnv(\"%s\", %ld)", - strval, strval, (long) strlen(strval)); - } else { - fail += 1; - msg_info("FAIL: hash_fnvz(\"%s\") == hash_fnv(\"%s\", %ld)", - strval, strval, (long) strlen(strval)); - } - } - - - /* - * Wrap up. - */ - msg_info("PASS=%d FAIL=%d", pass, fail); - return (fail != 0); -} - -#endif diff --git a/postfix/src/util/hash_fnv_test.c b/postfix/src/util/hash_fnv_test.c new file mode 100644 index 000000000..6255a1d81 --- /dev/null +++ b/postfix/src/util/hash_fnv_test.c @@ -0,0 +1,81 @@ + /* + * Test program to exercise the hash_fnv implementation. See comments in + * ptest_main.h for a documented example. + */ + + /* + * System library. + */ +#include +#include +#include + + /* + * Utility library. + */ +#include +#include + + /* + * Test library. + */ +#include + +typedef struct PTEST_CASE { + const char *testname; + void (*action) (PTEST_CTX *t, const struct PTEST_CASE *tp); + HASH_FNV_T want_hval; + const char *str; +} PTEST_CASE; + +static void setup_test(void) +{ + + /* + * Sanity check. + */ +#ifdef STRICT_FNV1A + msg_fatal("This test requires no STRICT_FNV1A"); +#endif + + /* + * Force unseeded hash, to make tests predictable. + */ + if (putenv("NORANDOMIZE=") != 0) + msg_fatal("putenv(\"NORANDOMIZE=\"): %m"); +} + +static void test_known_input(PTEST_CTX *t, const PTEST_CASE *tp) +{ + HASH_FNV_T got_hval; + + setup_test(); + + if ((got_hval = hash_fnvz(tp->str)) != tp->want_hval) + ptest_error(t, "hash_fnvz(\"%s\") got %lu, want %lu", + tp->str, (unsigned long) got_hval, + (unsigned long) tp->want_hval); + + if ((got_hval = hash_fnv(tp->str, strlen(tp->str))) != tp->want_hval) + ptest_error(t, "hash_fnv(\"%s\", strlen(\"%s\")) got %lu, want %lu", + tp->str, tp->str, (unsigned long) got_hval, + (unsigned long) tp->want_hval); +} + + +static const PTEST_CASE ptestcases[] = +{ +#ifdef USE_FNV_32BIT + "test_known_input_overdeeply", test_known_input, 0x1c00fc06UL, "overdeeply", + "test_known_input_undescript", test_known_input, 0x1c00fc06UL, "undescript", + "test_known_input_fanfold", test_known_input, 0x1e1e52a4UL, "fanfold", + "test_known_input_phrensied", test_known_input, 0x1e1e52a4UL, "phrensied", +#else + "test_known_input_overdeeply", test_known_input, 0xda19999ec0bda706ULL, "overdeeply", + "test_known_input_undescript", test_known_input, 0xd7b9e43f26396a66ULL, "undescript", + "test_known_input_fanfold", test_known_input, 0xa50c585d385a2604ULL, "fanfold", + "test_known_input_phrensied", test_known_input, 0x1ec3ef9bb2b734a4ULL, "phrensied", +#endif +}; + +#include diff --git a/postfix/src/util/inet_addr_list.c b/postfix/src/util/inet_addr_list.c index e579b1760..1162c77f3 100644 --- a/postfix/src/util/inet_addr_list.c +++ b/postfix/src/util/inet_addr_list.c @@ -167,9 +167,8 @@ static void inet_addr_list_print(INET_ADDR_LIST *list) int main(int argc, char **argv) { INET_ADDR_LIST list; - INET_PROTO_INFO *proto_info; - proto_info = inet_proto_init(argv[0], INET_PROTO_NAME_ALL); + (void) inet_proto_init(argv[0], INET_PROTO_NAME_ALL); inet_addr_list_init(&list); while (--argc && *++argv) if (inet_addr_host(&list, *argv) == 0) diff --git a/postfix/src/util/known_tcp_ports.c b/postfix/src/util/known_tcp_ports.c index 1d524d4d3..3ddc084fd 100644 --- a/postfix/src/util/known_tcp_ports.c +++ b/postfix/src/util/known_tcp_ports.c @@ -78,6 +78,8 @@ const char *add_known_tcp_port(const char *name, const char *port) return ("numerical service name"); if (!alldig(port)) return ("non-numerical service port"); + if (strlen(port) > 5 || (strlen(port) == 5 && strcmp(port, "65535") > 0)) + return ("port number out of range"); if (known_tcp_ports == 0) known_tcp_ports = htable_create(10); if (htable_locate(known_tcp_ports, name) != 0) @@ -139,115 +141,3 @@ char *export_known_tcp_ports(VSTRING *out) VSTRING_TERMINATE(out); return (STR(out)); } - -#ifdef TEST - -#include - -struct association { - const char *lhs; /* service name */ - const char *rhs; /* service port */ -}; - -struct probe { - const char *query; /* query */ - const char *exp_reply; /* expected reply */ -}; - -struct test_case { - const char *label; /* identifies test case */ - struct association associations[10]; - const char *exp_err; /* expected error */ - const char *exp_export; /* expected export output */ - struct probe probes[10]; -}; - -struct test_case test_cases[] = { - {"good", - /* association */ {{"smtp", "25"}, {"lmtp", "24"}, 0}, - /* error */ 0, - /* export */ "lmtp=24 smtp=25", - /* probe */ {{"smtp", "25"}, {"1", "1"}, {"x", "x"}, {"lmtp", "24"}, 0} - }, - {"duplicate lhs", - /* association */ {{"smtp", "25"}, {"smtp", "100"}, 0}, - /* error */ "duplicate service name" - }, - {"numerical lhs", - /* association */ {{"100", "100"}, 0}, - /* error */ "numerical service name" - }, - {"symbolic rhs", - /* association */ {{"smtp", "lmtp"}, 0}, - /* error */ "non-numerical service port" - }, - {"uninitialized", - /* association */ {0}, - /* error */ 0, - /* export */ "", - /* probe */ {{"smtp", "smtp"}, {"1", "1"}, {"x", "x"}, 0} - }, - 0, -}; - -int main(int argc, char **argv) -{ - VSTRING *export_buf; - struct test_case *tp; - struct association *ap; - struct probe *pp; - int pass = 0; - int fail = 0; - const char *err; - int test_failed; - const char *reply; - const char *export; - -#define STRING_OR_NULL(s) ((s) ? (s) : "(null)") - - export_buf = vstring_alloc(100); - for (tp = test_cases; tp->label != 0; tp++) { - test_failed = 0; - for (err = 0, ap = tp->associations; err == 0 && ap->lhs != 0; ap++) - err = add_known_tcp_port(ap->lhs, ap->rhs); - if (!err != !tp->exp_err) { - msg_warn("test case %s: got error: \"%s\", want: \"%s\"", - tp->label, STRING_OR_NULL(err), STRING_OR_NULL(tp->exp_err)); - test_failed = 1; - } else if (err != 0) { - if (strcmp(err, tp->exp_err) != 0) { - msg_warn("test case %s: got err: \"%s\", want: \"%s\"", - tp->label, err, tp->exp_err); - test_failed = 1; - } - } else { - export = export_known_tcp_ports(export_buf); - if (strcmp(export, tp->exp_export) != 0) { - msg_warn("test case %s: got export: \"%s\", want: \"%s\"", - tp->label, export, tp->exp_export); - test_failed = 1; - } - for (pp = tp->probes; test_failed == 0 && pp->query != 0; pp++) { - reply = filter_known_tcp_port(pp->query); - if (strcmp(reply, pp->exp_reply) != 0) { - msg_warn("test case %s: got reply: \"%s\", want: \"%s\"", - tp->label, reply, pp->exp_reply); - test_failed = 1; - } - } - } - clear_known_tcp_ports(); - if (test_failed) { - msg_info("%s: FAIL", tp->label); - fail++; - } else { - msg_info("%s: PASS", tp->label); - pass++; - } - } - msg_info("PASS=%d FAIL=%d", pass, fail); - vstring_free(export_buf); - exit(fail != 0); -} - -#endif diff --git a/postfix/src/util/known_tcp_ports.ref b/postfix/src/util/known_tcp_ports.ref deleted file mode 100644 index adcf18208..000000000 --- a/postfix/src/util/known_tcp_ports.ref +++ /dev/null @@ -1,6 +0,0 @@ -unknown: good: PASS -unknown: duplicate lhs: PASS -unknown: numerical lhs: PASS -unknown: symbolic rhs: PASS -unknown: uninitialized: PASS -unknown: PASS=5 FAIL=0 diff --git a/postfix/src/util/known_tcp_ports_test.c b/postfix/src/util/known_tcp_ports_test.c new file mode 100644 index 000000000..bdd1084e8 --- /dev/null +++ b/postfix/src/util/known_tcp_ports_test.c @@ -0,0 +1,116 @@ + /* + * Test program to exercise known_tcp_ports.c. See ptest_main.h for a + * documented example. + */ + + /* + * System library. + */ +#include +#include + + /* + * Utility library + */ +#include +#include + + /* + * Test library. + */ +#include + +struct association { + const char *lhs; /* service name */ + const char *rhs; /* service port */ +}; + +struct probe { + const char *query; /* query */ + const char *want_reply; /* expected reply */ +}; + +typedef struct PTEST_CASE { + const char *testname; /* identifies test case */ + void (*action) (PTEST_CTX *, const struct PTEST_CASE *); + struct association associations[10]; + const char *want_err; /* expected error */ + const char *want_export; /* expected export output */ + struct probe probes[10]; +} PTEST_CASE; + +static void test_known_tcp_ports(PTEST_CTX *t, const PTEST_CASE *tp) +{ + VSTRING *export_buf; + const struct association *ap; + const struct probe *pp; + const char *got_err; + const char *got_reply; + const char *got_export; + +#define STRING_OR_NULL(s) ((s) ? (s) : "(null)") + + export_buf = vstring_alloc(100); + for (got_err = 0, ap = tp->associations; got_err == 0 && ap->lhs != 0; ap++) + got_err = add_known_tcp_port(ap->lhs, ap->rhs); + if (!got_err != !tp->want_err) { + ptest_error(t, "got error '%s', want '%s'", + STRING_OR_NULL(got_err), STRING_OR_NULL(tp->want_err)); + } else if (got_err != 0) { + if (strcmp(got_err, tp->want_err) != 0) { + ptest_error(t, "got err '%s', want '%s'", got_err, tp->want_err); + } + } else { + got_export = export_known_tcp_ports(export_buf); + if (strcmp(got_export, tp->want_export) != 0) { + ptest_error(t, "got export '%s', want '%s'", + got_export, tp->want_export); + } + for (pp = tp->probes; pp->query != 0; pp++) { + got_reply = filter_known_tcp_port(pp->query); + if (strcmp(got_reply, pp->want_reply) != 0) { + ptest_error(t, "got reply '%s', want '%s'", + got_reply, pp->want_reply); + break; + } + } + } + clear_known_tcp_ports(); + vstring_free(export_buf); +} + +const PTEST_CASE ptestcases[] = { + {"good", test_known_tcp_ports, + /* association */ {{"smtp", "25"}, {"lmtp", "24"}, 0}, + /* error */ 0, + /* export */ "lmtp=24 smtp=25", + /* probe */ {{"smtp", "25"}, {"1", "1"}, {"x", "x"}, {"lmtp", "24"}, 0} + }, + {"duplicate lhs", test_known_tcp_ports, + /* association */ {{"smtp", "25"}, {"smtp", "100"}, 0}, + /* error */ "duplicate service name" + }, + {"numerical lhs", test_known_tcp_ports, + /* association */ {{"100", "100"}, 0}, + /* error */ "numerical service name" + }, + {"symbolic rhs", test_known_tcp_ports, + /* association */ {{"smtp", "lmtp"}, 0}, + /* error */ "non-numerical service port" + }, + {"uninitialized", test_known_tcp_ports, + /* association */ {0}, + /* error */ 0, + /* export */ "", + /* probe */ {{"smtp", "smtp"}, {"1", "1"}, {"x", "x"}, 0} + }, + {"too large", test_known_tcp_ports, + /* association */ {{"one", "65535"}, {"two", "65536"}, 0}, + /* error */ "port number out of range", + }, +}; + + /* + * Test library. + */ +#include diff --git a/postfix/src/util/msg.c b/postfix/src/util/msg.c index 70c6eab08..efefd9893 100644 --- a/postfix/src/util/msg.c +++ b/postfix/src/util/msg.c @@ -59,6 +59,17 @@ /* int count; /* /* void msg_error_clear() +/* +/* int msg_setjmp( +/* MSG_JMP_BUF *bufp) +/* +/* void msg_longjmp( +/* int value) +/* +/* void msg_resetjmp( +/* MSG_JMP_BUF *bufp) +/* +/* void msg_clearjmp(void) /* DESCRIPTION /* This module reports diagnostics. By default, diagnostics are sent /* to the standard error stream, but the disposition can be changed @@ -131,6 +142,26 @@ /* This protection exists under the condition that these /* specific resources are accessed exclusively via the msg_info() /* etc. functions. +/* EXCEPTIONS AND NON-PRODUCTION TESTS +/* The default action for msg_fatal*() and msg_panic is to +/* terminate the program. In order to support non-production +/* tests, the following macros implement support for long +/* jumps. +/* +/* msg_setjmp() specifies a caller-specified buffer and saves +/* state for a future long jump. The buffer lifetime must +/* extend to the next msg_resetjmp() or msg_clearjmp() call. +/* +/* In-between the msg_setjmp() and msg_clearjmp() calls, +/* msg_fatal*() and msg_panic() will perform a long jump instead +/* of terminating the program. +/* +/* msg_resetjmp() should be used to restore state that was +/* previously saved with msg_setjmp(). The buffer lifetime +/* must extend to the next msg_resetjmp() or msg_clearjmp() +/* call. +/* .ad +/* .fi /* SEE ALSO /* msg_output(3) specify diagnostics disposition /* msg_stdio(3) direct diagnostics to standard I/O stream @@ -157,6 +188,11 @@ /* IBM T.J. Watson Research /* P.O. Box 704 /* Yorktown Heights, NY 10598, USA +/* +/* Wietse Venema +/* Google, Inc. +/* 111 8th Avenue +/* New York, NY 10011, USA /*--*/ /* System libraries. */ @@ -165,6 +201,7 @@ #include #include #include +#include /* Application-specific. */ @@ -176,6 +213,12 @@ */ int msg_verbose = 0; + /* + * Semi-private state. Managed with msg_setjmp(), msg_longjmp(), and + * msg_clearjmp(). + */ +MSG_JMP_BUF *msg_jmp_bufp; + /* * Private state. */ @@ -262,6 +305,11 @@ NORETURN vmsg_fatal(const char *fmt, va_list ap) if (msg_cleanup_fn) msg_cleanup_fn(); } + if (msg_jmp_bufp) { + /* This code is for testing only. */ + msg_exiting = 0; + msg_longjmp(MSG_LONGJMP_FATAL); + } sleep(1); /* In case we're running as a signal handler. */ _exit(1); @@ -285,6 +333,11 @@ NORETURN vmsg_fatal_status(int status, const char *fmt, va_list ap) if (msg_cleanup_fn) msg_cleanup_fn(); } + if (msg_jmp_bufp) { + /* This code is for testing only. */ + msg_exiting = 0; + msg_longjmp(MSG_LONGJMP_FATAL); + } sleep(1); /* In case we're running as a signal handler. */ _exit(status); @@ -306,6 +359,11 @@ NORETURN vmsg_panic(const char *fmt, va_list ap) if (msg_exiting++ == 0) { msg_vprintf(MSG_PANIC, fmt, ap); } + if (msg_jmp_bufp) { + /* This code is for testing only. */ + msg_exiting = 0; + msg_longjmp(MSG_LONGJMP_PANIC); + } sleep(1); abort(); /* Die! */ /* In case we're running as a signal handler. */ diff --git a/postfix/src/util/msg.h b/postfix/src/util/msg.h index 6c75bafc1..188870643 100644 --- a/postfix/src/util/msg.h +++ b/postfix/src/util/msg.h @@ -16,6 +16,7 @@ */ #include #include +#include /* * External interface. @@ -46,6 +47,26 @@ extern void PRINTFLIKE(4, 5) msg_rate_delay(time_t *, int, void PRINTFPTRLIKE(1, 2) (*log_fn) (const char *,...), const char *,...); + /* + * Only for tests: make a long jump instead of terminating. + */ +#ifdef NO_SIGSETJMP +#define MSG_JMP_BUF jmp_buf +#define msg_setjmp(bufp) setjmp((msg_jmp_bufp = (bufp))[0]) +#define msg_longjmp(val) longjmp(msg_jmp_bufp[0], (val)) +#else +#define MSG_JMP_BUF sigjmp_buf +#define msg_setjmp(bufp) sigsetjmp((msg_jmp_bufp = (bufp))[0], 1) +#define msg_longjmp(val) siglongjmp(msg_jmp_bufp[0], (val)) +#endif +#define msg_resetjmp(bufp) do { msg_jmp_bufp = (bufp); } while (0) +#define msg_clearjmp() do { msg_jmp_bufp = 0; } while (0) + +extern MSG_JMP_BUF *msg_jmp_bufp; + +#define MSG_LONGJMP_FATAL 2 /* msg_fatal longjmp code */ +#define MSG_LONGJMP_PANIC 3 /* msg_panic longjmp code */ + /* LICENSE /* .ad /* .fi @@ -55,6 +76,11 @@ extern void PRINTFLIKE(4, 5) msg_rate_delay(time_t *, int, /* IBM T.J. Watson Research /* P.O. Box 704 /* Yorktown Heights, NY 10598, USA +/* +/* Wietse Venema +/* Google, Inc. +/* 111 8th Avenue +/* New York, NY 10011, USA /*--*/ #endif diff --git a/postfix/src/util/msg_output.c b/postfix/src/util/msg_output.c index 6663877d7..2ba897e8d 100644 --- a/postfix/src/util/msg_output.c +++ b/postfix/src/util/msg_output.c @@ -6,10 +6,15 @@ /* SYNOPSIS /* #include /* -/* typedef void (*MSG_OUTPUT_FN)(int level, char *text) +/* typedef void (*MSG_OUTPUT3_FN)(int level, char *text, void *context) /* -/* void msg_output(output_fn) -/* MSG_OUTPUT_FN output_fn; +/* void msg_output_push(output_fn, context) +/* MSG_OUTPUT3_FN output_fn; +/* void *context; +/* +/* msg_output_pop(output_fn, context) +/* MSG_OUTPUT3_FN output_fn +/* void *context; /* /* void msg_printf(level, format, ...) /* int level; @@ -19,39 +24,50 @@ /* int level; /* const char *format; /* va_list ap; +/* LEGACY API +/* typedef void (*MSG_OUTPUT_FN)(int level, char *text) +/* +/* void msg_output(output_fn) +/* MSG_OUTPUT_FN output_fn; /* DESCRIPTION /* This module implements low-level output management for the /* msg(3) diagnostics interface. /* -/* msg_output() registers an output handler for the diagnostics -/* interface. An application can register multiple output handlers. -/* Output handlers are called in the specified order. -/* An output handler takes as arguments a severity level (MSG_INFO, -/* MSG_WARN, MSG_ERROR, MSG_FATAL, MSG_PANIC, monotonically increasing +/* msg_output_push() registers an output handler and call-back +/* context, if that handler and context are not already +/* registered. Output handlers are called in the specified +/* order. The context pointer is passed as the third argument +/* to an MSG_OUTPUT3_FN output handler. An output handler +/* takes as arguments a severity level (MSG_INFO, MSG_WARN, +/* MSG_ERROR, MSG_FATAL, MSG_PANIC, monotonically increasing /* integer values ranging from 0 to MSG_LAST) and pre-formatted, /* sanitized, text in the form of a null-terminated string. /* -/* msg_printf() and msg_vprintf() format their arguments, sanitize the -/* result, and call the output handlers registered with msg_output(). +/* msg_output_pop() unregisters the specified output handler +/* and context, and all later registered (handler, context) +/* pairs. It invokes a panic when the handler and context are +/* not found. +/* +/* msg_printf() and msg_vprintf() format their arguments, +/* sanitize the result, and call the output handlers registered +/* with msg_output(). /* -/* msg_text() copies a pre-formatted text, sanitizes the result, and -/* calls the output handlers registered with msg_output(). +/* msg_text() copies a pre-formatted text, sanitizes the result, +/* and calls the output handlers registered with msg_output(). /* REENTRANCY /* .ad /* .fi -/* The above output routines are protected against ordinary -/* recursive calls and against re-entry by signal -/* handlers, with the following limitations: +/* The output routines are protected against ordinary recursive +/* calls and against re-entry by signal handlers, with the +/* following limitations: /* .IP \(bu /* The signal handlers must never return. In other words, the /* signal handlers must do one or more of the following: call -/* _exit(), kill the process with a signal, and permanently +/* _exit(), kill the process with a signal, or permanently /* block the process. /* .IP \(bu /* The signal handlers must call the above output routines not -/* until after msg_output() completes initialization, and not -/* until after the first formatted output to a VSTRING or -/* VSTREAM. +/* until after msg_output() completes initialization. /* .IP \(bu /* Each msg_output() call-back function, and each Postfix or /* system function called by that call-back function, either @@ -95,6 +111,7 @@ #include #include #include +#include #include /* @@ -106,36 +123,81 @@ volatile int msg_vprintf_level; * Private state. Allow one nested call, so that one logging error can be * reported to stderr before bailing out. */ +typedef struct MSG_OUTPUT_INFO { + int argc; /* 2 or 3 arguments */ + union { + MSG_OUTPUT_FN output_fn; /* two-argument form */ + MSG_OUTPUT3_FN output3_fn; /* three-argument form */ + } u; + void *context; /* used for three-argument form */ +} MSG_OUTPUT_INFO; + #define MSG_OUT_NESTING_LIMIT 2 -static MSG_OUTPUT_FN *msg_output_fn = 0; -static int msg_output_fn_count = 0; +static MSG_OUTPUT_INFO *msg_output_info = 0; +static int msg_output_info_count = 0; static VSTRING *msg_buffers[MSG_OUT_NESTING_LIMIT]; -/* msg_output - specify output handler */ +/* do_msg_output - add output handler */ -void msg_output(MSG_OUTPUT_FN output_fn) +static void do_msg_output(MSG_OUTPUT_INFO *info) { - int i; + VSTRING **bp; + MSG_OUTPUT_INFO *mp; /* * Allocate all resources during initialization. This may result in a * recursive call due to memory allocation error. */ if (msg_buffers[MSG_OUT_NESTING_LIMIT - 1] == 0) { - for (i = 0; i < MSG_OUT_NESTING_LIMIT; i++) - msg_buffers[i] = vstring_alloc(100); + for (bp = msg_buffers; bp < msg_buffers + MSG_OUT_NESTING_LIMIT; bp++) + *bp = vstring_alloc(100); + } + + /* + * Deduplicate requests. + */ + for (mp = msg_output_info; mp < msg_output_info + msg_output_info_count; mp++) { + if (mp->argc == info->argc + && mp->context == info->context + && ((info->argc == 2 && mp->u.output_fn == info->u.output_fn) + || (info->argc == 3 && mp->u.output3_fn == info->u.output3_fn))) + return; } /* * We're not doing this often, so avoid complexity and allocate memory * for an exact fit. */ - if (msg_output_fn_count == 0) - msg_output_fn = (MSG_OUTPUT_FN *) mymalloc(sizeof(*msg_output_fn)); + if (msg_output_info_count == 0) + msg_output_info = (MSG_OUTPUT_INFO *) mymalloc(sizeof(*msg_output_info)); else - msg_output_fn = (MSG_OUTPUT_FN *) myrealloc((void *) msg_output_fn, - (msg_output_fn_count + 1) * sizeof(*msg_output_fn)); - msg_output_fn[msg_output_fn_count++] = output_fn; + msg_output_info = (MSG_OUTPUT_INFO *) myrealloc((void *) msg_output_info, + (msg_output_info_count + 1) * sizeof(*msg_output_info)); + msg_output_info[msg_output_info_count++] = *info; +} + +/* msg_output - specify output handler */ + +void msg_output(MSG_OUTPUT_FN output_fn) +{ + MSG_OUTPUT_INFO info; + + info.argc = 2; + info.u.output_fn = output_fn; + info.context = 0; + do_msg_output(&info); +} + +/* msg_output_push - specify three-argument output handler */ + +void msg_output_push(MSG_OUTPUT3_FN output_fn, void *context) +{ + MSG_OUTPUT_INFO info; + + info.argc = 3; + info.u.output3_fn = output_fn; + info.context = context; + do_msg_output(&info); } /* msg_printf - format text and log it */ @@ -154,21 +216,46 @@ void msg_printf(int level, const char *format,...) void msg_vprintf(int level, const char *format, va_list ap) { int saved_errno = errno; + MSG_OUTPUT_INFO *mp; VSTRING *vp; - int i; if (msg_vprintf_level < MSG_OUT_NESTING_LIMIT) { msg_vprintf_level += 1; /* On-the-fly initialization for test programs and startup errors. */ - if (msg_output_fn_count == 0) + if (msg_output_info_count == 0) msg_vstream_init("unknown", VSTREAM_ERR); vp = msg_buffers[msg_vprintf_level - 1]; /* OK if terminating signal handler hijacks control before next stmt. */ vstring_vsprintf(vp, format, ap); printable(vstring_str(vp), '?'); - for (i = 0; i < msg_output_fn_count; i++) - msg_output_fn[i] (level, vstring_str(vp)); + for (mp = msg_output_info; mp < msg_output_info + msg_output_info_count; mp++) { + switch (mp->argc) { + case 3: + mp->u.output3_fn(level, vstring_str(vp), mp->context); + break; + case 2: + mp->u.output_fn(level, vstring_str(vp)); + break; + default: + msg_panic("msg_vprintf: bad argument count: %d", mp->argc); + } + } msg_vprintf_level -= 1; } errno = saved_errno; } + +/* msg_output_pop - pop output handler and context */ + +void msg_output_pop(MSG_OUTPUT3_FN output_fn, void *context) +{ + MSG_OUTPUT_INFO *mp; + + for (;;) { + if (msg_output_info_count <= 0) + msg_panic("msg_output_pop: handler and context not found"); + mp = msg_output_info + --msg_output_info_count; + if (context == mp->context && mp->u.output3_fn == output_fn) + return; + } +} diff --git a/postfix/src/util/msg_output.h b/postfix/src/util/msg_output.h index bd84276f6..5beb2c75b 100644 --- a/postfix/src/util/msg_output.h +++ b/postfix/src/util/msg_output.h @@ -21,6 +21,9 @@ */ typedef void (*MSG_OUTPUT_FN) (int, const char *); extern void msg_output(MSG_OUTPUT_FN); +typedef void (*MSG_OUTPUT3_FN) (int, const char *, void *); +extern void msg_output_push(MSG_OUTPUT3_FN, void *); +extern void msg_output_pop(MSG_OUTPUT3_FN, void *); extern void PRINTFLIKE(2, 3) msg_printf(int, const char *,...); extern void msg_vprintf(int, const char *, va_list); diff --git a/postfix/src/util/msg_output_test.c b/postfix/src/util/msg_output_test.c new file mode 100644 index 000000000..48c6402d8 --- /dev/null +++ b/postfix/src/util/msg_output_test.c @@ -0,0 +1,142 @@ + /* + * Test program to exercise the msg_output module. See comments in + * ptest_main.h for documented examples. + */ + + /* + * System library. + */ +#include +#include + + /* + * Utility library. + */ +#include +#include +#include +#include +#include + + /* + * Test library. + */ +#include +#include + + /* + * Note: the test framework calls msg_output_pop() after a test returns or + * makes a long jump, so there is no requirement that each test below pops + * the handlers that it has pushed. + */ +static ARGV *got_argv; +static VSTRING *buf; + +typedef struct PTEST_CASE { + const char *testname; /* Human-readable description */ + void (*action) (PTEST_CTX *t, const struct PTEST_CASE *); +} PTEST_CASE; + +/* update_argv - append formatted handler inputs */ + +static void update_argv(ARGV *argv, int level, const char *text, void *context) +{ + if (buf == 0) + buf = vstring_alloc(10); + vstring_sprintf(buf, "%d:%s:%s", level, text, (char *) context); + argv_add(argv, vstring_str(buf), (char *) 0); +} + +/* handler - output handler */ + +static void handler(int level, const char *text, void *context) +{ + update_argv(got_argv, level, text, context); +} + +static void test_msg_output_push_pop_works(PTEST_CTX *t, const PTEST_CASE *unused) +{ + ARGV *want_argv = argv_alloc(1); + char *req_context = "handler"; + + /* + * Install our logging output handler. + */ + got_argv = argv_alloc(1); + msg_output_push(handler, req_context); + + /* + * Expect and generate one logging event. + */ + update_argv(want_argv, 0, "text", req_context); + expect_ptest_log_event(t, "text"); + msg_info("text"); + + /* + * Verify that the event was sent to our logging handler. + */ + if (got_argv->argc != 1) + ptest_error(t, "handler: got %ld results, want 1", + (long) got_argv->argc); + else + (void) eq_argv(t, "handler events", got_argv, want_argv); + + /* + * Pop our logging output handler and verify that it no longer receives + * logging + */ + msg_output_pop(handler, req_context); + expect_ptest_log_event(t, "more text"); + msg_info("more text"); + if (got_argv->argc > 1) + ptest_error(t, "handler: got result after it was popped"); + + /* + * Clean up. + */ + argv_free(got_argv); + argv_free(want_argv); +} + +static void test_msg_output_push_dedups(PTEST_CTX *t, const PTEST_CASE *unused) +{ + ARGV *want_argv = argv_alloc(1); + char *req_context = "handler"; + + /* + * Push the same logging handler twice. + */ + got_argv = argv_alloc(1); + msg_output_push(handler, req_context); + msg_output_push(handler, req_context); + + /* + * Expect and generate a logging event. + */ + update_argv(want_argv, 0, "text", req_context); + expect_ptest_log_event(t, "text"); + msg_info("text"); + + /* + * Verify the handler is called only once. + */ + if (got_argv->argc != 1) + ptest_error(t, "handler: got %ld results, want 1", + (long) got_argv->argc); + else + (void) eq_argv(t, "handler events", got_argv, want_argv); + + /* + * Clean up. + */ + msg_output_pop(handler, req_context); + argv_free(got_argv); + argv_free(want_argv); +} + +static PTEST_CASE ptestcases[] = { + {"test msg_output_push_pop works", test_msg_output_push_pop_works}, + {"test msg_output_push dedups", test_msg_output_push_dedups}, +}; + +#include diff --git a/postfix/src/util/msg_vstream.c b/postfix/src/util/msg_vstream.c index b6e24e609..759e1953b 100644 --- a/postfix/src/util/msg_vstream.c +++ b/postfix/src/util/msg_vstream.c @@ -9,6 +9,9 @@ /* void msg_vstream_init(progname, stream) /* const char *progname; /* VSTREAM *stream; +/* +/* void msg_vstream_enable(yesno) +/* int yesno; /* DESCRIPTION /* This module implements support to report msg(3) diagnostics /* to a VSTREAM. @@ -16,6 +19,9 @@ /* msg_vstream_init() sets the program name that appears in each output /* record, and directs diagnostics (see msg(3)) to the specified /* VSTREAM. The \fIprogname\fR argument is not copied. +/* +/* msg_vstream_enable() enables or disables msg_vstream logging, +/* depending on the argument value. /* SEE ALSO /* msg(3) /* BUGS @@ -51,6 +57,7 @@ */ static const char *msg_tag; static VSTREAM *msg_stream; +static int msg_vstream_enabled; /* msg_vstream_print - log diagnostic to VSTREAM */ @@ -60,6 +67,9 @@ static void msg_vstream_print(int level, const char *text) "info", "warning", "error", "fatal", "panic", }; + if (!msg_vstream_enabled) + return; + if (level < 0 || level >= (int) (sizeof(level_text) / sizeof(level_text[0]))) msg_panic("invalid severity level: %d", level); if (level == MSG_INFO) { @@ -76,12 +86,15 @@ static void msg_vstream_print(int level, const char *text) void msg_vstream_init(const char *name, VSTREAM *vp) { - static int first_call = 1; - msg_tag = name; msg_stream = vp; - if (first_call) { - first_call = 0; - msg_output(msg_vstream_print); - } + msg_output(msg_vstream_print); + msg_vstream_enabled = 1; +} + +/* msg_vstream_enable - on/off switch */ + +void msg_vstream_enable(int yesno) +{ + msg_vstream_enabled = yesno; } diff --git a/postfix/src/util/msg_vstream.h b/postfix/src/util/msg_vstream.h index d0679a047..02ec67ccb 100644 --- a/postfix/src/util/msg_vstream.h +++ b/postfix/src/util/msg_vstream.h @@ -19,6 +19,7 @@ * External interface. */ extern void msg_vstream_init(const char *, VSTREAM *); +extern void msg_vstream_enable(int); /* LICENSE /* .ad diff --git a/postfix/src/util/myaddrinfo.c b/postfix/src/util/myaddrinfo.c index 5edafde8d..7bf213325 100644 --- a/postfix/src/util/myaddrinfo.c +++ b/postfix/src/util/myaddrinfo.c @@ -193,7 +193,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/postfix/src/util/myaddrinfo.h b/postfix/src/util/myaddrinfo.h index 94f1e9fa6..702cb1a74 100644 --- a/postfix/src/util/myaddrinfo.h +++ b/postfix/src/util/myaddrinfo.h @@ -17,7 +17,7 @@ #include #include #include -#include +#include #include #include /* MAI_STRERROR() */ #include /* CHAR_BIT */ @@ -42,7 +42,7 @@ #define sockaddr_storage mai_sockaddr_storage /* - * Modern systems define this in . + * Modern systems define this in . */ struct addrinfo { int ai_flags; /* AI_PASSIVE|CANONNAME|NUMERICHOST */ diff --git a/postfix/src/util/myaddrinfo.ref b/postfix/src/util/myaddrinfo.ref deleted file mode 100644 index 7dccdb0bf..000000000 --- a/postfix/src/util/myaddrinfo.ref +++ /dev/null @@ -1,8 +0,0 @@ -./myaddrinfo: === hostname belly.porcupine.org === -./myaddrinfo: belly.porcupine.org -> family=2 sock=1 proto=6 168.100.3.6 -./myaddrinfo: 168.100.3.6 -> belly.porcupine.org -./myaddrinfo: belly.porcupine.org -> family=28 sock=1 proto=6 2604:8d00:189::6 -./myaddrinfo: 2604:8d00:189::6 -> belly.porcupine.org -./myaddrinfo: === host address 168.100.3.2 === -./myaddrinfo: 168.100.3.2 -> family=2 sock=1 proto=6 168.100.3.2 -./myaddrinfo: 168.100.3.2 -> spike.porcupine.org diff --git a/postfix/src/util/myaddrinfo.ref2 b/postfix/src/util/myaddrinfo.ref2 deleted file mode 100644 index f2305dceb..000000000 --- a/postfix/src/util/myaddrinfo.ref2 +++ /dev/null @@ -1,5 +0,0 @@ -./myaddrinfo: === hostname null.porcupine.org === -./myaddrinfo: hostname_to_sockaddr(null.porcupine.org): hostname nor servname provided, or not known -./myaddrinfo: === host address 10.0.0.0 === -./myaddrinfo: 10.0.0.0 -> family=2 sock=1 proto=6 10.0.0.0 -./myaddrinfo: sockaddr_to_hostname: hostname nor servname provided, or not known diff --git a/postfix/src/util/myaddrinfo4.ref b/postfix/src/util/myaddrinfo4.ref deleted file mode 100644 index 33c128422..000000000 --- a/postfix/src/util/myaddrinfo4.ref +++ /dev/null @@ -1,6 +0,0 @@ -./myaddrinfo4: === hostname belly.porcupine.org === -./myaddrinfo4: belly.porcupine.org -> family=2 sock=1 proto=6 168.100.3.6 -./myaddrinfo4: 168.100.3.6 -> belly.porcupine.org -./myaddrinfo4: === host address 168.100.3.2 === -./myaddrinfo4: 168.100.3.2 -> family=2 sock=1 proto=6 168.100.3.2 -./myaddrinfo4: 168.100.3.2 -> spike.porcupine.org diff --git a/postfix/src/util/myaddrinfo4.ref2 b/postfix/src/util/myaddrinfo4.ref2 deleted file mode 100644 index f73560bf3..000000000 --- a/postfix/src/util/myaddrinfo4.ref2 +++ /dev/null @@ -1,5 +0,0 @@ -./myaddrinfo4: === hostname null.porcupine.org === -./myaddrinfo4: hostname2sockaddr(null.porcupine.org): No address associated with hostname -./myaddrinfo4: === host address 10.0.0.0 === -./myaddrinfo4: 10.0.0.0 -> family=2 sock=1 proto=6 10.0.0.0 -./myaddrinfo4: sockaddr2hostname: hostname nor servname provided, or not known diff --git a/postfix/src/util/myaddrinfo_test.c b/postfix/src/util/myaddrinfo_test.c new file mode 100644 index 000000000..4a8cf0f7a --- /dev/null +++ b/postfix/src/util/myaddrinfo_test.c @@ -0,0 +1,215 @@ + /* + * Test program for the myaddrinfo module. The purpose is to verify that the + * myaddrinfo functions make the expected calls, and that they forward the + * expected results. See comments in ptest_main.h and pmock_expect_test.c + * for a documented example. + */ + + /* + * System library. + */ +#include +#include + + /* + * Utility library. + */ +#include +#include + + /* + * Test library. + */ +#include +#include + +#define STR vstring_str + +typedef struct PTEST_CASE { + const char *testname; /* Human-readable description */ + void (*action) (PTEST_CTX *t, const struct PTEST_CASE *); +} PTEST_CASE; + +static void test_hostname_to_sockaddr_host(PTEST_CTX *t, const PTEST_CASE *tp) +{ + int got_st, want_st = 0; + struct addrinfo *got_info = 0, *want_info; + struct addrinfo req_hints; + const char *hostname = "belly.porcupine.org"; + + inet_proto_init(tp->testname, "all"); + + /* + * Set up expectations. + */ + memset(&req_hints, 0, sizeof(req_hints)); + req_hints.ai_family = PF_INET; + want_info = make_addrinfo(&req_hints, (char *) 0, "168.100.3.6", 0); + req_hints.ai_family = PF_INET6; + want_info->ai_next = make_addrinfo(&req_hints, (char *) 0, + "2604:8d00:189::6", 0); + req_hints.ai_family = inet_proto_info()->ai_family; + req_hints.ai_socktype = SOCK_STREAM; /* XXX */ + expect_getaddrinfo(1, want_st, hostname, (char *) 0, + &req_hints, want_info); + + /* + * Call the mock and verify the results. + */ + got_st = hostname_to_sockaddr(hostname, (char *) 0, 0, &got_info); + if (got_st != want_st) { + ptest_error(t, "hostname_to_sockaddr status: got %d, want %d", + got_st, want_st); + } else if (!eq_addrinfo(t, "hostname_to_sockaddr addrinfo", + got_info, want_info)) { + /* already logged by eq_addrinfo() */ + } + + /* + * Clean up. + */ + freeaddrinfo(want_info); + if (got_info) + freeaddrinfo(got_info); +} + +static void test_hostname_to_sockaddr_v4host(PTEST_CTX *t, const PTEST_CASE *tp) +{ + int got_st, want_st = 0; + struct addrinfo *got_info = 0, *want_info; + struct addrinfo req_hints; + const char *hostname = "belly.porcupine.org"; + + inet_proto_init(tp->testname, "ipv4"); + + /* + * Set up expectations. + */ + memset(&req_hints, 0, sizeof(req_hints)); + req_hints.ai_family = PF_INET; + want_info = make_addrinfo(&req_hints, (char *) 0, "168.100.3.6", 0); + req_hints.ai_family = inet_proto_info()->ai_family; + req_hints.ai_socktype = SOCK_STREAM; /* XXX */ + expect_getaddrinfo(1, want_st, hostname, (char *) 0, + &req_hints, want_info); + + /* + * Call the mock and verify the results. + */ + got_st = hostname_to_sockaddr(hostname, (char *) 0, 0, &got_info); + if (got_st != want_st) { + ptest_error(t, "hostname_to_sockaddr status: got %d, want %d", + got_st, want_st); + } else if (!eq_addrinfo(t, "hostname_to_sockaddr addrinfo", + got_info, want_info)) { + /* already logged by eq_addrinfo() */ + } + + /* + * Clean up. + */ + freeaddrinfo(want_info); + if (got_info) + freeaddrinfo(got_info); +} + + +static void test_hostaddr_to_sockaddr_host(PTEST_CTX *t, const PTEST_CASE *tp) +{ + int got_st, want_st = 0; + struct addrinfo *got_info, *want_info; + struct addrinfo req_hints; + const char *req_hostaddr = "168.100.3.2"; + + /* + * Set up expectations. + */ + memset(&req_hints, 0, sizeof(req_hints)); + req_hints.ai_family = PF_INET; + want_info = make_addrinfo(&req_hints, (char *) 0, req_hostaddr, 0); + req_hints.ai_family = inet_proto_info()->ai_family; + req_hints.ai_socktype = SOCK_STREAM; /* XXX */ + req_hints.ai_flags = AI_NUMERICHOST; + expect_getaddrinfo(1, want_st, req_hostaddr, (char *) 0, + &req_hints, want_info); + + /* + * Call the mock indirectly, and verify the results. + */ + got_st = hostaddr_to_sockaddr(req_hostaddr, (char *) 0, 0, &got_info); + if (got_st != want_st) { + ptest_error(t, "hostaddr_to_sockaddr status: got %d, want %d", + got_st, want_st); + } else if (!eq_addrinfo(t, "hostname_to_sockaddr addrinfo", + got_info, want_info)) { + /* already logged by eq_addrinfo() */ + } + + /* + * Clean up. + */ + freeaddrinfo(want_info); + if (got_info) + freeaddrinfo(got_info); +} + +static void test_hostname_to_sockaddr_nxhost(PTEST_CTX *t, const PTEST_CASE *tp) +{ + int got_st, want_st = EAI_NONAME; + struct addrinfo *got_info = 0, *want_info = 0; + struct addrinfo req_hints; + const char *hostname = "null.porcupine.org"; + + inet_proto_init(tp->testname, "all"); + + /* + * Set up expectations. + */ + memset(&req_hints, 0, sizeof(req_hints)); + req_hints.ai_family = inet_proto_info()->ai_family; + req_hints.ai_socktype = SOCK_STREAM; /* XXX */ + expect_getaddrinfo(1, want_st, hostname, (char *) 0, &req_hints, want_info); + + /* + * Call the mock and verify the results. + */ + got_st = hostname_to_sockaddr(hostname, (char *) 0, 0, &got_info); + if (got_st != want_st) { + ptest_error(t, "hostname_to_sockaddr status: got %d, want %d", + got_st, want_st); + } else if (!eq_addrinfo(t, "hostname_to_sockaddr addrinfo", + got_info, want_info)) { + /* already logged by eq_addrinfo() */ + } + + /* + * Clean up. + */ + if (got_info) + freeaddrinfo(got_info); +} + + /* + * Test cases. + */ +const PTEST_CASE ptestcases[] = { + { + /* name */ "test hostname_to_sockaddr host only", + /* action */ test_hostname_to_sockaddr_host, + }, + { + /* name */ "test hostname_to_sockaddr v4host only", + /* action */ test_hostname_to_sockaddr_v4host, + }, + { + /* name */ "test hostaddr_to_sockaddr host only", + /* action */ test_hostaddr_to_sockaddr_host, + }, + { + /* name */ "test hostname_to_sockaddr non-existent host only", + /* action */ test_hostname_to_sockaddr_nxhost, + }, + /* TODO: sockadddr_to_hostaddr, sockaddr_to_hostname. */ +}; + +#include diff --git a/postfix/src/util/mymalloc.c b/postfix/src/util/mymalloc.c index 94f7bb3e7..37a8afd2a 100644 --- a/postfix/src/util/mymalloc.c +++ b/postfix/src/util/mymalloc.c @@ -249,7 +249,7 @@ char *mystrndup(const char *str, ssize_t len) if (len < 0) msg_panic("mystrndup: requested length %ld", (long) len); #ifndef NO_SHARED_EMPTY_STRINGS - if (*str == 0) + if (*str == 0 || /* fix 20220615 */ len == 0) return ((char *) empty_string); #endif if ((cp = memchr(str, 0, len)) != 0) diff --git a/postfix/src/util/mymalloc_test.c b/postfix/src/util/mymalloc_test.c new file mode 100644 index 000000000..fc3c8c036 --- /dev/null +++ b/postfix/src/util/mymalloc_test.c @@ -0,0 +1,308 @@ + /* + * Tests to verify malloc sanity checks. See comments in ptest_main.h for a + * documented example. The test code depends on the real mymalloc library, + * so we can 't do destructive tests. + */ + + /* + * System library. + */ +#include +#include + + /* + * Utility library. + */ +#include +#include + + /* + * Test library. + */ +#include + + /* + * See + */ +typedef struct PTEST_CASE { + const char *testname; /* Human-readable description */ + void (*action) (PTEST_CTX *, const struct PTEST_CASE *); +} PTEST_CASE; + +/* Test functions. */ + +static void test_mymalloc_normal(PTEST_CTX *t, const PTEST_CASE *tp) +{ + void *ptr; + + ptr = mymalloc(100); + myfree(ptr); +} + +static void test_mymalloc_panic_too_small(PTEST_CTX *t, const PTEST_CASE *tp) +{ + expect_ptest_log_event(t, "panic: mymalloc: requested length 0"); + (void) mymalloc(0); + ptest_fatal(t, "mymalloc(0) returned"); +} + +static void test_mymalloc_fatal_out_of_mem(PTEST_CTX *t, const PTEST_CASE *tp) +{ + if (sizeof(size_t) <= 4) + ptest_skip(t); + expect_ptest_log_event(t, "fatal: mymalloc: insufficient memory for"); + (void) mymalloc(SSIZE_T_MAX - 100); + ptest_fatal(t, "mymalloc(SSIZE_T_MAX-100) returned"); +} + +static void test_myfree_panic_double_free(PTEST_CTX *t, const PTEST_CASE *tp) +{ + void *ptr; + + expect_ptest_log_event(t, "panic: myfree: corrupt or unallocated memory block"); + ptr = mymalloc(100); + myfree(ptr); + /* The next call unavoidably reads unallocated memory */ + myfree(ptr); + ptest_fatal(t, "double myfree(_) returned"); +} + +static void test_myfree_panic_null(PTEST_CTX *t, const PTEST_CASE *tp) +{ + expect_ptest_log_event(t, "panic: myfree: null pointer input"); + myfree((void *) 0); + ptest_fatal(t, "myfree(0) returned"); +} + +static void test_myrealloc_normal(PTEST_CTX *t, const PTEST_CASE *tp) +{ + void *ptr; + + ptr = mymalloc(100); + ptr = myrealloc(ptr, 200); + myfree(ptr); +} + +static void test_myrealloc_panic_too_small(PTEST_CTX *t, const PTEST_CASE *tp) +{ + void *ptr; + + expect_ptest_log_event(t, "panic: myrealloc: requested length 0"); + ptr = mymalloc(100); + ptest_defer(t, myfree, ptr); + (void) myrealloc(ptr, 0); + ptest_fatal(t, "myrealloc(_, 0) returned"); +} + +static void test_myrealloc_fatal_out_of_mem(PTEST_CTX *t, const PTEST_CASE *tp) +{ + void *ptr; + + if (sizeof(size_t) <= 4) + ptest_skip(t); + + /* + * Unlike the previous test, this test can't use test_defer(t, myfree, + * ptr), because myrealloc() clears the memory block's signature field + * before it calls realloc(). + */ + expect_ptest_log_event(t, "fatal: myrealloc: insufficient memory for"); + ptr = mymalloc(100); + (void) myrealloc(ptr, SSIZE_T_MAX - 100); + ptest_fatal(t, "myrealloc(_, SSIZE_T_MAX-100) returned"); +} + +static void test_myrealloc_panic_unallocated(PTEST_CTX *t, const PTEST_CASE *tp) +{ + void *ptr; + + expect_ptest_log_event(t, "panic: myrealloc: corrupt or unallocated memory block"); + ptr = mymalloc(100); + myfree(ptr); + ptr = myrealloc(ptr, 200); + ptest_fatal(t, "myrealloc() after free() returned"); +} + +static void test_myrealloc_panic_null(PTEST_CTX *t, const PTEST_CASE *tp) +{ + expect_ptest_log_event(t, "panic: myrealloc: null pointer input"); + (void) myrealloc((void *) 0, 200); + ptest_fatal(t, "myrealloc(0, _) returned"); +} + +static void test_mystrdup_normal(PTEST_CTX *t, const PTEST_CASE *tp) +{ + void *ptr; + + ptr = mystrdup("foo"); + myfree(ptr); +} + +static void test_mystrdup_panic_null(PTEST_CTX *t, const PTEST_CASE *tp) +{ + expect_ptest_log_event(t, "panic: mystrdup: null pointer argument"); + (void) mystrdup((char *) 0); + ptest_fatal(t, "mystrdup(0) returned"); +} + +static void test_mystrdup_static_empty(PTEST_CTX *t, const PTEST_CASE *tp) +{ + char *want; + char *got; + + /* + * Repeated mystrdup("") produce the same result. + */ + want = mystrdup(""); + got = mystrdup(""); + if (got != want) + ptest_error(t, "mystrndup: empty string results differ: got %p, want %p", + got, want); + + /* + * myfree() is a NOOP. + */ + myfree(want); + myfree(got); +} + +static void test_mystrndup_normal_short(PTEST_CTX *t, const PTEST_CASE *tp) +{ + char *want = "foo"; + char *got; + + got = mystrndup("foo", 5); + if (strcmp(got, want) != 0) + ptest_error(t, "mystrndup: got '%s', want '%s'", got, want); + myfree(got); +} + +static void test_mystrndup_normal_long(PTEST_CTX *t, + const PTEST_CASE *tp) +{ + char *want = "fooba"; + char *got; + + got = mystrndup("foobar", 5); + if (strcmp(got, want) != 0) + ptest_error(t, "mystrndup: got '%s', want '%s'", got, want); + myfree(got); +} + +static void test_mystrndup_panic_null(PTEST_CTX *t, + const PTEST_CASE *tp) +{ + expect_ptest_log_event(t, "panic: mystrndup: null pointer argument"); + (void) mystrndup((char *) 0, 5); + ptest_fatal(t, "mystrndup(0, _) returned"); +} + +static void test_mystrndup_panic_too_small(PTEST_CTX *t, + const PTEST_CASE *tp) +{ + expect_ptest_log_event(t, "panic: mystrndup: requested length -1"); + (void) mystrndup("foo", -1); + ptest_fatal(t, "mystrndup(0, -1) returned"); +} + +static void test_mystrndup_static_empty(PTEST_CTX *t, + const PTEST_CASE *tp) +{ + char *want; + char *got; + + /* + * Different ways to save an empty string produce the same result. + */ + want = mystrndup("", 10); + got = mystrndup("foo", 0); + if (got != want) + ptest_error(t, "mystrndup: empty string results differ: got %p, want %p", + got, want); + + /* + * myfree() is a NOOP. + */ + myfree(want); + myfree(got); +} + +static void test_mymemdup_normal(PTEST_CTX *t, const PTEST_CASE *tp) +{ + void *ptr; + + ptr = mymemdup("abcdef", 5); + myfree(ptr); +} + +static void test_mymemdup_panic_null(PTEST_CTX *t, const PTEST_CASE *tp) +{ + expect_ptest_log_event(t, "panic: mymemdup: null pointer argument"); + (void) mymemdup((void *) 0, 100); + ptest_fatal(t, "mymemdup(0, _) returned"); +} + +static void test_mymemdup_panic_too_small(PTEST_CTX *t, const PTEST_CASE *tp) +{ + expect_ptest_log_event(t, "panic: mymalloc: requested length 0"); + (void) mymemdup("abcdef", 0); + ptest_fatal(t, "mymemdup(_, 0) returned"); +} + +static void test_mymemdup_fatal_out_of_mem(PTEST_CTX *t, const PTEST_CASE *tp) +{ + if (sizeof(size_t) <= 4) + ptest_skip(t); + expect_ptest_log_event(t, "fatal: mymalloc: insufficient memory for"); + (void) mymemdup("abcdef", SSIZE_T_MAX - 100); + ptest_fatal(t, "mymemdup(_, SSIZE_T_MAX-100) returned"); +} + +const PTEST_CASE ptestcases[] = { + {"mymalloc + myfree normal case", test_mymalloc_normal, + }, + {"mymalloc panic for too small request", test_mymalloc_panic_too_small, + }, + {"mymalloc fatal for out of memory", test_mymalloc_fatal_out_of_mem, + }, + {"myfree panic for double free", test_myfree_panic_double_free, + }, + {"myfree panic for null input", test_myfree_panic_null, + }, + {"myrealloc + myfree normal case", test_myrealloc_normal, + }, + {"myrealloc panic for too small request", test_myrealloc_panic_too_small, + }, + {"myrealloc fatal for out of memory", test_myrealloc_fatal_out_of_mem, + }, + {"myrealloc panic for unallocated input", test_myrealloc_panic_unallocated, + }, + {"myrealloc panic for null input", test_myrealloc_panic_null, + }, + {"mystrdup + myfree normal case", test_mystrdup_normal, + }, + {"mystrdup panic for null input", test_mystrdup_panic_null, + }, + {"mystrdup static result for empty string", test_mystrdup_static_empty, + }, + {"mystrndup + myfree normal short", test_mystrndup_normal_short, + }, + {"mystrndup + myfree normal long", test_mystrndup_normal_long, + }, + {"mystrndup panic for null input", test_mystrndup_panic_null, + }, + {"mystrndup panic for for too small size", test_mystrndup_panic_too_small, + }, + {"mystrndup static result for empty string", test_mystrndup_static_empty, + }, + {"mymemdup normal case", test_mymemdup_normal, + }, + {"mymemdup panic for null input", test_mymemdup_panic_null, + }, + {"mymemdup panic for too small request", test_mymemdup_panic_too_small, + }, + {"mymemdup fatal for out of memory", test_mymemdup_fatal_out_of_mem, + }, +}; + +#include diff --git a/postfix/src/util/mystrtok.c b/postfix/src/util/mystrtok.c index 85b15f3b3..95cdc9741 100644 --- a/postfix/src/util/mystrtok.c +++ b/postfix/src/util/mystrtok.c @@ -159,100 +159,3 @@ char *mystrtokdq(char **src, const char *sep) *src = cp; return (start); } - -#ifdef TEST - - /* - * Test program. - */ -#include "msg.h" -#include "mymalloc.h" - - /* - * The following needs to be large enough to include a null terminator in - * every testcase.expected field. - */ -#define EXPECT_SIZE 5 - -struct testcase { - const char *action; - const char *input; - const char *expected[EXPECT_SIZE]; -}; -static const struct testcase testcases[] = { - {"mystrtok", ""}, - {"mystrtok", " foo ", {"foo"}}, - {"mystrtok", " foo bar ", {"foo", "bar"}}, - {"mystrtokq", ""}, - {"mystrtokq", "foo bar", {"foo", "bar"}}, - {"mystrtokq", "{ bar } ", {"{ bar }"}}, - {"mystrtokq", "foo { bar } baz", {"foo", "{ bar }", "baz"}}, - {"mystrtokq", "foo{ bar } baz", {"foo{ bar }", "baz"}}, - {"mystrtokq", "foo { bar }baz", {"foo", "{ bar }baz"}}, - {"mystrtokdq", ""}, - {"mystrtokdq", " foo ", {"foo"}}, - {"mystrtokdq", " foo bar ", {"foo", "bar"}}, - {"mystrtokdq", " foo\\ bar ", {"foo\\ bar"}}, - {"mystrtokdq", " foo \\\" bar", {"foo", "\\\"", "bar"}}, - {"mystrtokdq", " foo \" bar baz\" ", {"foo", "\" bar baz\""}}, -}; - -int main(void) -{ - const struct testcase *tp; - char *actual; - int pass; - int fail; - int match; - int n; - -#define NUM_TESTS sizeof(testcases)/sizeof(testcases[0]) -#define STR_OR_NULL(s) ((s) ? (s) : "null") - - for (pass = fail = 0, tp = testcases; tp < testcases + NUM_TESTS; tp++) { - char *saved_input = mystrdup(tp->input); - char *cp = saved_input; - - msg_info("RUN test case %ld %s >%s<", - (long) (tp - testcases), tp->action, tp->input); -#if 0 - msg_info("action=%s", tp->action); - msg_info("input=%s", tp->input); - for (n = 0; tp->expected[n]; tp++) - msg_info("expected[%d]=%s", n, tp->expected[n]); -#endif - - for (n = 0; n < EXPECT_SIZE; n++) { - if (strcmp(tp->action, "mystrtok") == 0) { - actual = mystrtok(&cp, CHARS_SPACE); - } else if (strcmp(tp->action, "mystrtokq") == 0) { - actual = mystrtokq(&cp, CHARS_SPACE, CHARS_BRACE); - } else if (strcmp(tp->action, "mystrtokdq") == 0) { - actual = mystrtokdq(&cp, CHARS_SPACE); - } else { - msg_panic("invalid command: %s", tp->action); - } - if ((match = (actual && tp->expected[n]) ? - (strcmp(actual, tp->expected[n]) == 0) : - (actual == tp->expected[n])) != 0) { - if (actual == 0) { - msg_info("PASS test %ld", (long) (tp - testcases)); - pass++; - break; - } - } else { - msg_warn("expected: >%s<, got: >%s<", - STR_OR_NULL(tp->expected[n]), STR_OR_NULL(actual)); - msg_info("FAIL test %ld", (long) (tp - testcases)); - fail++; - break; - } - } - if (n >= EXPECT_SIZE) - msg_panic("need to increase EXPECT_SIZE"); - myfree(saved_input); - } - return (fail > 0); -} - -#endif diff --git a/postfix/src/util/mystrtok.ref b/postfix/src/util/mystrtok.ref deleted file mode 100644 index 4f920f994..000000000 --- a/postfix/src/util/mystrtok.ref +++ /dev/null @@ -1,30 +0,0 @@ -unknown: RUN test case 0 mystrtok >< -unknown: PASS test 0 -unknown: RUN test case 1 mystrtok > foo < -unknown: PASS test 1 -unknown: RUN test case 2 mystrtok > foo bar < -unknown: PASS test 2 -unknown: RUN test case 3 mystrtokq >< -unknown: PASS test 3 -unknown: RUN test case 4 mystrtokq >foo bar< -unknown: PASS test 4 -unknown: RUN test case 5 mystrtokq >{ bar } < -unknown: PASS test 5 -unknown: RUN test case 6 mystrtokq >foo { bar } baz< -unknown: PASS test 6 -unknown: RUN test case 7 mystrtokq >foo{ bar } baz< -unknown: PASS test 7 -unknown: RUN test case 8 mystrtokq >foo { bar }baz< -unknown: PASS test 8 -unknown: RUN test case 9 mystrtokdq >< -unknown: PASS test 9 -unknown: RUN test case 10 mystrtokdq > foo < -unknown: PASS test 10 -unknown: RUN test case 11 mystrtokdq > foo bar < -unknown: PASS test 11 -unknown: RUN test case 12 mystrtokdq > foo\ bar < -unknown: PASS test 12 -unknown: RUN test case 13 mystrtokdq > foo \" bar< -unknown: PASS test 13 -unknown: RUN test case 14 mystrtokdq > foo " bar baz" < -unknown: PASS test 14 diff --git a/postfix/src/util/mystrtok_test.c b/postfix/src/util/mystrtok_test.c new file mode 100644 index 000000000..9cc50a4a2 --- /dev/null +++ b/postfix/src/util/mystrtok_test.c @@ -0,0 +1,108 @@ + /* + * Test program to exercise mystrtok.c. See ptest_main.h for a documented + * example. + */ + + /* + * System library. + */ +#include +#include + + /* + * Utility library. + */ +#include +#include +#include + + /* + * Test library. + */ +#include + + /* + * The following needs to be large enough to include a null terminator in + * every ptestcase.want field. + */ +#define WANT_SIZE 5 + +typedef struct PTEST_CASE { + const char *testname; + void (*action) (PTEST_CTX *, const struct PTEST_CASE *); + const char *fname; + const char *input; + const char *want[WANT_SIZE]; +} PTEST_CASE; + +#define STR_OR_NULL(s) ((s) ? (s) : "null") + +static void tester(PTEST_CTX *t, const struct PTEST_CASE *tp) +{ + char *got; + int match; + int n; + char *saved_input = mystrdup(tp->input); + char *cp = saved_input; + + for (n = 0; n < WANT_SIZE; n++) { + if (strcmp(tp->fname, "mystrtok") == 0) { + got = mystrtok(&cp, CHARS_SPACE); + } else if (strcmp(tp->fname, "mystrtokq") == 0) { + got = mystrtokq(&cp, CHARS_SPACE, CHARS_BRACE); + } else if (strcmp(tp->fname, "mystrtokdq") == 0) { + got = mystrtokdq(&cp, CHARS_SPACE); + } else { + msg_panic("invalid function name: %s", tp->fname); + } + if ((match = (got && tp->want[n]) ? + (strcmp(got, tp->want[n]) == 0) : + (got == tp->want[n])) != 0) { + if (got == 0) { + break; + } + } else { + ptest_error(t, "got '%s', want '%s'", + STR_OR_NULL(got), STR_OR_NULL(tp->want[n])); + break; + } + } + if (n >= WANT_SIZE) + msg_panic("need to increase WANT_SIZE"); + myfree(saved_input); +} + +static const PTEST_CASE ptestcases[] = { + {"mystrtok empty", tester, + "mystrtok", ""}, + {"mystrtok > foo <", tester, + "mystrtok", " foo ", {"foo"}}, + {"mystrtok > foo bar <", tester, + "mystrtok", " foo bar ", {"foo", "bar"}}, + {"mystrtokq empty", tester, + "mystrtokq", ""}, + {"mystrtokq >foo bar<", tester, + "mystrtokq", "foo bar", {"foo", "bar"}}, + {"mystrtokq >{ bar }<", tester, + "mystrtokq", "{ bar } ", {"{ bar }"}}, + {"mystrtokq >foo { bar } baz<", tester, + "mystrtokq", "foo { bar } baz", {"foo", "{ bar }", "baz"}}, + {"mystrtokq >foo{ bar } baz<", tester, + "mystrtokq", "foo{ bar } baz", {"foo{ bar }", "baz"}}, + {"mystrtokq >foo { bar }baz<", tester, + "mystrtokq", "foo { bar }baz", {"foo", "{ bar }baz"}}, + {"mystrtokdq empty", tester, + "mystrtokdq", ""}, + {"mystrtokdq > foo <", tester, + "mystrtokdq", " foo ", {"foo"}}, + {"mystrtokdq > foo bar <", tester, + "mystrtokdq", " foo bar ", {"foo", "bar"}}, + {"mystrtokdq > foo\\ bar <", tester, + "mystrtokdq", " foo\\ bar ", {"foo\\ bar"}}, + {"mystrtokdq > foo \\\" bar<", tester, + "mystrtokdq", " foo \\\" bar", {"foo", "\\\"", "bar"}}, + {"mystrtokdq > foo \" bar baz\" <", tester, + "mystrtokdq", " foo \" bar baz\" ", {"foo", "\" bar baz\""}}, +}; + +#include diff --git a/postfix/src/util/name_mask.c b/postfix/src/util/name_mask.c index 284d4fa9d..764e3c4ae 100644 --- a/postfix/src/util/name_mask.c +++ b/postfix/src/util/name_mask.c @@ -156,6 +156,9 @@ /* Enable case-insensitive matching. /* This feature is not enabled by default when calling name_mask(); /* it has no effect with str_name_mask(). +/* .IP NAME_MASK_NULL +/* When converting from mask to string, output "0" when the +/* input mask is empty. /* .IP NAME_MASK_COMMA /* Use comma instead of space when converting a mask to string. /* .IP NAME_MASK_PIPE @@ -308,6 +311,8 @@ const char *str_name_mask_opt(VSTRING *buf, const char *context, } if ((len = VSTRING_LEN(buf)) > 0) vstring_truncate(buf, len - 1); + else if (flags & NAME_MASK_NULL) + vstring_strcat(buf, "0"); VSTRING_TERMINATE(buf); return (STR(buf)); @@ -316,7 +321,7 @@ const char *str_name_mask_opt(VSTRING *buf, const char *context, /* long_name_mask_delim_opt - compute mask corresponding to list of names */ long long_name_mask_delim_opt(const char *context, - const LONG_NAME_MASK * table, + const LONG_NAME_MASK *table, const char *names, const char *delim, int flags) { @@ -378,7 +383,7 @@ long long_name_mask_delim_opt(const char *context, /* str_long_name_mask_opt - mask to string */ const char *str_long_name_mask_opt(VSTRING *buf, const char *context, - const LONG_NAME_MASK * table, + const LONG_NAME_MASK *table, long mask, int flags) { const char *myname = "name_mask"; diff --git a/postfix/src/util/name_mask.h b/postfix/src/util/name_mask.h index 05e45ec38..492470506 100644 --- a/postfix/src/util/name_mask.h +++ b/postfix/src/util/name_mask.h @@ -32,6 +32,7 @@ typedef struct { #define NAME_MASK_NUMBER (1<<5) #define NAME_MASK_WARN (1<<6) #define NAME_MASK_IGNORE (1<<7) +#define NAME_MASK_NULL (1<<8) #define NAME_MASK_REQUIRED \ (NAME_MASK_FATAL | NAME_MASK_RETURN | NAME_MASK_WARN | NAME_MASK_IGNORE) diff --git a/postfix/src/util/stream_test.c b/postfix/src/util/test_sunos5_stream.c similarity index 100% rename from postfix/src/util/stream_test.c rename to postfix/src/util/test_sunos5_stream.c diff --git a/postfix/src/util/unescape.in b/postfix/src/util/unescape.in deleted file mode 100644 index 41f24a72d..000000000 --- a/postfix/src/util/unescape.in +++ /dev/null @@ -1,4 +0,0 @@ -\a\b\c\d\e\f\g\h\i\j\k\l\m\n\o\p\q\r\s\t\u\v\w\x\y\z -\1\2\3\4\5\6\7\8\9 -\1234\2345\3456\4567 -rcpt to: diff --git a/postfix/src/util/unescape.ref b/postfix/src/util/unescape.ref deleted file mode 100644 index db16fa879..000000000 --- a/postfix/src/util/unescape.ref +++ /dev/null @@ -1,11 +0,0 @@ -0000000 \a \b c d e \f g h i j k l m \n o p - 007 010 143 144 145 014 147 150 151 152 153 154 155 012 157 160 -0000020 q \r s \t u \v w x y z \n 001 002 003 004 005 - 161 015 163 011 165 013 167 170 171 172 012 001 002 003 004 005 -0000040 006 \a 8 9 \n S 4 234 5 345 6 . 7 \n r c - 006 007 070 071 012 123 064 234 065 345 066 056 067 012 162 143 -0000060 p t t o : < w i e t s e @ π ** - 160 164 040 164 157 072 074 167 151 145 164 163 145 100 317 200 -0000100 . p o r c u p i n e . o r g > \n - 056 160 157 162 143 165 160 151 156 145 056 157 162 147 076 012 -0000120 diff --git a/postfix/src/util/unescape_test.c b/postfix/src/util/unescape_test.c new file mode 100644 index 000000000..9a22749a9 --- /dev/null +++ b/postfix/src/util/unescape_test.c @@ -0,0 +1,94 @@ + /* + * Test program to exercise unescape.c. See ptest_main.h for a documented + * example. + */ + + /* + * System library. + */ +#include +#include + + /* + * Utility library + */ +#include +#include + + /* + * Test library. + */ +#include + +typedef struct PTEST_CASE { + const char *testname; /* Human-readable description */ + void (*action) (PTEST_CTX *, const struct PTEST_CASE *); + const char *input; + const char *want; +} PTEST_CASE; + + /* + * SLMs. + */ +#define STR vstring_str + +static char *to_octal_string(VSTRING *buf, const char *str) +{ + const unsigned char *cp; + + VSTRING_RESET(buf); + for (cp = (const unsigned char *) str; *cp; cp++) { + vstring_sprintf_append(buf, "%03o", *cp); + if (cp[1]) + vstring_strcat(buf, " "); + } + VSTRING_TERMINATE(buf); + return (STR(buf)); +} + +static void test_unescape(PTEST_CTX *t, const PTEST_CASE *tp) +{ + VSTRING *got, *got_octal, *want_octal;; + + got = vstring_alloc(100); + got_octal = vstring_alloc(100); + want_octal = vstring_alloc(100); + + unescape(got, tp->input); + if (strcmp(STR(got), tp->want) != 0) + ptest_error(t, "unescape got '%s' want '%s'", + to_octal_string(got_octal, STR(got)), + to_octal_string(want_octal, tp->want)); + vstring_free(got); + vstring_free(got_octal); + vstring_free(want_octal); +} + +static const PTEST_CASE ptestcases[] = { + { + /* name */ "escape lowecase a-z", + /* action */ test_unescape, + /* input */ "\\a\\b\\c\\d\\e\\f\\g\\h\\i\\j\\k\\l\\m\\n\\o\\p\\q\\r\\s\\t\\u\\v\\w\\x\\y\\z", + /* want */ "\a\bcde\fghijklm\nopq\rs\tu\vwxyz", + }, { + /* name */ "escape digits 0-9", + /* action */ test_unescape, + /* input */ "\\1\\2\\3\\4\\5\\6\\7\\8\\9", + /* want */ "\001\002\003\004\005\006\00789", + }, { + /* name */ "\\nnn plus digit", + /* action */ test_unescape, + /* input */ "\\1234\\2345\\3456\\04567", + /* want */ "\1234\2345\3456\04567", + }, { + /* name */ "non-ascii email", + /* action */ test_unescape, + /* input */ "rcpt to:", + /* want */ "rcpt to:", + }, +}; + + /* + * Test library. + */ +#include diff --git a/postfix/src/util/wrap_netdb.c b/postfix/src/util/wrap_netdb.c new file mode 100644 index 000000000..7d33f2fff --- /dev/null +++ b/postfix/src/util/wrap_netdb.c @@ -0,0 +1,136 @@ +/*++ +/* NAME +/* wrap_netdb 3 +/* SUMMARY +/* mockable netdb wrappers +/* SYNOPSIS +/* #include +/* +/* int wrap_getaddrinfo( +/* const char *hostname, +/* const char *servname, +/* const struct addrinfo *hints, +/* struct addrinfo **res) +/* +/* void wrap_freeaddrinfo(struct addrinfo *ai) +/* +/* int wrap_getnameinfo( +/* const struct sockaddr *sa, +/* socklen_t salen, +/* char *host, +/* size_t hostlen, +/* char *serv, +/* size_t servlen, +/* int flags) +/* +/* struct servent *wrap_getservbyname( +/* const char *name, +/* const char *proto) +/* +/* struct servent *wrap_getservbyport( +/* int port, +/* const char *proto) +/* DESCRIPTION +/* This module is a NOOP when the NO_MOCK_WRAPPERS macro is +/* defined. +/* +/* This code implements a workaround for inconsistencies in +/* netdb.h header files, that can break test mock functions +/* that have the same name as a system library function. +/* +/* For example, BSD and older Linux getnameinfo() implementations +/* use size_t for the hostlen and servlen arguments, but GLIBC +/* 2.2 and later use socklen_t instead; those two types have +/* different sizes on LP64 systems. For a rationale why those +/* types were changed, see +/* https://pubs.opengroup.org/onlinepubs/9699919799/xrat/V4_xsh_chap02.html#tag_22_02_10_06 +/* +/* By default, 1) the header file of this module redirects +/* netdb function calls by prepending a "wrap_" name prefix +/* to netdb function names, 2) the code of this module implements +/* functions with "wrap_" name prefixes that forward redirected +/* calls to the real netdb functions, while taking care of any +/* necessary type incompatibilities, and 3) test mock functions +/* use the "wrap_" prefixed function names instead of the netdb +/* function names. Build with -DNO_MOCK_WRAPPERS to avoid +/* this workaround. +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* Google, Inc. +/* 111 8th Avenue +/* New York, NY 10011, USA +/*--*/ + + /* + * System library. + */ +#include +#include + +#ifndef NO_MOCK_WRAPPERS + +/* wrap_getaddrinfo - wrap getaddrinfo() with stable internal API */ + +int wrap_getaddrinfo(const char *hostname, const char *servname, + const struct addrinfo *hints, + struct addrinfo **res) +{ +#undef getaddrinfo + return (getaddrinfo(hostname, servname, hints, res)); +} + +/* wrap_freeaddrinfo - wrap freeaddrinfo() with stable internal API */ + +void wrap_freeaddrinfo(struct addrinfo *ai) +{ +#undef freeaddrinfo + freeaddrinfo(ai); +} + +/* wrap_getnameinfo - wrap getnameinfo() with stable internal API */ + +int wrap_getnameinfo(const struct sockaddr *sa, socklen_t salen, + char *host, size_t hostlen, + char *serv, size_t servlen, int flags) +{ +#undef getnameinfo + return (getnameinfo(sa, salen, host, hostlen, serv, servlen, flags)); +} + +/* wrap_getservbyname - wrap getservbyname() with stable internal API */ + +struct servent *wrap_getservbyname(const char *name, const char *proto) +{ +#undef getservbyname + return (getservbyname(name, proto)); +} + +/* wrap_getservbyport - wrap getservbyport() with stable internal API */ + +struct servent *wrap_getservbyport(int port, const char *proto) +{ +#undef getservbyport + return (getservbyport(port, proto)); +} + +/* wrap_setservent - wrap setservent() with stable internal API */ + +void wrap_setservent(int stayopen) +{ +#undef setservent + return (setservent(stayopen)); +} + +/* wrap_endservent - wrap endservent() with stable internal API */ + +void wrap_endservent(void) +{ +#undef endservent + return (endservent()); +} + +#endif diff --git a/postfix/src/util/wrap_netdb.h b/postfix/src/util/wrap_netdb.h new file mode 100644 index 000000000..3e49e7a0d --- /dev/null +++ b/postfix/src/util/wrap_netdb.h @@ -0,0 +1,57 @@ +#ifndef _WRAP_NETDB_H_INCLUDED_ +#define _WRAP_NETDB_H_INCLUDED_ + +/*++ +/* NAME +/* wrap_netdb 3h +/* SUMMARY +/* mockable netdb wrappers +/* SYNOPSIS +/* #include +/* DESCRIPTION +/* .nf + + /* + * System library + */ +#include +#include + + /* + * External interface. + */ +#ifndef NO_MOCK_WRAPPERS +extern int wrap_getaddrinfo(const char *, const char *, + const struct addrinfo *, + struct addrinfo **); +extern void wrap_freeaddrinfo(struct addrinfo *); +extern int wrap_getnameinfo(const struct sockaddr *, socklen_t, char *, + size_t, char *, size_t, int); + +#define getaddrinfo wrap_getaddrinfo +#define freeaddrinfo wrap_freeaddrinfo +#define getnameinfo wrap_getnameinfo + +extern struct servent *wrap_getservbyname(const char *, const char *); +extern struct servent *wrap_getservbyport(int, const char *); +extern void wrap_setservent(int); +extern void wrap_endservent(void); + +#define getservbyname wrap_getservbyname +#define getservbyport wrap_getservbyport +#define setservent wrap_setservent +#define endservent wrap_endservent +#endif + +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* Google, Inc. +/* 111 8th Avenue +/* New York, NY 10011, USA +/*--*/ + +#endif diff --git a/postfix/src/xsasl/Makefile.in b/postfix/src/xsasl/Makefile.in index ad483021b..f0dac8fa5 100644 --- a/postfix/src/xsasl/Makefile.in +++ b/postfix/src/xsasl/Makefile.in @@ -148,6 +148,7 @@ xsasl_dovecot_server.o: ../../include/vbuf.h xsasl_dovecot_server.o: ../../include/vstream.h xsasl_dovecot_server.o: ../../include/vstring.h xsasl_dovecot_server.o: ../../include/vstring_vstream.h +xsasl_dovecot_server.o: ../../include/wrap_netdb.h xsasl_dovecot_server.o: xsasl.h xsasl_dovecot_server.o: xsasl_dovecot.h xsasl_dovecot_server.o: xsasl_dovecot_server.c