]> git.ipfire.org Git - thirdparty/postfix.git/commitdiff
postfix-3.8-20220724-nonprod
authorWietse Venema <wietse@porcupine.org>
Sun, 24 Jul 2022 05:00:00 +0000 (00:00 -0500)
committerViktor Dukhovni <postfix-users@dukhovni.org>
Sat, 6 Aug 2022 16:52:24 +0000 (12:52 -0400)
145 files changed:
postfix/.indent.pro
postfix/HISTORY
postfix/Makefile.in
postfix/TODO [new file with mode: 0644]
postfix/makedefs
postfix/proto/stop
postfix/proto/stop.double-cc
postfix/proto/stop.spell-cc
postfix/proto/stop.spell-history
postfix/proto/stop.spell-proto-html
postfix/src/bounce/Makefile.in
postfix/src/bounce/bounce_notify_util_tester.c
postfix/src/dns/Makefile.in
postfix/src/dns/dns.h
postfix/src/dns/dns_lookup.c
postfix/src/dns/dns_lookup_types.c [new file with mode: 0644]
postfix/src/dns/dns_lookup_types_test.c [new file with mode: 0644]
postfix/src/dnsblog/Makefile.in
postfix/src/global/Makefile.in
postfix/src/global/config_known_tcp_ports.c
postfix/src/global/config_known_tcp_ports_test.c [new file with mode: 0644]
postfix/src/global/delivered_hdr.c
postfix/src/global/delivered_hdr_test.c [new file with mode: 0644]
postfix/src/global/dict_mysql.c
postfix/src/global/fold_addr.h
postfix/src/global/hfrom_format.c
postfix/src/global/hfrom_format.ref [deleted file]
postfix/src/global/hfrom_format_test.c [new file with mode: 0644]
postfix/src/global/login_sender_match.c
postfix/src/global/login_sender_match.ref [deleted file]
postfix/src/global/login_sender_match_test.c [new file with mode: 0644]
postfix/src/global/mail_dict.c
postfix/src/global/map_search.c
postfix/src/global/map_search.ref [deleted file]
postfix/src/global/map_search_test.c [new file with mode: 0644]
postfix/src/global/maps.ref
postfix/src/global/normalize_mailhost_addr.c
postfix/src/global/normalize_mailhost_addr_test.c [new file with mode: 0644]
postfix/src/global/smtp_reply_footer.c
postfix/src/global/smtp_reply_footer.ref [deleted file]
postfix/src/global/smtp_reply_footer_test.c [new file with mode: 0644]
postfix/src/global/test_server_main.c [moved from postfix/src/global/test_main.c with 93% similarity]
postfix/src/global/test_server_main.h [moved from postfix/src/global/test_main.h with 94% similarity]
postfix/src/local/Makefile.in
postfix/src/local/biff_notify.c
postfix/src/master/Makefile.in
postfix/src/postconf/Makefile.in
postfix/src/postlogd/.indent.pro [new symlink]
postfix/src/postscreen/Makefile.in
postfix/src/posttls-finger/Makefile.in
postfix/src/ptest/.indent.pro [new symlink]
postfix/src/ptest/Makefile.in [new file with mode: 0644]
postfix/src/ptest/pmock_expect.c [new file with mode: 0644]
postfix/src/ptest/pmock_expect.h [new file with mode: 0644]
postfix/src/ptest/pmock_expect_test.c [new file with mode: 0644]
postfix/src/ptest/ptest.h [new file with mode: 0644]
postfix/src/ptest/ptest_ctx.c [new file with mode: 0644]
postfix/src/ptest/ptest_error.c [new file with mode: 0644]
postfix/src/ptest/ptest_log.c [new file with mode: 0644]
postfix/src/ptest/ptest_log_test.c [new file with mode: 0644]
postfix/src/ptest/ptest_main.h [new file with mode: 0644]
postfix/src/ptest/ptest_run.c [new file with mode: 0644]
postfix/src/qmqpd/Makefile.in
postfix/src/smtp/Makefile.in
postfix/src/smtp/smtp_addr.c
postfix/src/smtpd/Makefile.in
postfix/src/smtpstone/Makefile.in
postfix/src/testing/.indent.pro [new symlink]
postfix/src/testing/Makefile.in [new file with mode: 0644]
postfix/src/testing/addrinfo_to_string.c [new file with mode: 0644]
postfix/src/testing/addrinfo_to_string.h [new file with mode: 0644]
postfix/src/testing/make_addr.c [new file with mode: 0644]
postfix/src/testing/make_addr.h [new file with mode: 0644]
postfix/src/testing/match_addr.c [new file with mode: 0644]
postfix/src/testing/match_addr.h [new file with mode: 0644]
postfix/src/testing/match_addr_test.c [new file with mode: 0644]
postfix/src/testing/match_basic.c [new file with mode: 0644]
postfix/src/testing/match_basic.h [new file with mode: 0644]
postfix/src/testing/mock_dns.h [new file with mode: 0644]
postfix/src/testing/mock_dns_lookup.c [new file with mode: 0644]
postfix/src/testing/mock_dns_lookup_test.c [new file with mode: 0644]
postfix/src/testing/mock_getaddrinfo.c [new file with mode: 0644]
postfix/src/testing/mock_getaddrinfo.h [new file with mode: 0644]
postfix/src/testing/mock_getaddrinfo_test.c [new file with mode: 0644]
postfix/src/testing/mock_myaddrinfo.c [new file with mode: 0644]
postfix/src/testing/mock_myaddrinfo.h [new file with mode: 0644]
postfix/src/testing/mock_myaddrinfo_test.c [new file with mode: 0644]
postfix/src/testing/mock_servent.c [new file with mode: 0644]
postfix/src/testing/mock_servent.h [new file with mode: 0644]
postfix/src/testing/mock_servent_test.c [new file with mode: 0644]
postfix/src/tls/Makefile.in
postfix/src/tlsmgr/Makefile.in
postfix/src/tlsproxy/Makefile.in
postfix/src/util/Makefile.in
postfix/src/util/argv.c
postfix/src/util/argv_test.c [new file with mode: 0644]
postfix/src/util/dict.h
postfix/src/util/dict_cli.c [moved from postfix/src/util/dict_test.c with 99% similarity]
postfix/src/util/dict_open.c
postfix/src/util/dict_pipe_test.c [new file with mode: 0644]
postfix/src/util/dict_pipe_test.in [deleted file]
postfix/src/util/dict_pipe_test.ref [deleted file]
postfix/src/util/dict_stream.c
postfix/src/util/dict_stream.ref [deleted file]
postfix/src/util/dict_stream_test.c [new file with mode: 0644]
postfix/src/util/dict_union_test.c [new file with mode: 0644]
postfix/src/util/dict_union_test.in [deleted file]
postfix/src/util/dict_union_test.ref [deleted file]
postfix/src/util/find_inet_service.c [new file with mode: 0644]
postfix/src/util/find_inet_service.h [new file with mode: 0644]
postfix/src/util/find_inet_service_test.c [new file with mode: 0644]
postfix/src/util/hash_fnv.c
postfix/src/util/hash_fnv_test.c [new file with mode: 0644]
postfix/src/util/inet_addr_list.c
postfix/src/util/known_tcp_ports.c
postfix/src/util/known_tcp_ports.ref [deleted file]
postfix/src/util/known_tcp_ports_test.c [new file with mode: 0644]
postfix/src/util/msg.c
postfix/src/util/msg.h
postfix/src/util/msg_output.c
postfix/src/util/msg_output.h
postfix/src/util/msg_output_test.c [new file with mode: 0644]
postfix/src/util/msg_vstream.c
postfix/src/util/msg_vstream.h
postfix/src/util/myaddrinfo.c
postfix/src/util/myaddrinfo.h
postfix/src/util/myaddrinfo.ref [deleted file]
postfix/src/util/myaddrinfo.ref2 [deleted file]
postfix/src/util/myaddrinfo4.ref [deleted file]
postfix/src/util/myaddrinfo4.ref2 [deleted file]
postfix/src/util/myaddrinfo_test.c [new file with mode: 0644]
postfix/src/util/mymalloc.c
postfix/src/util/mymalloc_test.c [new file with mode: 0644]
postfix/src/util/mystrtok.c
postfix/src/util/mystrtok.ref [deleted file]
postfix/src/util/mystrtok_test.c [new file with mode: 0644]
postfix/src/util/name_mask.c
postfix/src/util/name_mask.h
postfix/src/util/test_sunos5_stream.c [moved from postfix/src/util/stream_test.c with 100% similarity]
postfix/src/util/unescape.in [deleted file]
postfix/src/util/unescape.ref [deleted file]
postfix/src/util/unescape_test.c [new file with mode: 0644]
postfix/src/util/wrap_netdb.c [new file with mode: 0644]
postfix/src/util/wrap_netdb.h [new file with mode: 0644]
postfix/src/xsasl/Makefile.in

