diff --git a/882/Makefile b/882/Makefile new file mode 100644 index 0000000..c7d4379 --- /dev/null +++ b/882/Makefile @@ -0,0 +1,28 @@ + +CFLAGS += -Wall + +ifneq ($(shell which strace 2>/dev/null),) +TRACE_CONNECT = strace -f -qq -r -e trace=connect +else ifneq ($(shell which ltrace 2>/dev/null),) +TRACE_CONNECT = ltrace -f -C -r -e connect +else +$(warning Could not find strace or ltrace; test targets will likely not produce helpful feedback.) +endif + +issue: issue.c + $(CC) $(CFLAGS) -lzmq -o $@ $^ + +clean: + rm -f issue + +no-fail: issue + $(TRACE_CONNECT) ./issue + +connect-fail: issue + $(TRACE_CONNECT) ./issue fail + +auth-fail: issue + $(TRACE_CONNECT) ./issue wrong + +PHONY: clean no-fail connect-fail auth-fail + diff --git a/882/README b/882/README new file mode 100644 index 0000000..6786899 --- /dev/null +++ b/882/README @@ -0,0 +1,46 @@ +Use the included GNU make Makefile to build on Unix-like systems. strace or +ltrace is required to trace the underlying calls to connect. Without some kind +of trace tool, it will be difficult to see the problem. + +With all the tests below, the connect calls are traced and are timestamped with +the time between calls. The ZAP request handler also prints the ZAP request and +reply. ZMQ_RECONNECT_IVL is set to 200, ZMQ_RECONNECT_IVL_MAX is 5000, and +ZMQ_SNDTIMEO is 30000. ZMQ_IMMEDIATE is also enabled. + + +To build the test: + + $ make + + +To show the behavior when connect and authentication succeed, make the no-fail +target: + + $ make no-fail + +The no-fail test issues a single connect, authentication succeeds, the bounce +test succeeds, and the process exits almost immediately. + + +To show the behavior when connect fails, make the connect-fail target: + + $ make connect-fail + +The connect-fail test issues a succession of connect calls to the wrong port, +with the time between each call roughly doubling from the previous call, +starting with the value of ZMQ_RECONNECT_IVL and plateauing at about +ZMQ_RECONNECT_IVL_MAX. The process will assert and exit after about 30 seconds, +when zmq_send() times out. This is the expected behavior. + + +Finally, to show the behavior when connect succeeds but authentication fails, +make the auth-fail: + + $ make auth-fail + +As with the connect-fail test, the auth-fail test issues a series of connect +calls, but the time between calls is roughly the value of ZMQ_RECONNECT_IVL, +never increasing toward ZMQ_RECONNECT_IVL_MAX. Also, zmq_send succeeds almost +immediately and is never allowed to time out due to an unsuccessful ZAP +handshake (issue #882). + diff --git a/882/issue.c b/882/issue.c new file mode 100644 index 0000000..8dd48ed --- /dev/null +++ b/882/issue.c @@ -0,0 +1,187 @@ + +#include +#include +#include +#include + +#include +#include + + +/* + * Receive a string from socket and return a copy (must be freed by caller) + * or NULL, if an error occurred. + */ +static char *s_tryrecv(void *socket) +{ + char buffer[256]; + int size; + + if ((size = zmq_recv(socket, buffer, sizeof(buffer), 0)) == -1) + return NULL; + if (size >= sizeof(buffer)) + size = sizeof(buffer) - 1; + buffer[size] = '\0'; + return strdup(buffer); +} + + +/* + * Receive a string from socket and return a copy (must be freed by caller). + */ +static char *s_recv(void *socket) +{ + char *str; + + assert((str = s_tryrecv(socket))); + return str; +} + + +/* + * Return non-zero if socket has more data, zero otherwise. + */ +static int has_more(void *socket) +{ + int more; + size_t size; + + size = sizeof(more); + assert(!zmq_getsockopt(socket, ZMQ_RCVMORE, &more, &size)); + return more; +} + + +/* + * Assure that socket has more data before receiving and returning a string. + */ +static char *s_recvmore(void *socket) +{ + assert(has_more(socket)); + return s_recv(socket); +} + + +/* + * Send a string (possibly the last in a multipart message). + */ +static void s_send(void *socket, const char *str) +{ + assert(zmq_send(socket, str, strlen(str), 0) != -1); +} + + +/* + * Send a string in a frame of a multipart message. + */ +static void s_sendmore(void *socket, const char *str) +{ + assert(zmq_send(socket, str, strlen(str), ZMQ_SNDMORE) != -1); +} + + +/* + * ZAP request handling loop. + */ +static void zap_handler(void *handler) +{ + for (;;) { + char *version, *sequence, *domain, *address, *identity, *mechanism; + + /* Receive request. */ + if(!(version = s_tryrecv(handler))) + break; + sequence = s_recvmore(handler); + domain = s_recvmore(handler); + address = s_recvmore(handler); + identity = s_recvmore(handler); + mechanism = s_recvmore(handler); + assert(!has_more(handler)); + + assert(!strcmp(version, "1.0")); + assert(!strcmp(mechanism, "NULL")); + + printf("ZAP REQUEST %s %s %s %s %s %s\n", version, sequence, domain, + address, identity, mechanism); + + /* Send reply. */ + s_sendmore(handler, version); + s_sendmore(handler, sequence); + if(!strcmp(domain, "test")) { + s_sendmore(handler, "200"); + s_sendmore(handler, "OK"); + s_sendmore(handler, "anonymous"); + printf("ZAP REPLY 1.0 %s 200 OK anonymous\n", sequence); + } else { + s_sendmore(handler, "500"); + s_sendmore(handler, "Denied"); + s_sendmore(handler, ""); + printf("ZAP REPLY 1.0 %s 200 Denied\n", sequence); + } + s_send(handler, ""); + + /* Cleanup. */ + free(version); + free(sequence); + free(domain); + free(address); + free(identity); + free(mechanism); + } + zmq_close(handler); +} + + + +int main(int argc, char *argv[]) +{ + void *ctx, *handler, *thread, *server, *client; + const char *domain, *connect_addr; + int optval; + + domain = argc > 1 ? argv[1] : "test"; + connect_addr = strcmp(domain, "fail") ? "tcp://127.0.0.1:9000" : "tcp://127.0.0.1:9001"; + + assert((ctx = zmq_ctx_new())); + + /* Start ZAP handler thread. */ + assert((handler = zmq_socket(ctx, ZMQ_REP))); + assert(!(zmq_bind(handler, "inproc://zeromq.zap.01"))); + assert((thread = zmq_threadstart(zap_handler, handler))); + + /* Bind server. */ + assert((server = zmq_socket(ctx, ZMQ_DEALER))); + assert(!(zmq_setsockopt(server, ZMQ_ZAP_DOMAIN, domain, strlen(domain)))); + assert(!zmq_bind(server, "tcp://127.0.0.1:9000")); + + /* Connect client. */ + assert((client = zmq_socket(ctx, ZMQ_DEALER))); + optval = 200; + assert(!(zmq_setsockopt(client, ZMQ_RECONNECT_IVL, &optval, sizeof(optval)))); + optval = 5000; + assert(!(zmq_setsockopt(client, ZMQ_RECONNECT_IVL_MAX, &optval, sizeof(optval)))); + optval = 30000; + assert(!(zmq_setsockopt(client, ZMQ_SNDTIMEO, &optval, sizeof(optval)))); + optval = 1; + assert(!(zmq_setsockopt(client, ZMQ_IMMEDIATE, &optval, sizeof(optval)))); + assert(!zmq_connect(client, connect_addr)); + + /* Bounce test. */ + s_send(client, "Hello, Server!"); + assert(!strcmp(s_recv(server), "Hello, Server!")); + s_send(server, "Hello, Client!"); + assert(!strcmp(s_recv(client), "Hello, Client!")); + + /* Cleanup. */ + assert(!zmq_disconnect(client, connect_addr)); + assert(!zmq_close(client)); + + assert(!zmq_unbind(server, "tcp://127.0.0.1:9000")); + assert(!zmq_close(server)); + + assert(!zmq_term(ctx)); + zmq_threadclose(thread); + + return 0; +} + diff --git a/883/Makefile b/883/Makefile new file mode 100644 index 0000000..c7d4379 --- /dev/null +++ b/883/Makefile @@ -0,0 +1,28 @@ + +CFLAGS += -Wall + +ifneq ($(shell which strace 2>/dev/null),) +TRACE_CONNECT = strace -f -qq -r -e trace=connect +else ifneq ($(shell which ltrace 2>/dev/null),) +TRACE_CONNECT = ltrace -f -C -r -e connect +else +$(warning Could not find strace or ltrace; test targets will likely not produce helpful feedback.) +endif + +issue: issue.c + $(CC) $(CFLAGS) -lzmq -o $@ $^ + +clean: + rm -f issue + +no-fail: issue + $(TRACE_CONNECT) ./issue + +connect-fail: issue + $(TRACE_CONNECT) ./issue fail + +auth-fail: issue + $(TRACE_CONNECT) ./issue wrong + +PHONY: clean no-fail connect-fail auth-fail + diff --git a/883/README b/883/README new file mode 100644 index 0000000..6786899 --- /dev/null +++ b/883/README @@ -0,0 +1,46 @@ +Use the included GNU make Makefile to build on Unix-like systems. strace or +ltrace is required to trace the underlying calls to connect. Without some kind +of trace tool, it will be difficult to see the problem. + +With all the tests below, the connect calls are traced and are timestamped with +the time between calls. The ZAP request handler also prints the ZAP request and +reply. ZMQ_RECONNECT_IVL is set to 200, ZMQ_RECONNECT_IVL_MAX is 5000, and +ZMQ_SNDTIMEO is 30000. ZMQ_IMMEDIATE is also enabled. + + +To build the test: + + $ make + + +To show the behavior when connect and authentication succeed, make the no-fail +target: + + $ make no-fail + +The no-fail test issues a single connect, authentication succeeds, the bounce +test succeeds, and the process exits almost immediately. + + +To show the behavior when connect fails, make the connect-fail target: + + $ make connect-fail + +The connect-fail test issues a succession of connect calls to the wrong port, +with the time between each call roughly doubling from the previous call, +starting with the value of ZMQ_RECONNECT_IVL and plateauing at about +ZMQ_RECONNECT_IVL_MAX. The process will assert and exit after about 30 seconds, +when zmq_send() times out. This is the expected behavior. + + +Finally, to show the behavior when connect succeeds but authentication fails, +make the auth-fail: + + $ make auth-fail + +As with the connect-fail test, the auth-fail test issues a series of connect +calls, but the time between calls is roughly the value of ZMQ_RECONNECT_IVL, +never increasing toward ZMQ_RECONNECT_IVL_MAX. Also, zmq_send succeeds almost +immediately and is never allowed to time out due to an unsuccessful ZAP +handshake (issue #882). + diff --git a/883/issue.c b/883/issue.c new file mode 100644 index 0000000..8dd48ed --- /dev/null +++ b/883/issue.c @@ -0,0 +1,187 @@ + +#include +#include +#include +#include + +#include +#include + + +/* + * Receive a string from socket and return a copy (must be freed by caller) + * or NULL, if an error occurred. + */ +static char *s_tryrecv(void *socket) +{ + char buffer[256]; + int size; + + if ((size = zmq_recv(socket, buffer, sizeof(buffer), 0)) == -1) + return NULL; + if (size >= sizeof(buffer)) + size = sizeof(buffer) - 1; + buffer[size] = '\0'; + return strdup(buffer); +} + + +/* + * Receive a string from socket and return a copy (must be freed by caller). + */ +static char *s_recv(void *socket) +{ + char *str; + + assert((str = s_tryrecv(socket))); + return str; +} + + +/* + * Return non-zero if socket has more data, zero otherwise. + */ +static int has_more(void *socket) +{ + int more; + size_t size; + + size = sizeof(more); + assert(!zmq_getsockopt(socket, ZMQ_RCVMORE, &more, &size)); + return more; +} + + +/* + * Assure that socket has more data before receiving and returning a string. + */ +static char *s_recvmore(void *socket) +{ + assert(has_more(socket)); + return s_recv(socket); +} + + +/* + * Send a string (possibly the last in a multipart message). + */ +static void s_send(void *socket, const char *str) +{ + assert(zmq_send(socket, str, strlen(str), 0) != -1); +} + + +/* + * Send a string in a frame of a multipart message. + */ +static void s_sendmore(void *socket, const char *str) +{ + assert(zmq_send(socket, str, strlen(str), ZMQ_SNDMORE) != -1); +} + + +/* + * ZAP request handling loop. + */ +static void zap_handler(void *handler) +{ + for (;;) { + char *version, *sequence, *domain, *address, *identity, *mechanism; + + /* Receive request. */ + if(!(version = s_tryrecv(handler))) + break; + sequence = s_recvmore(handler); + domain = s_recvmore(handler); + address = s_recvmore(handler); + identity = s_recvmore(handler); + mechanism = s_recvmore(handler); + assert(!has_more(handler)); + + assert(!strcmp(version, "1.0")); + assert(!strcmp(mechanism, "NULL")); + + printf("ZAP REQUEST %s %s %s %s %s %s\n", version, sequence, domain, + address, identity, mechanism); + + /* Send reply. */ + s_sendmore(handler, version); + s_sendmore(handler, sequence); + if(!strcmp(domain, "test")) { + s_sendmore(handler, "200"); + s_sendmore(handler, "OK"); + s_sendmore(handler, "anonymous"); + printf("ZAP REPLY 1.0 %s 200 OK anonymous\n", sequence); + } else { + s_sendmore(handler, "500"); + s_sendmore(handler, "Denied"); + s_sendmore(handler, ""); + printf("ZAP REPLY 1.0 %s 200 Denied\n", sequence); + } + s_send(handler, ""); + + /* Cleanup. */ + free(version); + free(sequence); + free(domain); + free(address); + free(identity); + free(mechanism); + } + zmq_close(handler); +} + + + +int main(int argc, char *argv[]) +{ + void *ctx, *handler, *thread, *server, *client; + const char *domain, *connect_addr; + int optval; + + domain = argc > 1 ? argv[1] : "test"; + connect_addr = strcmp(domain, "fail") ? "tcp://127.0.0.1:9000" : "tcp://127.0.0.1:9001"; + + assert((ctx = zmq_ctx_new())); + + /* Start ZAP handler thread. */ + assert((handler = zmq_socket(ctx, ZMQ_REP))); + assert(!(zmq_bind(handler, "inproc://zeromq.zap.01"))); + assert((thread = zmq_threadstart(zap_handler, handler))); + + /* Bind server. */ + assert((server = zmq_socket(ctx, ZMQ_DEALER))); + assert(!(zmq_setsockopt(server, ZMQ_ZAP_DOMAIN, domain, strlen(domain)))); + assert(!zmq_bind(server, "tcp://127.0.0.1:9000")); + + /* Connect client. */ + assert((client = zmq_socket(ctx, ZMQ_DEALER))); + optval = 200; + assert(!(zmq_setsockopt(client, ZMQ_RECONNECT_IVL, &optval, sizeof(optval)))); + optval = 5000; + assert(!(zmq_setsockopt(client, ZMQ_RECONNECT_IVL_MAX, &optval, sizeof(optval)))); + optval = 30000; + assert(!(zmq_setsockopt(client, ZMQ_SNDTIMEO, &optval, sizeof(optval)))); + optval = 1; + assert(!(zmq_setsockopt(client, ZMQ_IMMEDIATE, &optval, sizeof(optval)))); + assert(!zmq_connect(client, connect_addr)); + + /* Bounce test. */ + s_send(client, "Hello, Server!"); + assert(!strcmp(s_recv(server), "Hello, Server!")); + s_send(server, "Hello, Client!"); + assert(!strcmp(s_recv(client), "Hello, Client!")); + + /* Cleanup. */ + assert(!zmq_disconnect(client, connect_addr)); + assert(!zmq_close(client)); + + assert(!zmq_unbind(server, "tcp://127.0.0.1:9000")); + assert(!zmq_close(server)); + + assert(!zmq_term(ctx)); + zmq_threadclose(thread); + + return 0; +} +