/* ** OSSP sa - Socket Abstraction ** Copyright (c) 2001-2006 Ralf S. Engelschall ** Copyright (c) 2001-2006 The OSSP Project ** Copyright (c) 2001-2005 Cable & Wireless ** ** This file is part of OSSP sa, a socket abstraction library which ** can be found at http://www.ossp.org/pkg/lib/sa/. ** ** Permission to use, copy, modify, and distribute this software for ** any purpose with or without fee is hereby granted, provided that ** the above copyright notice and this permission notice appear in all ** copies. ** ** THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED ** WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF ** MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. ** IN NO EVENT SHALL THE AUTHORS AND COPYRIGHT HOLDERS AND THEIR ** CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF ** USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ** ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, ** OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT ** OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF ** SUCH DAMAGE. ** ** sa_test.c: socket abstraction library test suite */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include #include #include #include #include "ts.h" #include "sa.h" /* test: address import/export */ TS_TEST(test_saa_impexp) { sa_addr_t *saa; sa_rc_t rv; char *cp; int i; struct { char *in; sa_rc_t rv; char *out; char *out_alt; } table[] = { /* positive tests */ { "inet://0.0.0.0:0", SA_OK, "inet://0.0.0.0:0", NULL }, { "inet://127.0.0.1:514", SA_OK, "inet://127.0.0.1:514", NULL }, { "inet://localhost:syslog#udp", SA_OK, "inet://127.0.0.1:514", "inet://[::1]:514" }, { "inet://localhost:smtp", SA_OK, "inet://127.0.0.1:25", "inet://[::1]:25" }, { "unix:/tmp/socket", SA_OK, "unix:/tmp/socket", NULL }, /* negative tests */ { "inet:", SA_ERR_ARG, NULL, NULL }, { "inet://1.2.3.4.5:0", SA_ERR_ARG, NULL, NULL }, { "inet://just-hostname", SA_ERR_ARG, NULL, NULL }, { "unix:", SA_ERR_ARG, NULL, NULL } }; ts_test_check(TS_CTX, "sa_addr_create"); if ((rv = sa_addr_create(&saa)) != SA_OK) ts_test_fail(TS_CTX, "sa_addr_create -> %d[%s] (expected %d[%s])", rv, sa_error(rv), SA_OK, sa_error(SA_OK)); for (i = 0; i < (int)(sizeof(table)/sizeof(table[0])); i++) { ts_test_check(TS_CTX, "sa_addr_u2a(\"%s\")", table[i].in); if ((rv = sa_addr_u2a(saa, table[i].in)) != table[i].rv) ts_test_fail(TS_CTX, "sa_addr_a2u -> %d[%s] (expected %d[%s])", rv, sa_error(rv), table[i].rv, sa_error(table[i].rv)); ts_test_check(TS_CTX, "sa_addr_a2u"); if ((rv = sa_addr_a2u(saa, &cp)) != SA_OK) { ts_test_fail(TS_CTX, "sa_addr_u2a -> %d[%s] (expected %d[%s])", rv, sa_error(rv), SA_OK, sa_error(SA_OK)); continue; } if (table[i].rv == SA_OK) { if (table[i].out_alt != NULL) { if (strcmp(cp, table[i].out) != 0 && strcmp(cp, table[i].out_alt) != 0) ts_test_fail(TS_CTX, "sa_addr_a2u -> \"%s\" (expected \"%s\" or \"%s\")", cp, table[i].out, table[i].out_alt); } else { if (strcmp(cp, table[i].out) != 0) ts_test_fail(TS_CTX, "sa_addr_a2u -> \"%s\" (expected \"%s\")", cp, table[i].out); } } free(cp); } ts_test_check(TS_CTX, "sa_addr_destroy"); if ((rv = sa_addr_destroy(saa)) != SA_OK) ts_test_fail(TS_CTX, "sa_addr_destroy -> %d[%s] (expected %d[%s])", rv, sa_error(rv), SA_OK, sa_error(SA_OK)); } /* test: address matching */ TS_TEST(test_saa_match) { sa_addr_t *saa1; sa_addr_t *saa2; sa_rc_t rv; int i; struct { char *in; char *acl; int prefixlen; sa_rc_t rv; } table[] = { { "unix:/foo/bar", "unix:/foo/bar", -1, SA_OK }, { "unix:/foo/bar", "unix:/foo/bar", 0, SA_OK }, { "unix:/foo/bar", "unix:/foo", 4, SA_OK }, { "unix:/foo/bar", "unix:/foo/quux", 4, SA_OK }, { "unix:/foo/bar", "unix:/baz/quux", -1, SA_ERR_MTC }, { "inet://0.0.0.0:0", "inet://0.0.0.0:0", 0, SA_OK }, { "inet://127.0.0.1:514", "inet://127.0.0.1:514", -1, SA_OK }, { "inet://127.0.0.1:514", "inet://0.0.0.0:0", 0, SA_OK }, { "inet://127.0.0.1:514", "inet://12.34.56.78:9", 0, SA_OK }, { "inet://127.0.0.1:514", "inet://12.34.56.78:9", -1, SA_ERR_MTC }, { "inet://127.0.0.1:514", "inet://127.0.0.0:0", 24, SA_OK }, { "inet://127.0.0.1:514", "inet://127.0.0.0:0", 32, SA_ERR_MTC }, { "inet://141.1.23.20:25", "inet://141.1.23.40:25", 32, SA_ERR_MTC }, { "inet://141.1.23.20:25", "inet://141.1.23.40:25", 24, SA_OK } }; sa_addr_create(&saa1); sa_addr_create(&saa2); for (i = 0; i < (int)(sizeof(table)/sizeof(table[0])); i++) { if ((rv = sa_addr_u2a(saa1, table[i].in)) != SA_OK) continue; if ((rv = sa_addr_u2a(saa2, table[i].acl)) != SA_OK) continue; ts_test_check(TS_CTX, "sa_addr_match(\"%s\", \"%s\", %d)", table[i].in, table[i].acl, table[i].prefixlen); if ((rv = sa_addr_match(saa1, saa2, table[i].prefixlen)) != table[i].rv) ts_test_fail(TS_CTX, "sa_addr_match -> %d[%s] (expected %d[%s])", rv, sa_error(rv), table[i].rv, sa_error(table[i].rv)); } sa_addr_destroy(saa1); sa_addr_destroy(saa2); } #define ex(proc, cmd) \ do { \ sa_rc_t __rv; \ ts_test_check(TS_CTX, "%s: %s", #proc, #cmd); \ if ((__rv = (cmd)) != SA_OK) { \ if (__rv == SA_ERR_SYS) \ ts_test_fail(TS_CTX, "%s -> error: %s (rc=%d) (system: %s)", \ #cmd, sa_error(__rv), __rv, strerror(errno)); \ else \ ts_test_fail(TS_CTX, "%s -> error: %s (rc=%d)", \ #cmd, sa_error(__rv), __rv); \ } \ } while (0) /* test: client/server stream communication */ TS_TEST(test_sa_stream) { sa_t *sa_clt; sa_t *sa_srv; sa_addr_t *saa_srv; sa_addr_t *saa_clt; pid_t pid_srv; sa_t *sa_cld; sa_addr_t *saa_cld; char buf_srv[1024]; char buf_clt[1024]; size_t l; #define MSG_TCP_SRV_WELCOME "Welcome client\n" #define MSG_TCP_CLT_WELCOME "Welcome server\n" #define MSG_TCP_SRV_GOODBYE "Goodbye client\n" #define MSG_TCP_CLT_GOODBYE "Goodbye server\n" if ((pid_srv = fork()) == 0) { /* ** SERVER */ /* create server socket */ ex(SRV, sa_create(&sa_srv)); ex(SRV, sa_option(sa_srv, SA_OPTION_REUSEADDR, 1)); ex(SRV, sa_option(sa_srv, SA_OPTION_REUSEPORT, 1)); ex(SRV, sa_timeout(sa_srv, SA_TIMEOUT_ALL, 10, 0)); /* bind socket to local port */ ex(SRV, sa_addr_create(&saa_srv)); ex(SRV, sa_addr_u2a(saa_srv, "inet://127.0.0.1:12345#tcp")); ex(SRV, sa_bind(sa_srv, saa_srv)); ex(SRV, sa_addr_destroy(saa_srv)); /* receive client connection */ ex(SRV, sa_listen(sa_srv, 10)); ex(SRV, sa_accept(sa_srv, &saa_cld, &sa_cld)); /* communicate with client */ ex(SRV, sa_writef(sa_cld, "%s", MSG_TCP_SRV_WELCOME)); ex(SRV, sa_readln(sa_cld, buf_srv, sizeof(buf_srv), &l)); if (strcmp(buf_srv, MSG_TCP_CLT_WELCOME) != 0) ts_test_fail(TS_CTX, "server: got \"%s\", expected \"%s\"", buf_srv, MSG_TCP_CLT_WELCOME); ex(SRV, sa_writef(sa_cld, "%s", MSG_TCP_SRV_GOODBYE)); ex(SRV, sa_shutdown(sa_cld, "w")); ex(SRV, sa_readln(sa_cld, buf_srv, sizeof(buf_srv), &l)); if (strcmp(buf_srv, MSG_TCP_CLT_GOODBYE) != 0) ts_test_fail(TS_CTX, "server: got \"%s\", expected \"%s\"", buf_srv, MSG_TCP_CLT_GOODBYE); ex(SRV, sa_shutdown(sa_cld, "r")); /* destroy client connection */ ex(SRV, sa_destroy(sa_cld)); ex(SRV, sa_addr_destroy(saa_cld)); /* destroy server socket and die */ ex(SRV, sa_destroy(sa_srv)); exit(0); } else { /* ** CLIENT **/ sleep(2); /* create client socket */ ex(CLT, sa_create(&sa_clt)); ex(CLT, sa_timeout(sa_clt, SA_TIMEOUT_ALL, 10, 0)); /* connect to server */ ex(CLT, sa_addr_create(&saa_clt)); ex(CLT, sa_addr_u2a(saa_clt, "inet://127.0.0.1:12345#tcp")); ex(CLT, sa_connect(sa_clt, saa_clt)); ex(CLT, sa_addr_destroy(saa_clt)); /* communicate with server */ ex(CLT, sa_readln(sa_clt, buf_clt, sizeof(buf_clt), &l)); if (strcmp(buf_clt, MSG_TCP_SRV_WELCOME) != 0) ts_test_fail(TS_CTX, "client: got \"%s\", expected \"%s\"", buf_clt, MSG_TCP_SRV_WELCOME); ex(CLT, sa_writef(sa_clt, "%s", MSG_TCP_CLT_WELCOME)); ex(CLT, sa_readln(sa_clt, buf_clt, sizeof(buf_clt), &l)); if (strcmp(buf_clt, MSG_TCP_SRV_GOODBYE) != 0) ts_test_fail(TS_CTX, "client: got \"%s\", expected \"%s\"", buf_clt, MSG_TCP_SRV_GOODBYE); ex(CLT, sa_writef(sa_clt, "%s", MSG_TCP_CLT_GOODBYE)); ex(CLT, sa_shutdown(sa_clt, "rw")); /* destroy server connection and wait for server to exit */ ex(CLT, sa_destroy(sa_clt)); waitpid(pid_srv, NULL, 0); } } /* test: client/server datagram communication */ TS_TEST(test_sa_datagram) { sa_t *sa_clt; sa_t *sa_srv; sa_addr_t *saa_srv; sa_addr_t *saa_clt; pid_t pid_srv; char buf_srv[1024]; char buf_clt[1024]; size_t l; #define MSG_UDP_CLT_REQUEST "Who are you?\n" #define MSG_UDP_SRV_RESPONSE "I'm god\n" if ((pid_srv = fork()) == 0) { /* ** SERVER */ /* create server socket */ ex(SRV, sa_create(&sa_srv)); ex(SRV, sa_type(sa_srv, SA_TYPE_DATAGRAM)); ex(SRV, sa_option(sa_srv, SA_OPTION_REUSEADDR, 1)); ex(SRV, sa_option(sa_srv, SA_OPTION_REUSEPORT, 1)); ex(SRV, sa_timeout(sa_srv, SA_TIMEOUT_ALL, 10, 0)); /* bind socket to local port */ ex(SRV, sa_addr_create(&saa_srv)); ex(SRV, sa_addr_u2a(saa_srv, "inet://127.0.0.1:12345#udp")); ex(SRV, sa_bind(sa_srv, saa_srv)); ex(SRV, sa_addr_destroy(saa_srv)); /* communicate with client */ ex(SRV, sa_recv(sa_srv, &saa_clt, buf_srv, sizeof(buf_srv), &l)); if (strncmp(buf_srv, MSG_UDP_CLT_REQUEST, l) != 0) ts_test_fail(TS_CTX, "server: got \"%s\", expected \"%s\"", buf_srv, MSG_UDP_CLT_REQUEST); ex(SRV, sa_sendf(sa_srv, saa_clt, "%s", MSG_UDP_SRV_RESPONSE)); ex(SRV, sa_addr_destroy(saa_clt)); /* destroy server socket and die */ ex(SRV, sa_destroy(sa_srv)); exit(0); } else { /* ** CLIENT **/ sleep(2); /* create client socket */ ex(CLT, sa_create(&sa_clt)); ex(CLT, sa_type(sa_clt, SA_TYPE_DATAGRAM)); ex(CLT, sa_timeout(sa_clt, SA_TIMEOUT_ALL, 10, 0)); /* communicate with server */ ex(CLT, sa_addr_create(&saa_srv)); ex(CLT, sa_addr_u2a(saa_srv, "inet://127.0.0.1:12345#udp")); ex(CLT, sa_sendf(sa_clt, saa_srv, "%s", MSG_UDP_CLT_REQUEST)); ex(CLT, sa_addr_destroy(saa_srv)); ex(CLT, sa_recv(sa_clt, &saa_srv, buf_clt, sizeof(buf_clt), &l)); if (strncmp(buf_clt, MSG_UDP_SRV_RESPONSE, l) != 0) ts_test_fail(TS_CTX, "client: got \"%s\", expected \"%s\"", buf_clt, MSG_UDP_SRV_RESPONSE); ex(CLT, sa_addr_destroy(saa_srv)); /* destroy server connection and wait for server to exit */ ex(CLT, sa_destroy(sa_clt)); waitpid(pid_srv, NULL, 0); } } /* test: exception handling */ #ifdef WITH_EX #include "ex.h" TS_TEST(test_sa_ex) { sa_addr_t *saa; ex_t ex; int caught; ts_test_check(TS_CTX, "exception handling"); caught = 0; ex_try { sa_addr_create(&saa); sa_addr_u2a(saa, "inet:DUMMY"); sa_addr_destroy(saa); } ex_catch (ex) { if ((sa_rc_t)ex.ex_value != SA_ERR_ARG) ts_test_fail(TS_CTX, "unexpected exception: %d\n", (sa_rc_t)ex.ex_value); caught = 1; } if (!caught) ts_test_fail(TS_CTX, "expected exception not caught\n"); } #endif int main(int argc, char *argv[]) { ts_suite_t *ts; int n; ts = ts_suite_new("OSSP sa (Socket Abstraction)"); ts_suite_test(ts, test_saa_impexp, "socket address abstraction import/export"); ts_suite_test(ts, test_saa_match, "socket address abstraction matching"); ts_suite_test(ts, test_sa_stream, "socket abstraction stream communication"); ts_suite_test(ts, test_sa_datagram, "socket abstraction datagram communication"); #ifdef WITH_EX ts_suite_test(ts, test_sa_ex, "exception handling"); #endif n = ts_suite_run(ts); ts_suite_free(ts); return n; }