index 1f8431f0d3b8098a134d56ce47a5949101dad029..7a382f767e0b8cd53b4ab9e48ef1eed287c81743 100644 (file)
@@ -1,4 +1,5 @@
 -TABOUNCE_STATE
+-Taddrinfo
 -TADDR_MATCH_LIST
 -TADDR_PATTERN
 -TALIAS_TOKEN
 -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
 -TPSC_SMTPD_COMMAND
 -TPSC_STARTTLS
 -TPSC_STATE
+-TPTEST_CASE
+-TPTEST_CTX
 -TQMGR_ENTRY
 -TQMGR_FEEDBACK
 -TQMGR_JOB
 -TSTRING_LIST
 -TSTRING_TABLE
 -TSYS_EXITS_DETAIL
--TTEST_CASE
+-TTEST_JMP_BUF
 -Ttime_t
 -Ttlsa_filter
 -TTLS_APPL_STATE
index 443cb2ab2bc0a206b75214941f172e06fa2873d4..aca90aee1ede7a9a6694c3598df9391ac688c172 100644 (file)
@@ -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 <stdio.h> 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.
index 5bc7a7cb173a6e5e982b9e4fd669715f84971078..72e99a913070fea5dad4c0bc45562e8a04bacbbf 100644 (file)
@@ -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 (file)
index 0000000..cc67947
--- /dev/null
@@ -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 <testing.h>? 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 
+
index 2839f3a8a89b35a227d1135ffd34a75c26329876..09ee561bfbb0957856c96fc46f200ce129d4e7d8 100644 (file)
@@ -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"
index d8ae008283a3f830ece666b1950772acb41f5c3a..9354b5f763cc0e9fbf69b724eaff33bb6719a9d5 100644 (file)
@@ -1565,3 +1565,5 @@ raf
 fnvz
 hc
 mkmap
+hc
+mkmap
index ceddb7f5cddcd2d150e498b3ae0195221b4ab145..41c5ffa0f4f058fd86711f2a37257fda567090e9 100644 (file)
@@ -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
index ea6ad0038b188d009608bae4524a7a99c2d560be..550d998d058c5062890d6ede72235f402572be58 100644 (file)
@@ -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
index 16c43bfd2dfc8dc21661e839d6a4dc93b13d51b8..e54670f50c4993a33c3c6b5248cdce6bd7f9a310 100644 (file)
@@ -19,3 +19,10 @@ libs
 segfaulting
 srctoman
 systemd
+Amma
+Jesper
+Pau
+gmock
+sunos
+Oss
+Spil
index a4ad7c594dd605674c3f652294e08ddf43a0ab78..616fb118e755ec2fdbd48c325bb31b6f2ef0ae14 100644 (file)
@@ -348,3 +348,4 @@ wiki
 JÃ
 ng
 rsyslogd
+ptest
index 969413a0003d993be817c7e76ff8f9d8c059cf98..9a7a69257c6df762a37481c5960527f1e088edaf 100644 (file)
@@ -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
index da13f470579903312bb5b0de654e8d5332a72cc8..91406f4cd9daef2ce2a02cf5f47467ca68d3e88f 100644 (file)
@@ -30,7 +30,7 @@
  /*
   * Testing library.
   */
-#include <test_main.h>
+#include <test_server_main.h>
 
 #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),
index 795f9ba2a807de4203e94f0866f172f16b2e9243..fcbe4bb5fb4e55d8831a1bf9475a08d1af9637d9 100644 (file)
@@ -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
index 5f53dbc8ffaf3ed09f770b4d1ec9c9f3dc5c5423..43f4a8e32f6595753bdc3871eb216e1c36519dc5 100644 (file)
@@ -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, \
index 1c12a889dd247add6e29b371408dbd8fd8c97d63..748c1a69aca0f1969e46edf4298613c7c9901739 100644 (file)
@@ -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 (file)
index 0000000..de89ca6
--- /dev/null
@@ -0,0 +1,183 @@
+/*++
+/* NAME
+/*     dns_lookup_types 3
+/* SUMMARY
+/*     domain name service lookup for multiple types
+/* SYNOPSIS
+/*     #include <dns.h>
+/*
+/*     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 <sys_defs.h>
+
+/* Utility library. */
+
+#include <vstring.h>
+#include <msg.h>
+
+/* DNS library. */
+
+#define LIBDNS_INTERNAL
+#include <dns.h>
+
+ /*
+  * 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 (file)
index 0000000..78d60cf
--- /dev/null
@@ -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 <sys_defs.h>
+
+ /*
+  * Utility library.
+  */
+#include <msg.h>
+#include <vstring.h>
+
+ /*
+  * DNS library.
+  */
+#include <dns.h>
+
+ /*
+  * Test library.
+  */
+#include <mock_dns.h>
+#include <ptest.h>
+
+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 <ptest_main.h>
index c2ed848811813af8de6d9b34e3310890fbf39c35..042e40c13ecef4fbac22fb92daf74081a115faf5 100644 (file)
@@ -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
index 86390ede24c3b8c65352a853e61e51386bd5526d..a8c7ccd4c51f5ac05f743aca4bcf092970d931c5 100644 (file)
@@ -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.in >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
index 563bbd356d4cdc7cccf9e5514e4a1d29c269c5fd..4a0d43300a2adeaae04d91e869e7e5aee0ca9a40 100644 (file)
@@ -123,135 +123,3 @@ void    config_known_tcp_ports(const char *source, const char *settings)
     }
     argv_free(associations);
 }
-
-#ifdef TEST
-
-#include <stdlib.h>
-#include <string.h>
-#include <msg_vstream.h>
-
-#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 (file)
index 0000000..e1c331e
--- /dev/null
@@ -0,0 +1,97 @@
+ /*
+  * Test program to exercise config_known_tcp_ports.c. See ptest_main.h for a
+  * documented example.
+  */
+
+ /*
+  * System library.
+  */
+#include <sys_defs.h>
+#include <string.h>
+
+ /*
+  * Utility library.
+  */
+#include <known_tcp_ports.h>
+#include <vstring.h>
+
+ /*
+  * Global library.
+  */
+#include <config_known_tcp_ports.h>
+
+ /*
+  * Test library.
+  */
+#include <ptest.h>
+
+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 <ptest_main.h>
index 0aea1cc1dbca814bc6734b302c710bcbafe282aa..1b20b38565a13bcd92e39a0b4cf155b5b1a20b39 100644 (file)
@@ -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 <msg_vstream.h>
-#include <mail_params.h>
-
-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 (file)
index 0000000..d470867
--- /dev/null
@@ -0,0 +1,117 @@
+ /*
+  * Test program to exercise delivered_hdr.c. See ptest_main.h for a
+  * documented example.
+  */
+
+ /*
+  * System library.
+  */
+#include <sys_defs.h>
+#include <string.h>
+
+ /*
+  * Utility library.
+  */
+#include <msg_vstream.h>
+#include <mymalloc.h>
+
+ /*
+  * Global library.
+  */
+#include <mail_params.h>
+#include <delivered_hdr.h>
+#include <rec_type.h>
+#include <record.h>
+
+ /*
+  * Test library.
+  */
+#include <ptest.h>
+
+ /*
+  * 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 <ptest_main.h>
index d90bc090dafea1d95391db0600c7a5a8a322eedd..00e37ac3defb2a295a81cea8756650e11d27b0d5 100644 (file)
@@ -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);
index ba8021df22526c6c38f59c7f7bba66ef0c854c04..67008154b0b7681119dd5334c220f5a6b6c808ec 100644 (file)
 /* DESCRIPTION
 /* .nf
 
+ /*
+  * Utility library.
+  */
+#include <vstring.h>
+
  /*
   * External interface.
   */
index f0f850a3e5e9fc718abedf5e00294e8cc879f571..8510d8785b4a0c0e810196ed0ba4d1f49ae657b1 100644 (file)
@@ -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 (file)
index 28ba870..0000000
+++ /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 (file)
index 0000000..9a6fe2e
--- /dev/null
@@ -0,0 +1,139 @@
+ /*
+  * Test program to exercise hfrom_format.c. See ptest_main.h for a
+  * documented example.
+  */
+
+ /*
+  * System library.
+  */
+#include <sys_defs.h>
+#include <string.h>
+
+ /*
+  * Global library.
+  */
+#include <hfrom_format.h>
+#include <mail_params.h>
+
+ /*
+  * Test library.
+  */
+#include <ptest.h>
+
+ /*
+  * 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 <ptest_main.h>
index e263762ef42f77f0b6309861583b59cd1817eba7..c03c53f3db7810e5d1b0a01b0ee3bec8c0f69239 100644 (file)
@@ -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 (file)
index 20ea483..0000000
+++ /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 (file)
index 0000000..16a366f
--- /dev/null
@@ -0,0 +1,142 @@
+ /*
+  * Test program to exercise login_sender_match.c. See and ptest_main.h for a
+  * documented example.
+  */
+
+ /*
+  * System library.
+  */
+#include <sys_defs.h>
+
+ /*
+  * Global library.
+  */
+#include <mail_params.h>
+#include <login_sender_match.h>
+
+ /*
+  * Test library.
+  */
+#include <ptest.h>
+
+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 <ptest_main.h>
index c640a807a3226eb5cba7237c30f64f24e7982548..1c391fbd8ed64d39ef55adeaebe11b4b3208248f 100644 (file)
@@ -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);
 }
 
index be4b42b33e9a3c70bc7c49efc19ff570335b3968..497fbb904cc6fd5bdb6f3c59c248ed697158cd47 100644 (file)
@@ -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 <stdlib.h>
-
- /*
-  * 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 (file)
index f072f25..0000000
+++ /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 (file)
index 0000000..ef8f88c
--- /dev/null
@@ -0,0 +1,189 @@
+ /*
+  * Test program to exercise map_search.c. See ptest_main.h for a documented
+  * example.
+  */
+
+ /*
+  * System library.
+  */
+#include <sys_defs.h>
+#include <stdlib.h>
+#include <string.h>
+
+ /*
+  * Utility library.
+  */
+#include <stringops.h>
+#include <vstring.h>
+
+ /*
+  * Global library.
+  */
+#include <map_search.h>
+
+ /*
+  * Test library.
+  */
+#include <ptest.h>
+
+ /*
+  * 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 <ptest_main.h>
index 84033c7d848efdddc0fa85331899092c0233b744..2d3ad20ee6499edb5f82e25ae29c335c1ace23e1 100644 (file)
@@ -1,3 +1,4 @@
+unknown: dict_open_lookup: fail
 unknown: dict_open: fail:1maps
 unknown: dict_register: fail:1maps(0,lock) 1
 "": not found
index ba0f7bd1016c277a62d1d104f965a9bfd775593b..a2a52e3b36b7aec9e942c9b5c472d7ec7a43bfea 100644 (file)
@@ -149,111 +149,3 @@ int     normalize_mailhost_addr(const char *string, char **mailhost_addr,
     }
     return (0);
 }
-
- /*
-  * Test program.
-  */
-#ifdef TEST
-#include <stdlib.h>
-#include <mymalloc.h>
-#include <msg.h>
-
- /*
-  * 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 (file)
index 0000000..5dcf58b
--- /dev/null
@@ -0,0 +1,179 @@
+ /*
+  * Test program to exercise normalize_mailhost_addr.c. See ptest_main.h for
+  * a documented example.
+  */
+
+ /*
+  * System library.
+  */
+#include <sys_defs.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/socket.h>
+
+ /*
+  * Utility library.
+  */
+#include <mymalloc.h>
+#include <inet_proto.h>
+
+ /*
+  * Global library.
+  */
+#include <normalize_mailhost_addr.h>
+
+ /*
+  * Test library.
+  */
+#include <ptest.h>
+
+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 <ptest_main.h>
index 6e5bb75d96310aa442e771e18cb19d9655d6fb44..ef1e40d39e073ba8d7a399695611698bcca713d1 100644 (file)
@@ -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 <stdlib.h>
-#include <unistd.h>
-#include <string.h>
-#include <msg.h>
-#include <vstream.h>
-#include <vstring_vstream.h>
-#include <msg_vstream.h>
-
-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 (file)
index d7eb5a7..0000000
+++ /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 (file)
index 0000000..13aeaab
--- /dev/null
@@ -0,0 +1,90 @@
+ /*
+  * Test program to exercise smtp_reply_footer.c. See ptest_main.h for a
+  * documented example.
+  */
+
+ /*
+  * System library.
+  */
+#include <sys_defs.h>
+#include <string.h>
+
+ /*
+  * Global library.
+  */
+#include <dsn_util.h>
+#include <smtp_reply_footer.h>
+
+ /*
+  * Test library.
+  */
+#include <ptest.h>
+
+ /*
+  * 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 <ptest_main.h>
similarity index 93%
rename from postfix/src/global/test_main.c
rename to postfix/src/global/test_server_main.c
index a783ce354618dc79feb498f07fbda23b6987d663..936d897d9e44cdd85c7612c78d75f2a35fcf6747 100644 (file)
@@ -1,12 +1,12 @@
 /*++
 /* NAME
-/*     test_main 3
+/*     test_server_main 3
 /* SUMMARY
 /*     test main program
 /* SYNOPSIS
-/*     #include <test_main.h>
+/*     #include <test_server_main.h>
 /*
-/*     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);
 /*     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
  /*
   * Test library.
   */
-#include <test_main.h>
+#include <test_server_main.h>
 
 /* 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;
similarity index 94%
rename from postfix/src/global/test_main.h
rename to postfix/src/global/test_server_main.h
index aea605a1f3cbfdc58e1878718308a1c2ade5d4f4..c2c24719153fc0274c6a730785759aa9e816e027 100644 (file)
@@ -1,10 +1,10 @@
 /*++
 /* NAME
-/*     test_main       3h
+/*     test_server_main        3h
 /* SUMMARY
 /*     test    main program
 /* SYNOPSIS
-/*     #include <test_main.h>
+/*     #include <test_server_main.h>
 /* 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
index 648ad5133c9d6a56e16310e18ab0acc5738cce0d..9659b6195de8b2080e45446b582cbe1bd279bccc 100644 (file)
@@ -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
index a6a49258ee65e39fd02d5f3a3828e4052ccb63b6..6aa75a2205ee8dc1f3327cff3863c961993d82be 100644 (file)
 /*     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 "sys_defs.h"
 #include <sys/socket.h>
 #include <netinet/in.h>
-#include <netdb.h>
 #include <string.h>
 
 /* Utility library. */
 
 #include <msg.h>
 #include <iostuff.h>
+#include <myaddrinfo.h>
+#include <inet_proto.h>
 
 /* Application-specific. */
 
 
 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");
 }
index db67a68eb8e9da0ecedce48990a70db3c34ff206..51987a79be0d6aa6cce9d6f6a1151c87b2903f4a 100644 (file)
@@ -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
index 4f6536fd346c2b015c7a3c9c367167969e7deb24..633708887f5c18b3a12147cca8621fe23de17c9e 100644 (file)
@@ -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 (symlink)
index 0000000..5c837ec
--- /dev/null
@@ -0,0 +1 @@
+../../.indent.pro
\ No newline at end of file
index 8ed869229887ba9ab46792d12a8751454251a76f..034c01be78cd4ac11e6cf9589b6b5b210d85c112 100644 (file)
@@ -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
index d246303f2fee89247a252c9b45c4ec6ebdae515f..6b8d53de529422fa84232f068fb55ed98d5ad43f 100644 (file)
@@ -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 (symlink)
index 0000000..5c837ec
--- /dev/null
@@ -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 (file)
index 0000000..e9b85bb
--- /dev/null
@@ -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 (file)
index 0000000..e6dd225
--- /dev/null
@@ -0,0 +1,286 @@
+/*++
+/* NAME
+/*     pmock_expect 3h
+/* SUMMARY
+/*     mock support for hermetic tests
+/* SYNOPSIS
+/*     #include <pmock_expect.h>
+/*
+/*     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 <sys_defs.h>
+
+ /*
+  * Utility library.
+  */
+#include <mymalloc.h>
+#include <htable.h>
+
+ /*
+  * Testing library.
+  */
+#include <pmock_expect.h>
+#include <ptest.h>
+
+ /*
+  * 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 (file)
index 0000000..541cb52
--- /dev/null
@@ -0,0 +1,78 @@
+#ifndef _PMOCK_EXPECT_H_INCLUDED_
+#define _PMOCK_EXPECT_H_INCLUDED_
+
+/*++
+/* NAME
+/*     pmock_expect 3h
+/* SUMMARY
+/*     mock test support
+/* SYNOPSIS
+/*     #include <pmock_expect.h>
+/* DESCRIPTION
+/* .nf
+
+ /*
+  * Utility library.
+  */
+#include <vstring.h>
+
+ /*
+  * Test library.
+  */
+#include <ptest.h>
+
+ /*
+  * 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 (file)
index 0000000..ef440dc
--- /dev/null
@@ -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 <sys_defs.h>
+#include <stdlib.h>
+#include <string.h>
+
+ /*
+  * Utility library.
+  */
+#include <msg.h>
+
+ /*
+  * Test library.
+  */
+#include <mymalloc.h>
+#include <pmock_expect.h>
+#include <ptest.h>
+
+ /*
+  * 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 <ptest_main.h>
diff --git a/postfix/src/ptest/ptest.h b/postfix/src/ptest/ptest.h
new file mode 100644 (file)
index 0000000..ae7b57b
--- /dev/null
@@ -0,0 +1,127 @@
+#ifndef _PTEST_H_INCLUDED_
+#define _PTEST_H_INCLUDED_
+
+/*++
+/* NAME
+/*     ptest 3h
+/* SUMMARY
+/*     run-time test support
+/* SYNOPSIS
+/*     #include <ptest.h>
+/* DESCRIPTION
+/* .nf
+
+ /*
+  * System library.
+  */
+#include <sys_defs.h>
+#include <setjmp.h>
+
+ /*
+  * Utility library.
+  */
+#include <argv.h>
+#include <msg.h>                       /* XXX for MSG_JMP_BUF in PTEST_RUN */
+#include <vstream.h>
+#include <vstring.h>
+
+ /*
+  * 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 (file)
index 0000000..71b7c56
--- /dev/null
@@ -0,0 +1,131 @@
+/*++
+/* NAME
+/*     ptest_ctx 3
+/* SUMMARY
+/*     test context support
+/* SYNOPSIS
+/*     #include <ptest.h>
+/*
+/*     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 <sys_defs.h>
+#include <setjmp.h>
+
+ /*
+  * Utility library.
+  */
+#include <msg.h>
+#include <mymalloc.h>
+#include <stringops.h>
+#include <vstream.h>
+
+ /*
+  * Test library.
+  */
+#include <ptest.h>
+
+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 (file)
index 0000000..4c691de
--- /dev/null
@@ -0,0 +1,221 @@
+/*++
+/* NAME
+/*     ptest_error 3
+/* SUMMARY
+/*     test error and non-error support
+/* SYNOPSIS
+/*     #include <ptest.h>
+/*
+/*     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 <sys_defs.h>
+#include <setjmp.h>
+#include <string.h>
+
+ /*
+  * Utility library.
+  */
+#include <argv.h>
+#include <msg.h>
+#include <mymalloc.h>
+#include <vstream.h>
+#include <vstring.h>
+
+ /*
+  * Test library.
+  */
+#include <ptest.h>
+
+ /*
+  * 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 (file)
index 0000000..40212e5
--- /dev/null
@@ -0,0 +1,149 @@
+/*++
+/* NAME
+/*     ptest_log 3
+/* SUMMARY
+/*     log event receiver support
+/* SYNOPSIS
+/*     #include <ptest.h>
+/*
+/*     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 <sys_defs.h>
+#include <string.h>
+
+ /*
+  * Utility library.
+  */
+#include <msg.h>
+#include <msg_output.h>
+#include <vstring.h>
+
+ /*
+  * Test library.
+  */
+#include <ptest.h>
+
+ /*
+  * 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 (file)
index 0000000..98ce2fd
--- /dev/null
@@ -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 <sys_defs.h>
+
+ /*
+  * Test library.
+  */
+#include <ptest.h>
+
+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 <ptest_main.h>
diff --git a/postfix/src/ptest/ptest_main.h b/postfix/src/ptest/ptest_main.h
new file mode 100644 (file)
index 0000000..ff2d511
--- /dev/null
@@ -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
+/*     <pmock_expect.h>.
+/*
+/*     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 <ptest.h>
+/*     #include <pmock_expect.h>
+/*
+/*      /*
+/*       * 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 <ptest_main.h>
+/*
+/*     /* End example. */
+/* .fi
+/*
+/*     The <ptest_main.h> 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 <sys_defs.h>
+#include <string.h>
+#include <stdlib.h>
+
+ /*
+  * Utility library.
+  */
+#include <msg.h>
+#include <msg_output.h>
+#include <msg_vstream.h>
+#include <stringops.h>
+#include <vstream.h>
+
+ /*
+  * Test library.
+  */
+#include <pmock_expect.h>
+#include <ptest.h>
+
+/* 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 (file)
index 0000000..66c35ca
--- /dev/null
@@ -0,0 +1,159 @@
+/*++
+/* NAME
+/*     ptest_run 3h
+/* SUMMARY
+/*     test runner
+/* SYNOPSIS
+/*     #include <ptest.h>
+/*
+/*     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 <sys_defs.h>
+
+ /*
+  * Utility library.
+  */
+#include <msg.h>
+#include <msg_vstream.h>
+#include <vstream.h>
+
+ /*
+  * Test library.
+  */
+#include <pmock_expect.h>
+#include <ptest.h>
+
+/* 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;
+}
index d4cdf33874625999519413756eebec937a63d758..c2d8e940ac057f9f49b583d79557ed367567badb 100644 (file)
@@ -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
index 6412e0e36170b95527558012774c03ac90f1051a..bb0e278320fafe70d473ed367cd5fb4b35c7ec03 100644 (file)
@@ -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
index 2b5c126e5ad18767b504c95c44fa696264d7a925..8fd051389ee1ff0977218acbd35144bf77779aa8 100644 (file)
@@ -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
index 8c4132a30bc8523f97a046ff9129dd62be3b203f..f7d7bbb6a19c511ddcdec4ebcac72de90916bd8c 100644 (file)
@@ -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
index f86e0195b3e2b21b537744170fa3966467d10fbd..ad1d7deea7a919fc75f7f04be44964a61e57a641 100644 (file)
@@ -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 (symlink)
index 0000000..5c837ec
--- /dev/null
@@ -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 (file)
index 0000000..6a66712
--- /dev/null
@@ -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 (file)
index 0000000..c08d75a
--- /dev/null
@@ -0,0 +1,304 @@
+/*++
+/* NAME
+/*      addrinfo_to_string 3
+/* SUMMARY
+/*     address info to string conversion
+/* SYNOPSIS
+/*      #include <addrinfo_to_string.h>
+/*
+/*      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 <sys_defs.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <wrap_netdb.h>
+#include <stdio.h>                     /* sprintf/snprintf */
+
+ /*
+  * Utility library.
+  */
+#include <msg.h>
+#include <myaddrinfo.h>
+#include <name_code.h>
+#include <name_mask.h>
+
+ /*
+  * Test library.
+  */
+#include <addrinfo_to_string.h>
+
+#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 (file)
index 0000000..758cc2d
--- /dev/null
@@ -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 <addrinfo_to_string.h>
+/* DESCRIPTION
+/* .nf
+
+ /*
+  * System library.
+  */
+#include <sys_defs.h>
+#include <wrap_netdb.h>
+
+ /*
+  * Utility library.
+  */
+#include <vstring.h>
+
+ /*
+  * 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 (file)
index 0000000..8b6fcfe
--- /dev/null
@@ -0,0 +1,208 @@
+/*++
+/* NAME
+/*     make_addr 3
+/* SUMMARY
+/*     make_addrinfo(), freeaddrinfo(), make_sockaddr() for hermetic tests
+/* SYNOPSIS
+/*     #include <make_addrinfo.h>
+/*
+/*     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 <sys_defs.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <wrap_netdb.h>
+#include <string.h>
+#include <errno.h>
+
+ /*
+  * Utility library.
+  */
+#include <msg.h>
+#include <mymalloc.h>
+
+ /*
+  * Test library.
+  */
+#include <make_addr.h>
+
+#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 (file)
index 0000000..ebe4a6d
--- /dev/null
@@ -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 <mock_getaddrinfo.h>
+/* DESCRIPTION
+/* .nf
+
+ /*
+  * System library.
+  */
+#include <sys_defs.h>
+#include <wrap_netdb.h>
+
+ /*
+  * 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 (file)
index 0000000..bce07b7
--- /dev/null
@@ -0,0 +1,142 @@
+/*++
+/* NAME
+/*     match_addr 3
+/* SUMMARY
+/*     matchers for network address information
+/* SYNOPSIS
+/*     #include <match_addr.h>
+/*
+/*     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 <sys_defs.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netdb.h>
+#include <string.h>
+
+ /*
+  * Test library.
+  */
+#include <ptest.h>
+#include <match_basic.h>
+#include <match_addr.h>
+#include <addrinfo_to_string.h>
+
+#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 (file)
index 0000000..5d2e82c
--- /dev/null
@@ -0,0 +1,55 @@
+#ifndef _MATCH_ADDR_H_INCLUDED_
+#define _MATCH_ADDR_H_INCLUDED_
+
+/*++
+/* NAME
+/*     match_addr 3h
+/* SUMMARY
+/*     network address matcher
+/* SYNOPSIS
+/*     #include <match_addr.h>
+/* DESCRIPTION
+/* .nf
+
+ /*
+  * System library.
+  */
+#include <sys_defs.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <wrap_netdb.h>
+
+ /*
+  * Test library.
+  */
+#include <ptest.h>
+
+ /*
+  * 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 (file)
index 0000000..c577468
--- /dev/null
@@ -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 <sys_defs.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <wrap_netdb.h>
+#include <string.h>
+
+ /*
+  * Test library.
+  */
+#include <make_addr.h>
+#include <match_addr.h>
+#include <ptest.h>
+
+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 <ptest_main.h>
diff --git a/postfix/src/testing/match_basic.c b/postfix/src/testing/match_basic.c
new file mode 100644 (file)
index 0000000..8fcc2cb
--- /dev/null
@@ -0,0 +1,213 @@
+/*++
+/* NAME
+/*     match_basic 3
+/* SUMMARY
+/*     basic matchers
+/* SYNOPSIS
+/*     #include <match_basic.h>
+/*
+/*     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 <sys_defs.h>
+#include <string.h>
+
+ /*
+  * Utility library.
+  */
+#include <vstring.h>
+
+ /*
+  * Test library.
+  */
+#include <ptest.h>
+#include <match_basic.h>
+
+#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 (file)
index 0000000..5451e02
--- /dev/null
@@ -0,0 +1,82 @@
+#ifndef _MATCH_BASIC_H_INCLUDED_
+#define _MATCH_BASIC_H_INCLUDED_
+
+/*++
+/* NAME
+/*     match_basic 3h
+/* SUMMARY
+/*     basic matchers
+/* SYNOPSIS
+/*     #include <matchers.h>
+/* DESCRIPTION
+/* .nf
+
+ /*
+  * System library.
+  */
+#include <sys_defs.h>
+#include <setjmp.h>
+
+ /*
+  * Test library.
+  */
+#include <ptest.h>
+
+ /*
+  * 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 (file)
index 0000000..674c402
--- /dev/null
@@ -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 <mock_dns.h>
+/* DESCRIPTION
+/* .nf
+
+ /*
+  * DNS library.
+  */
+#include <dns.h>
+
+ /*
+  * Test library.
+  */
+#include <ptest.h>
+
+ /*
+  * 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 (file)
index 0000000..0c010bd
--- /dev/null
@@ -0,0 +1,482 @@
+/*++
+/* NAME
+/*     mock_dns_lookup 3
+/* SUMMARY
+/*     dns_lookup mock for hermetic tests
+/* SYNOPSIS
+/*     #include <mock_dns_lookup.h>
+/*
+/*     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 <sys_defs.h>
+
+ /*
+  * Utility library.
+  */
+#include <mymalloc.h>
+#include <msg.h>
+#include <vstring.h>
+#include <stringops.h>
+#include <hex_code.h>
+#include <name_code.h>
+
+ /*
+  * DNS library.
+  */
+#include <dns.h>
+
+ /*
+  * Test library.
+  */
+#include <mock_dns.h>
+#include <pmock_expect.h>
+#include <ptest.h>
+
+ /*
+  * 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 (file)
index 0000000..bdc0a99
--- /dev/null
@@ -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 <sys_defs.h>
+#include <arpa/inet.h>
+
+ /*
+  * Utility library.
+  */
+#include <msg.h>
+#include <vstring.h>
+
+ /*
+  * Test library.
+  */
+#include <mock_dns.h>
+#include <pmock_expect.h>
+#include <ptest.h>
+
+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 <ptest_main.h>
diff --git a/postfix/src/testing/mock_getaddrinfo.c b/postfix/src/testing/mock_getaddrinfo.c
new file mode 100644 (file)
index 0000000..41fc941
--- /dev/null
@@ -0,0 +1,487 @@
+/*++
+/* NAME
+/*     mock_getaddrinfo 3
+/* SUMMARY
+/*     mock getaddrinfo/getnameinfo for hermetic tests
+/* SYNOPSIS
+/*     #include <mock_getaddrinfo.h>
+/*
+/*     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 <sys_defs.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <wrap_netdb.h>
+#include <string.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <stdio.h>                     /* sprintf/snprintf */
+
+ /*
+  * Utility library.
+  */
+#include <msg.h>
+#include <myaddrinfo.h>
+#include <mymalloc.h>
+
+ /*
+  * Test library.
+  */
+#include <mock_getaddrinfo.h>
+#include <pmock_expect.h>
+#include <ptest.h>
+
+#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 (file)
index 0000000..b1333a4
--- /dev/null
@@ -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 <mock_getaddrinfo.h>
+/* DESCRIPTION
+/* .nf
+
+ /*
+  * System library.
+  */
+#include <sys_defs.h>
+#include <wrap_netdb.h>
+
+ /*
+  * Utility library.
+  */
+#include <myaddrinfo.h>                        /* MAI_HOSTNAME_STR, etc. */
+
+ /*
+  * Test library.
+  */
+#include <addrinfo_to_string.h>
+#include <make_addr.h>
+#include <match_addr.h>
+#include <match_basic.h>
+#include <ptest.h>
+
+ /*
+  * 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 (file)
index 0000000..23c1b56
--- /dev/null
@@ -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 <sys_defs.h>
+#include <wrap_netdb.h>
+
+ /*
+  * Utility library.
+  */
+#include <vstring.h>
+
+ /*
+  * Test library.
+  */
+#include <mock_getaddrinfo.h>
+#include <pmock_expect.h>
+#include <ptest.h>
+
+#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 <ptest_main.h>
diff --git a/postfix/src/testing/mock_myaddrinfo.c b/postfix/src/testing/mock_myaddrinfo.c
new file mode 100644 (file)
index 0000000..aeae3cd
--- /dev/null
@@ -0,0 +1,776 @@
+/*++
+/* NAME
+/*     mock_myaddrinfo 3
+/* SUMMARY
+/*     myaddrinfo mock for hermetic tests
+/* SYNOPSIS
+/*     #include <mock_myaddrinfo.h>
+/*
+/*     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 <sys_defs.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <string.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <stdio.h>                     /* sprintf */
+
+ /*
+  * Utility library.
+  */
+#include <mymalloc.h>
+#include <msg.h>
+#include <myaddrinfo.h>
+
+ /*
+  * Test library.
+  */
+#include <mock_myaddrinfo.h>
+#include <pmock_expect.h>
+#include <make_addr.h>
+#include <ptest.h>
+
+#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 (file)
index 0000000..3d4311d
--- /dev/null
@@ -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 <mock_myaddrinfo.h>
+/* DESCRIPTION
+/* .nf
+
+ /*
+  * Utility library.
+  */
+#include <myaddrinfo.h>
+
+ /*
+  * Test library.
+  */
+#include <addrinfo_to_string.h>
+#include <make_addr.h>
+#include <match_addr.h>
+#include <match_basic.h>
+#include <ptest.h>
+
+ /*
+  * 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 (file)
index 0000000..4d41310
--- /dev/null
@@ -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 <sys_defs.h>
+
+ /*
+  * Utility library.
+  */
+#include <msg.h>
+#include <vstring.h>
+
+ /*
+  * Test library.
+  */
+#include <mock_myaddrinfo.h>
+#include <pmock_expect.h>
+#include <ptest.h>
+
+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 <ptest_main.h>
diff --git a/postfix/src/testing/mock_servent.c b/postfix/src/testing/mock_servent.c
new file mode 100644 (file)
index 0000000..599c241
--- /dev/null
@@ -0,0 +1,474 @@
+/*++
+/* NAME
+/*     mock_servent 3
+/* SUMMARY
+/*     getservbyname mock for hermetic tests
+/* SYNOPSIS
+/*     #include <mock_servent.h>
+/*
+/*     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 <sys_defs.h>
+#include <wrap_netdb.h>
+#include <string.h>
+
+ /*
+  * Utility library.
+  */
+#include <mymalloc.h>
+#include <msg.h>
+
+ /*
+  * Test library.
+  */
+#include <mock_servent.h>
+#include <pmock_expect.h>
+
+ /*
+  * 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 (file)
index 0000000..04b99db
--- /dev/null
@@ -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 <mock_servent.h>
+/* DESCRIPTION
+/* .nf
+
+ /*
+  * System library.
+  */
+#include <sys_defs.h>
+#include <wrap_netdb.h>
+
+ /*
+  * Test library.
+  */
+#include <ptest.h>
+
+ /*
+  * 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 (file)
index 0000000..29ffde8
--- /dev/null
@@ -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 <sys_defs.h>
+#include <wrap_netdb.h>
+
+ /*
+  * Utility library.
+  */
+#include <msg.h>
+
+ /*
+  * Test library.
+  */
+#include <mock_servent.h>
+#include <pmock_expect.h>
+#include <ptest.h>
+
+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 <ptest_main.h>
index 948afab9f56a7f8428f23660c2bf943a8f78d825..f06817d549a7d6ab159058980f7ac9357552caf1 100644 (file)
@@ -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
index 8e7aab6b0fe1c1db6c606929c78154a6010e02ee..223d91da188b9e08d35c49ca05c5cfdfddcd18b1 100644 (file)
@@ -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
index c72aa812827954ae9fb71f158c726c4fd26ec084..8bb00459de9b70ebf2782e5d1eea83e60651204e 100644 (file)
@@ -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
index cdc671cffb267f761f811ac5b90fedcf66d2be62..f234f40ce66c2f53b8349a3a6407ea9f317ea247 100644 (file)
@@ -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.in | od -cb >unescape.tmp
-       diff -b unescape.ref unescape.tmp
-#      $(SHLIB_ENV) ${VALGRIND} ./unescape <unescape.in | $(SHLIB_ENV) ./unescape -e >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.c | od -cb >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.in >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
index 4e05fd0b4b3ae9dcd1caa83efdf1bfb6215589f0..8c77a8041bc621b0463c4a766e6885a5fa0cc7d3 100644 (file)
@@ -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 <setjmp.h>
-
- /*
-  * Utility library.
-  */
-#include <msg_vstream.h>
-#include <stringops.h>
-
-#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 (file)
index 0000000..d604962
--- /dev/null
@@ -0,0 +1,316 @@
+ /*
+  * Test program to exercise argv.c. See ptest_main.h for a documented
+  * example.
+  */
+
+ /*
+  * System library.
+  */
+#include <sys_defs.h>
+#include <string.h>
+
+ /*
+  * Utility library.
+  */
+#include <argv.h>
+
+ /*
+  * Test library.
+  */
+#include <ptest.h>
+
+#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 <ptest_main.h>
index 217deb7a6287fd8670dbd2cbfec4b3473a8e50c0..15fa074fd30d8ba5f9b22ef1fa53c57ad66e05f4 100644 (file)
@@ -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
similarity index 99%
rename from postfix/src/util/dict_test.c
rename to postfix/src/util/dict_cli.c
index ead61b20c068a11d12f53f891d8cdafa596abc72..1c800c28a62746d9103bdb4ee81b4a2005ddd54a 100644 (file)
@@ -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);
index c3b90d497f9e8fe1e14b1d0174b4ee2b2e557778..6e2e58ba02a9271d69742201749468bfda1ea6c0 100644 (file)
@@ -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 (file)
index 0000000..4a566ef
--- /dev/null
@@ -0,0 +1,106 @@
+ /*
+  * Test program to exercise dict_pipe.c. See ptest_main.h for a documented
+  * example.
+  */
+
+ /*
+  * System library.
+  */
+#include <sys_defs.h>
+#include <string.h>
+
+ /*
+  * Utility library.
+  */
+#include <msg.h>
+#include <mymalloc.h>
+#include <stringops.h>
+#include <dict_pipe.h>
+
+ /*
+  * Test library.
+  */
+#include <ptest.h>
+
+ /*
+  * 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 <ptest_main.h>
diff --git a/postfix/src/util/dict_pipe_test.in b/postfix/src/util/dict_pipe_test.in
deleted file mode 100644 (file)
index 9626dcd..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-${VALGRIND} ./dict_open 'pipemap:{inline:{k1=v1,k2=v2},inline:{v2=v3}}' read <<EOF
-get k0
-get k1
-get k2
-EOF
-${VALGRIND} ./dict_open 'pipemap:{inline:{k1=v1},fail:fail}' read <<EOF
-get k0
-get k1
-EOF
diff --git a/postfix/src/util/dict_pipe_test.ref b/postfix/src/util/dict_pipe_test.ref
deleted file mode 100644 (file)
index ecb865a..0000000
+++ /dev/null
@@ -1,14 +0,0 @@
-+ ./dict_open pipemap:{inline:{k1=v1,k2=v2},inline:{v2=v3}} read
-owner=trusted (uid=2147483647)
-> 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
index e28ad71fd4a08827d524be7ced18d6da69a788a3..90ba036172ccf266a55973763b04c7a453050a2a 100644 (file)
@@ -145,130 +145,3 @@ VSTREAM *dict_stream_open(const char *dict_type, const char *mapname,
        return (map_fp);
     }
 }
-
-#ifdef TEST
-
-#include <string.h>
-
-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 (file)
index 87c30e5..0000000
+++ /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 (file)
index 0000000..47332b4
--- /dev/null
@@ -0,0 +1,114 @@
+ /*
+  * Test program to exercise dict_stream.c. See ptest_main.h for a documented
+  * example.
+  */
+
+ /*
+  * System library.
+  */
+#include <sys_defs.h>
+#include <string.h>
+
+ /*
+  * Utility library.
+  */
+#include <vstream.h>
+#include <vstring.h>
+#include <dict.h>
+
+ /*
+  * Test library.
+  */
+#include <ptest.h>
+
+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 <ptest_main.h>
diff --git a/postfix/src/util/dict_union_test.c b/postfix/src/util/dict_union_test.c
new file mode 100644 (file)
index 0000000..845dc9b
--- /dev/null
@@ -0,0 +1,104 @@
+ /*
+  * Test program to exercise dict_union.c. See ptest_main.h for a documented
+  * example.
+  */
+
+ /*
+  * System library.
+  */
+#include <sys_defs.h>
+#include <string.h>
+
+ /*
+  * Utility library.
+  */
+#include <msg.h>
+#include <mymalloc.h>
+#include <stringops.h>
+#include <dict_union.h>
+
+ /*
+  * Test library.
+  */
+#include <ptest.h>
+
+ /*
+  * 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 <ptest_main.h>
diff --git a/postfix/src/util/dict_union_test.in b/postfix/src/util/dict_union_test.in
deleted file mode 100644 (file)
index 9d111d4..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-${VALGRIND} ./dict_open 'unionmap:{static:one,static:two,inline:{foo=three}}' read <<EOF
-get foo
-get bar
-EOF
-${VALGRIND} ./dict_open 'unionmap:{static:one,fail:fail}' read <<EOF
-get foo
-EOF
diff --git a/postfix/src/util/dict_union_test.ref b/postfix/src/util/dict_union_test.ref
deleted file mode 100644 (file)
index b609410..0000000
+++ /dev/null
@@ -1,10 +0,0 @@
-+ ./dict_open unionmap:{static:one,static:two,inline:{foo=three}} read
-owner=trusted (uid=2147483647)
-> 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 (file)
index 0000000..29948c1
--- /dev/null
@@ -0,0 +1,68 @@
+/*++
+/* NAME
+/*     find_inet_service 3
+/* SUMMARY
+/*     TCP service lookup
+/* SYNOPSIS
+/*     #include <find_inet_service.h>
+/*
+/*     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 <sys_defs.h>
+#include <wrap_netdb.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+/* Application-specific. */
+
+#include <find_inet_service.h>
+#include <known_tcp_ports.h>
+#include <msg.h>
+#include <sane_strtol.h>
+#include <stringops.h>
+
+/* 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 (file)
index 0000000..0f11697
--- /dev/null
@@ -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 <find_inet_service.h>
+/* 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 (file)
index 0000000..11b60b3
--- /dev/null
@@ -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 <sys_defs.h>
+
+ /*
+  * Utility library
+  */
+#include <find_inet_service.h>
+#include <known_tcp_ports.h>
+#include <msg.h>
+
+ /*
+  * Test library.
+  */
+#include <mock_servent.h>
+#include <ptest.h>
+
+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 <ptest_main.h>
index b4d7b304b7ea756d321233d8b0795ea6f2f482cc..6a728325549d9e585ebc892a7f6fb5da4c09fdc4 100644 (file)
@@ -137,101 +137,3 @@ HASH_FNV_T hash_fnvz(const char *src)
     }
     return (hash);
 }
-
-#ifdef TEST
-#include <stdlib.h>
-#include <string.h>
-#include <msg.h>
-
-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 (file)
index 0000000..6255a1d
--- /dev/null
@@ -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 <sys_defs.h>
+#include <stdlib.h>
+#include <string.h>
+
+ /*
+  * Utility library.
+  */
+#include <msg.h>
+#include <hash_fnv.h>
+
+ /*
+  * Test library.
+  */
+#include <ptest.h>
+
+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 <ptest_main.h>
index e579b1760c148db5dcf8ca5b12f9250cd3ae0f8d..1162c77f3c69038175148ec961ce992eb2da7a2d 100644 (file)
@@ -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)
index 1d524d4d37349c8ffd2ccf2f7bbd7b911df90e1e..3ddc084fd14a3f6efb78ebc08a9bf3657c1ab08a 100644 (file)
@@ -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 <msg.h>
-
-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 (file)
index adcf182..0000000
+++ /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 (file)
index 0000000..bdd1084
--- /dev/null
@@ -0,0 +1,116 @@
+ /*
+  * Test program to exercise known_tcp_ports.c. See ptest_main.h for a
+  * documented example.
+  */
+
+ /*
+  * System library.
+  */
+#include <sys_defs.h>
+#include <string.h>
+
+ /*
+  * Utility library
+  */
+#include <known_tcp_ports.h>
+#include <msg.h>
+
+ /*
+  * Test library.
+  */
+#include <ptest.h>
+
+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 <ptest_main.h>
index 70c6eab083635934af52671e78abdab9ae3bee73..efefd989378655ccc4572eef8aa491cdb12e0052 100644 (file)
 /*     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
 /*     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
 /*     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. */
 #include <stdlib.h>
 #include <stdarg.h>
 #include <unistd.h>
+#include <signal.h>
 
 /* Application-specific. */
 
   */
 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. */
index 6c75bafc1147dcf3def8135d44b87c976a94ef8e..1888706438cc37d1d59ceb0bf76dd75809a481d4 100644 (file)
@@ -16,6 +16,7 @@
  */
 #include <stdarg.h>
 #include <time.h>
+#include <setjmp.h>
 
 /*
  * 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
index 6663877d7367170b24a1412aca75299ef3b53b8f..2ba897e8d8675a0b5c1d58e03a2e58c83469db68 100644 (file)
@@ -6,10 +6,15 @@
 /* SYNOPSIS
 /*     #include <msg_output.h>
 /*
-/*     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;
 /*     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
 #include <vstream.h>
 #include <msg_vstream.h>
 #include <stringops.h>
+#include <msg.h>
 #include <msg_output.h>
 
  /*
@@ -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;
+    }
+}
index bd84276f6fd8065cdb24a7e7f2a0be9b27c05cf7..5beb2c75b633ca49d33bff782227fc76c637ec55 100644 (file)
@@ -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 (file)
index 0000000..48c6402
--- /dev/null
@@ -0,0 +1,142 @@
+ /*
+  * Test program to exercise the msg_output module. See comments in
+  * ptest_main.h for documented examples.
+  */
+
+ /*
+  * System library.
+  */
+#include <sys_defs.h>
+#include <string.h>
+
+ /*
+  * Utility library.
+  */
+#include <argv.h>
+#include <msg.h>
+#include <msg_output.h>
+#include <mymalloc.h>
+#include <vstring.h>
+
+ /*
+  * Test library.
+  */
+#include <ptest.h>
+#include <match_basic.h>
+
+ /*
+  * 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 <ptest_main.h>
index b6e24e60902ca8bd80147e23815b2a16dcdf407a..759e1953be434f35037586909ed88a5f1586a891 100644 (file)
@@ -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;
 }
index d0679a047cb1427295f229f4217eeb4fe35d1f9c..02ec67ccb205125d6a5184137a5490bd787a8c26 100644 (file)
@@ -19,6 +19,7 @@
   * External interface.
   */
 extern void msg_vstream_init(const char *, VSTREAM *);
+extern void msg_vstream_enable(int);
 
 /* LICENSE
 /* .ad
index 5edafde8d11d5c803a493a588b173d95a4261367..7bf2133250131ceda05c0e59e9cda2994e990493 100644 (file)
 #include <sys/socket.h>
 #include <netinet/in.h>
 #include <arpa/inet.h>
-#include <netdb.h>
+#include <wrap_netdb.h>
 #include <string.h>
 #include <errno.h>
 #include <stdlib.h>
index 94f1e9fa68fe79b82cb510a77200563f67efc569..702cb1a74b8cecf877774b73bfac331795b2013e 100644 (file)
@@ -17,7 +17,7 @@
 #include <sys/types.h>
 #include <sys/socket.h>
 #include <netinet/in.h>
-#include <netdb.h>
+#include <wrap_netdb.h>
 #include <string.h>
 #include <errno.h>                     /* MAI_STRERROR() */
 #include <limits.h>                    /* CHAR_BIT */
@@ -42,7 +42,7 @@
 #define sockaddr_storage mai_sockaddr_storage
 
  /*
-  * Modern systems define this in <netdb.h>.
+  * Modern systems define this in <wrap_netdb.h>.
   */
 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 (file)
index 7dccdb0..0000000
+++ /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 (file)
index f2305dc..0000000
+++ /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 (file)
index 33c1284..0000000
+++ /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 (file)
index f73560b..0000000
+++ /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 (file)
index 0000000..4a8cf0f
--- /dev/null
@@ -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 <sys_defs.h>
+#include <wrap_netdb.h>
+
+ /*
+  * Utility library.
+  */
+#include <vstring.h>
+#include <inet_proto.h>
+
+ /*
+  * Test library.
+  */
+#include <ptest.h>
+#include <mock_getaddrinfo.h>
+
+#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 <ptest_main.h>
index 94f7bb3e7aa733e31e0290e67022740ca46222bd..37a8afd2a8d0f676a001ef43a78dac9b9dbcac90 100644 (file)
@@ -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 (file)
index 0000000..fc3c8c0
--- /dev/null
@@ -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 <sys_defs.h>
+#include <string.h>
+
+ /*
+  * Utility library.
+  */
+#include <msg.h>
+#include <mymalloc.h>
+
+ /*
+  * Test library.
+  */
+#include <ptest.h>
+
+ /*
+  * See <ptest_main.h>
+  */
+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 <ptest_main.h>
index 85b15f3b3f1f433ad5b4a5cdfe1e2a1aef1da05d..95cdc97414aa0b5334c2573052b1e928f4ee550b 100644 (file)
@@ -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 (file)
index 4f920f9..0000000
+++ /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 (file)
index 0000000..9cc50a4
--- /dev/null
@@ -0,0 +1,108 @@
+ /*
+  * Test program to exercise mystrtok.c. See ptest_main.h for a documented
+  * example.
+  */
+
+ /*
+  * System library.
+  */
+#include <sys_defs.h>
+#include <string.h>
+
+ /*
+  * Utility library.
+  */
+#include <msg.h>
+#include <mymalloc.h>
+#include <stringops.h>
+
+ /*
+  * Test library.
+  */
+#include <ptest.h>
+
+ /*
+  * 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 <ptest_main.h>
index 284d4fa9d308c166f08e47e69d820fd16473affb..764e3c4ae7cc48d773b0cd21db73bdb4eceb452f 100644 (file)
 /*     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";
index 05e45ec38ba93fb7830a19b7d3931a56ee7ca468..492470506fbab02c69b838a8d515320cc5f746c8 100644 (file)
@@ -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/unescape.in b/postfix/src/util/unescape.in
deleted file mode 100644 (file)
index 41f24a7..0000000
+++ /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:<wietse@\317\200.porcupine.org>
diff --git a/postfix/src/util/unescape.ref b/postfix/src/util/unescape.ref
deleted file mode 100644 (file)
index db16fa8..0000000
+++ /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 (file)
index 0000000..9a22749
--- /dev/null
@@ -0,0 +1,94 @@
+ /*
+  * Test program to exercise unescape.c. See ptest_main.h for a documented
+  * example.
+  */
+
+ /*
+  * System library.
+  */
+#include <sys_defs.h>
+#include <string.h>
+
+ /*
+  * Utility library
+  */
+#include <stringops.h>
+#include <msg.h>
+
+ /*
+  * Test library.
+  */
+#include <ptest.h>
+
+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:<wietse@\\317\\200.porcupine.org>",
+        /* want */ "rcpt to:<wietse@\317\200.porcupine.org>",
+    },
+};
+
+ /*
+  * Test library.
+  */
+#include <ptest_main.h>
diff --git a/postfix/src/util/wrap_netdb.c b/postfix/src/util/wrap_netdb.c
new file mode 100644 (file)
index 0000000..7d33f2f
--- /dev/null
@@ -0,0 +1,136 @@
+/*++
+/* NAME
+/*     wrap_netdb 3
+/* SUMMARY
+/*     mockable netdb wrappers
+/* SYNOPSIS
+/*     #include <wrap_netdb.h>
+/*
+/*     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 <sys_defs.h>
+#include <wrap_netdb.h>
+
+#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 (file)
index 0000000..3e49e7a
--- /dev/null
@@ -0,0 +1,57 @@
+#ifndef _WRAP_NETDB_H_INCLUDED_
+#define _WRAP_NETDB_H_INCLUDED_
+
+/*++
+/* NAME
+/*     wrap_netdb 3h
+/* SUMMARY
+/*     mockable netdb wrappers
+/* SYNOPSIS
+/*     #include <wrap_netdb.h>
+/* DESCRIPTION
+/* .nf
+
+ /*
+  * System library
+  */
+#include <sys_defs.h>
+#include <netdb.h>
+
+ /*
+  * 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
index ad483021b19110201cc42fc4b4f3fcf364d2e78c..f0dac8fa50633e18d3fd212c200e518554ff0433 100644 (file)
@@ -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