diff options
| author | Garrett D'Amore <garrett@damore.org> | 2017-10-03 20:28:09 -0700 |
|---|---|---|
| committer | Garrett D'Amore <garrett@damore.org> | 2017-10-05 21:25:57 -0700 |
| commit | b0f31f578b0669b598d3ded3a625685b125bef1d (patch) | |
| tree | 0044b1f6924700a4fe4e557826bb79796f9e94d0 | |
| parent | 557964482f2b9d4246a2943fb1bedc6074d01e0d (diff) | |
| download | nng-b0f31f578b0669b598d3ded3a625685b125bef1d.tar.gz nng-b0f31f578b0669b598d3ded3a625685b125bef1d.tar.bz2 nng-b0f31f578b0669b598d3ded3a625685b125bef1d.zip | |
Improve UDP test coverage, fix numerous issues found.
We introduced richer, deeper tests for UDP functionality.
These tests uncovered a number of issues which this commit fixes.
The Windows IOCP code needs to support multiple aios on a single
nni_win_event. A redesign of the IOCP handling addresses that.
The POSIX UDP code also needed fixes; foremost among them is the
fact that the UDP file descriptor is not placed into non-blocking
mode, leading to potential hangs.
A number of race conditions and bugs along the implementation of
the above items were uncovered and fixed. To the best of our knowledge
the current code is bug-free.
| -rw-r--r-- | src/core/aio.c | 7 | ||||
| -rw-r--r-- | src/core/aio.h | 1 | ||||
| -rw-r--r-- | src/platform/posix/posix_pollq_poll.c | 11 | ||||
| -rw-r--r-- | src/platform/posix/posix_sockaddr.c | 6 | ||||
| -rw-r--r-- | src/platform/posix/posix_udp.c | 142 | ||||
| -rw-r--r-- | src/platform/windows/win_debug.c | 13 | ||||
| -rw-r--r-- | src/platform/windows/win_impl.h | 4 | ||||
| -rw-r--r-- | src/platform/windows/win_iocp.c | 125 | ||||
| -rw-r--r-- | src/platform/windows/win_sockaddr.c | 6 | ||||
| -rw-r--r-- | tests/tcp.c | 4 | ||||
| -rw-r--r-- | tests/udp.c | 197 |
11 files changed, 359 insertions, 157 deletions
diff --git a/src/core/aio.c b/src/core/aio.c index 141499e2..cec2ff7c 100644 --- a/src/core/aio.c +++ b/src/core/aio.c @@ -321,6 +321,13 @@ nni_aio_list_append(nni_list *list, nni_aio *aio) } void +nni_aio_list_prepend(nni_list *list, nni_aio *aio) +{ + nni_aio_list_remove(aio); + nni_list_prepend(list, aio); +} + +void nni_aio_list_remove(nni_aio *aio) { nni_list_node_remove(&aio->a_prov_node); diff --git a/src/core/aio.h b/src/core/aio.h index 14af4efc..b12fcc55 100644 --- a/src/core/aio.h +++ b/src/core/aio.h @@ -151,6 +151,7 @@ extern void nni_aio_wait(nni_aio *); // and append will perform any necessary remove first. extern void nni_aio_list_init(nni_list *); extern void nni_aio_list_append(nni_list *, nni_aio *); +extern void nni_aio_list_prepend(nni_list *, nni_aio *); extern void nni_aio_list_remove(nni_aio *); extern int nni_aio_list_active(nni_aio *); diff --git a/src/platform/posix/posix_pollq_poll.c b/src/platform/posix/posix_pollq_poll.c index d8252af9..e6abd2d2 100644 --- a/src/platform/posix/posix_pollq_poll.c +++ b/src/platform/posix/posix_pollq_poll.c @@ -266,14 +266,11 @@ nni_posix_pollq_arm(nni_posix_pollq_node *node, int events) // on the polled list. The polled list would be the case where // the index is set to a positive value. if ((oevents == 0) && (events != 0) && (node->index < 1)) { - if (nni_list_node_active(&node->node)) { - nni_list_node_remove(&node->node); - } + nni_list_node_remove(&node->node); nni_list_append(&pq->armed, node); } if ((events != 0) && (oevents != events)) { - // Possibly wake up the poller since we're looking for - // new events. + // Possibly wake up poller since we're looking for new events. if (pq->inpoll) { nni_plat_pipe_raise(pq->wakewfd); } @@ -295,9 +292,7 @@ nni_posix_pollq_disarm(nni_posix_pollq_node *node, int events) oevents = node->events; node->events &= ~events; if ((node->events == 0) && (oevents != 0)) { - if (nni_list_node_active(&node->node)) { - nni_list_node_remove(&node->node); - } + nni_list_node_remove(&node->node); nni_list_append(&pq->idle, node); } // No need to wake anything, we might get a spurious wake up but diff --git a/src/platform/posix/posix_sockaddr.c b/src/platform/posix/posix_sockaddr.c index ea630b01..25953f50 100644 --- a/src/platform/posix/posix_sockaddr.c +++ b/src/platform/posix/posix_sockaddr.c @@ -33,6 +33,9 @@ nni_posix_nn2sockaddr(void *sa, const nni_sockaddr *na) const nng_sockaddr_path *nspath; size_t sz; + if ((sa == NULL) || (na == NULL)) { + return (-1); + } switch (na->s_un.s_family) { case NNG_AF_INET: sin = (void *) sa; @@ -80,6 +83,9 @@ nni_posix_sockaddr2nn(nni_sockaddr *na, const void *sa) nng_sockaddr_in6 * nsin6; nng_sockaddr_path * nspath; + if ((na == NULL) || (sa == NULL)) { + return (-1); + } switch (((struct sockaddr *) sa)->sa_family) { case AF_INET: sin = (void *) sa; diff --git a/src/platform/posix/posix_udp.c b/src/platform/posix/posix_udp.c index 05f7bea1..31ef76f6 100644 --- a/src/platform/posix/posix_udp.c +++ b/src/platform/posix/posix_udp.c @@ -37,7 +37,6 @@ struct nni_plat_udp { nni_posix_pollq_node udp_pitem; int udp_fd; - int udp_closed; nni_list udp_recvq; nni_list udp_sendq; nni_mtx udp_mtx; @@ -48,7 +47,6 @@ nni_posix_udp_doclose(nni_plat_udp *udp) { nni_aio *aio; - udp->udp_closed = 1; while (((aio = nni_list_first(&udp->udp_recvq)) != NULL) || ((aio = nni_list_first(&udp->udp_sendq)) != NULL)) { nni_aio_list_remove(aio); @@ -62,57 +60,51 @@ nni_posix_udp_dorecv(nni_plat_udp *udp) { nni_aio * aio; nni_list *q = &udp->udp_recvq; - // While we're able to recv, do so. while ((aio = nni_list_first(q)) != NULL) { - nni_list_remove(q, aio); struct iovec iov[4]; // never have more than 4 int niov; struct sockaddr_storage ss; struct msghdr hdr; - int rv; + int rv = 0; + int cnt = 0; - hdr.msg_iov = iov; for (niov = 0; niov < aio->a_niov; niov++) { iov[niov].iov_base = aio->a_iov[niov].iov_buf; iov[niov].iov_len = aio->a_iov[niov].iov_len; } + hdr.msg_iov = iov; hdr.msg_iovlen = niov; hdr.msg_name = &ss; hdr.msg_namelen = sizeof(ss); hdr.msg_flags = 0; hdr.msg_control = NULL; hdr.msg_controllen = 0; - rv = recvmsg(udp->udp_fd, &hdr, 0); - if (rv < 0) { + + if ((cnt = recvmsg(udp->udp_fd, &hdr, 0)) < 0) { if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) { - // No data available at socket. Return - // the AIO to the head of the queue. - nni_list_prepend(q, aio); + // No data available at socket. Leave + // the AIO at the head of the queue. return; } rv = nni_plat_errno(errno); - nni_aio_finish_error(aio, rv); - continue; - } - - // We need to store the address information. - // It is incumbent on the AIO submitter to supply - // storage for the address. - if (aio->a_addr != NULL) { + } else if (aio->a_addr != NULL) { + // We need to store the address information. + // It is incumbent on the AIO submitter to supply + // storage for the address. nni_posix_sockaddr2nn(aio->a_addr, (void *) &ss); } - - nni_aio_finish(aio, 0, rv); + nni_list_remove(q, aio); + nni_aio_finish(aio, rv, cnt); } } static void nni_posix_udp_dosend(nni_plat_udp *udp) { - // XXX: TBD. nni_aio * aio; nni_list *q = &udp->udp_sendq; + int x = 0; // While we're able to send, do so. while ((aio = nni_list_first(q)) != NULL) { @@ -120,48 +112,37 @@ nni_posix_udp_dosend(nni_plat_udp *udp) struct msghdr hdr; struct iovec iov[4]; int niov; - int rv; int len; - - nni_list_remove(q, aio); - - if (aio->a_addr == NULL) { - // No outgoing address? - nni_aio_finish_error(aio, NNG_EADDRINVAL); - return; - } - len = nni_posix_nn2sockaddr(&ss, aio->a_addr); - if (len < 0) { - nni_aio_finish_error(aio, NNG_EADDRINVAL); - return; - } - - hdr.msg_iov = iov; - for (niov = 0; niov < aio->a_niov; niov++) { - iov[niov].iov_base = aio->a_iov[niov].iov_buf; - iov[niov].iov_len = aio->a_iov[niov].iov_len; - } - hdr.msg_iovlen = niov; - hdr.msg_name = &ss; - hdr.msg_namelen = len; - hdr.msg_flags = NNI_MSG_NOSIGNAL; - hdr.msg_control = NULL; - hdr.msg_controllen = 0; - - rv = sendmsg(udp->udp_fd, &hdr, 0); - if (rv < 0) { - if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) { - // Cannot send (buffers full), return to - // head of queue. - nni_list_prepend(q, aio); - return; + int rv = 0; + int cnt = 0; + + if ((len = nni_posix_nn2sockaddr(&ss, aio->a_addr)) < 0) { + rv = NNG_EADDRINVAL; + } else { + for (niov = 0; niov < aio->a_niov; niov++) { + iov[niov].iov_base = aio->a_iov[niov].iov_buf; + iov[niov].iov_len = aio->a_iov[niov].iov_len; + } + hdr.msg_iov = iov; + hdr.msg_iovlen = niov; + hdr.msg_name = &ss; + hdr.msg_namelen = len; + hdr.msg_flags = NNI_MSG_NOSIGNAL; + hdr.msg_control = NULL; + hdr.msg_controllen = 0; + + if ((cnt = sendmsg(udp->udp_fd, &hdr, 0)) < 0) { + if ((errno == EAGAIN) || + (errno == EWOULDBLOCK)) { + // Cannot send now, leave at head. + return; + } + rv = nni_plat_errno(errno); } - rv = nni_plat_errno(errno); - nni_aio_finish_error(aio, rv); - continue; } - nni_aio_finish(aio, 0, rv); + nni_list_remove(q, aio); + nni_aio_finish(aio, rv, cnt); } } @@ -234,6 +215,8 @@ nni_plat_udp_open(nni_plat_udp **upp, nni_sockaddr *bindaddr) udp->udp_pitem.cb = nni_posix_udp_cb; udp->udp_pitem.data = udp; + (void) fcntl(udp->udp_fd, F_SETFL, O_NONBLOCK); + nni_aio_list_init(&udp->udp_recvq); nni_aio_list_init(&udp->udp_sendq); @@ -255,17 +238,10 @@ nni_plat_udp_close(nni_plat_udp *udp) { nni_aio *aio; - nni_mtx_lock(&udp->udp_mtx); - if (udp->udp_closed) { - // The only way this happens is in response to a callback that - // is being canceled. Double close from user code is a bug. - nni_mtx_unlock(&udp->udp_mtx); - return; - } - // We're no longer interested in events. nni_posix_pollq_remove(&udp->udp_pitem); + nni_mtx_lock(&udp->udp_mtx); nni_posix_udp_doclose(udp); nni_mtx_unlock(&udp->udp_mtx); @@ -291,19 +267,10 @@ void nni_plat_udp_recv(nni_plat_udp *udp, nni_aio *aio) { nni_mtx_lock(&udp->udp_mtx); - if (nni_aio_start(aio, nni_plat_udp_cancel, udp) != 0) { - nni_mtx_unlock(&udp->udp_mtx); - return; - } - - if (udp->udp_closed) { - nni_aio_finish_error(aio, NNG_ECLOSED); - nni_mtx_unlock(&udp->udp_mtx); - return; + if (nni_aio_start(aio, nni_plat_udp_cancel, udp) == 0) { + nni_list_append(&udp->udp_recvq, aio); + nni_posix_pollq_arm(&udp->udp_pitem, POLLIN); } - - nni_list_append(&udp->udp_recvq, aio); - nni_posix_pollq_arm(&udp->udp_pitem, POLLIN); nni_mtx_unlock(&udp->udp_mtx); } @@ -311,19 +278,10 @@ void nni_plat_udp_send(nni_plat_udp *udp, nni_aio *aio) { nni_mtx_lock(&udp->udp_mtx); - if (nni_aio_start(aio, nni_plat_udp_cancel, udp) != 0) { - nni_mtx_unlock(&udp->udp_mtx); - return; + if (nni_aio_start(aio, nni_plat_udp_cancel, udp) == 0) { + nni_list_append(&udp->udp_sendq, aio); + nni_posix_pollq_arm(&udp->udp_pitem, POLLOUT); } - - if (udp->udp_closed) { - nni_aio_finish_error(aio, NNG_ECLOSED); - nni_mtx_unlock(&udp->udp_mtx); - return; - } - - nni_list_append(&udp->udp_sendq, aio); - nni_posix_pollq_arm(&udp->udp_pitem, POLLOUT); nni_mtx_unlock(&udp->udp_mtx); } diff --git a/src/platform/windows/win_debug.c b/src/platform/windows/win_debug.c index 5c6c3fb5..0113af58 100644 --- a/src/platform/windows/win_debug.c +++ b/src/platform/windows/win_debug.c @@ -83,8 +83,9 @@ nni_plat_errno(int errnum) static struct { int win_err; int nng_err; -} nni_win_errnos[] = { - // clang-format off +} nni_win_errnos[] = + { + // clang-format off { ERROR_FILE_NOT_FOUND, NNG_ENOENT }, { ERROR_ACCESS_DENIED, NNG_EPERM }, { ERROR_INVALID_HANDLE, NNG_ECLOSED }, @@ -114,6 +115,10 @@ static struct { { WSAENOPROTOOPT, NNG_ENOTSUP }, { WSAEPROTONOSUPPORT, NNG_ENOTSUP }, { WSAEPROTONOSUPPORT, NNG_ENOTSUP }, + { WSAESOCKTNOSUPPORT, NNG_ENOTSUP }, + { WSAEOPNOTSUPP, NNG_ENOTSUP }, + { WSAEPFNOSUPPORT, NNG_ENOTSUP }, + { WSAEAFNOSUPPORT, NNG_ENOTSUP }, { WSAEADDRINUSE, NNG_EADDRINUSE }, { WSAEADDRNOTAVAIL, NNG_EADDRINVAL }, { WSAENETDOWN, NNG_EUNREACHABLE }, @@ -137,8 +142,8 @@ static struct { // Must be Last!! { 0, 0 }, - // clang-format on -}; + // clang-format on + }; // This converts a Windows API error (from GetLastError()) to an // nng standard error code. diff --git a/src/platform/windows/win_impl.h b/src/platform/windows/win_impl.h index c2549266..236feb31 100644 --- a/src/platform/windows/win_impl.h +++ b/src/platform/windows/win_impl.h @@ -63,13 +63,15 @@ struct nni_win_event_ops { struct nni_win_event { OVERLAPPED olpd; void * ptr; - nni_aio * aio; nni_mtx mtx; nni_cv cv; unsigned run : 1; unsigned fini : 1; + unsigned closed : 1; unsigned count; int status; + nni_list aios; + nni_aio * active; nni_win_event_ops ops; }; diff --git a/src/platform/windows/win_iocp.c b/src/platform/windows/win_iocp.c index 6d2438d3..0f9348d6 100644 --- a/src/platform/windows/win_iocp.c +++ b/src/platform/windows/win_iocp.c @@ -24,11 +24,16 @@ static HANDLE nni_win_global_iocp = NULL; static nni_thr nni_win_iocp_thrs[NNI_WIN_IOCP_NTHREADS]; static nni_mtx nni_win_iocp_mtx; +static void nni_win_event_start(nni_win_event *); + static void -nni_win_event_finish(nni_win_event *evt, nni_aio *aio) +nni_win_event_finish(nni_win_event *evt) { + nni_aio *aio; evt->run = 0; - if (aio != NULL) { + + if ((aio = evt->active) != NULL) { + evt->active = NULL; evt->ops.wev_finish(evt, aio); } if (evt->fini) { @@ -44,9 +49,7 @@ nni_win_iocp_handler(void *arg) ULONG_PTR key; OVERLAPPED * olpd; nni_win_event *evt; - int rv; BOOL ok; - nni_aio * aio; NNI_ARG_UNUSED(arg); @@ -70,16 +73,15 @@ nni_win_iocp_handler(void *arg) nni_mtx_lock(&evt->mtx); if (ok) { - rv = ERROR_SUCCESS; + evt->status = 0; } else if (evt->status == 0) { evt->status = nni_win_error(GetLastError()); } - aio = evt->aio; - evt->aio = NULL; evt->count = cnt; - nni_win_event_finish(evt, aio); + nni_win_event_finish(evt); + nni_win_event_start(evt); nni_mtx_unlock(&evt->mtx); } } @@ -90,47 +92,65 @@ nni_win_event_cancel(nni_aio *aio, int rv) nni_win_event *evt = aio->a_prov_data; nni_mtx_lock(&evt->mtx); - if (evt->aio == aio) { + if (aio == evt->active) { evt->status = rv; // Use provider specific cancellation. evt->ops.wev_cancel(evt); + } else if (nni_aio_list_active(aio)) { + nni_aio_list_remove(aio); + nni_aio_finish_error(aio, rv); } nni_mtx_unlock(&evt->mtx); } void -nni_win_event_resubmit(nni_win_event *evt, nni_aio *aio) +nni_win_event_start(nni_win_event *evt) { - // This is just continuation of a pre-existing AIO operation. - // For example, continuing I/O of a multi-buffer s/g operation. - // The lock is held. + nni_aio *aio; + + // Lock held. + + if (evt->run) { + // Already running. + return; + } // Abort operation -- no further activity. - if (evt->fini) { - evt->run = 0; - nni_cv_wake(&evt->cv); + if (evt->fini || evt->closed) { + while ((aio = nni_list_first(&evt->aios)) != NULL) { + nni_aio_list_remove(aio); + nni_aio_finish_error(aio, NNG_ECLOSED); + } return; } + if ((aio = nni_list_first(&evt->aios)) == NULL) { + return; + } + + nni_aio_list_remove(aio); + evt->active = aio; evt->status = 0; evt->count = 0; if (!ResetEvent(evt->olpd.hEvent)) { - evt->status = nni_win_error(GetLastError()); - evt->count = 0; - nni_win_event_finish(evt, aio); + evt->active = NULL; + nni_aio_finish_error(aio, nni_win_error(GetLastError())); return; } - evt->aio = aio; evt->run = 1; if (evt->ops.wev_start(evt, aio) != 0) { // Start completed synchronously. It will have stored // the count and status in the evt. - evt->aio = NULL; - nni_win_event_finish(evt, aio); + nni_win_event_finish(evt); } } +void +nni_win_event_resubmit(nni_win_event *evt, nni_aio *aio) +{ + nni_aio_list_prepend(&evt->aios, aio); +} void nni_win_event_submit(nni_win_event *evt, nni_aio *aio) @@ -141,7 +161,8 @@ nni_win_event_submit(nni_win_event *evt, nni_aio *aio) nni_mtx_unlock(&evt->mtx); return; } - nni_win_event_resubmit(evt, aio); + nni_aio_list_append(&evt->aios, aio); + nni_win_event_start(evt); nni_mtx_unlock(&evt->mtx); } @@ -154,12 +175,20 @@ nni_win_event_complete(nni_win_event *evt, int cnt) void nni_win_event_close(nni_win_event *evt) { - if (evt->ptr != NULL) { - nni_mtx_lock(&evt->mtx); - evt->status = NNG_ECLOSED; - evt->ops.wev_cancel(evt); - nni_mtx_unlock(&evt->mtx); + nni_aio *aio; + + if (evt->ptr == NULL) { + return; // Never initialized + } + nni_mtx_lock(&evt->mtx); + evt->closed = 1; + evt->status = NNG_ECLOSED; + evt->ops.wev_cancel(evt); + while ((aio = nni_list_first(&evt->aios)) != NULL) { + nni_aio_list_remove(aio); + nni_aio_finish_error(aio, NNG_ECLOSED); } + nni_mtx_unlock(&evt->mtx); } int @@ -175,44 +204,52 @@ int nni_win_event_init(nni_win_event *evt, nni_win_event_ops *ops, void *ptr) { ZeroMemory(&evt->olpd, sizeof(evt->olpd)); - evt->olpd.hEvent = CreateEvent(NULL, TRUE, TRUE, NULL); - if (evt->olpd.hEvent == NULL) { - return (nni_win_error(GetLastError())); - } nni_mtx_init(&evt->mtx); nni_cv_init(&evt->cv, &evt->mtx); - + nni_aio_list_init(&evt->aios); evt->ops = *ops; - evt->aio = NULL; evt->ptr = ptr; evt->fini = 0; evt->run = 0; + + evt->olpd.hEvent = CreateEvent(NULL, TRUE, TRUE, NULL); + if (evt->olpd.hEvent == NULL) { + return (nni_win_error(GetLastError())); + } + return (0); } void nni_win_event_fini(nni_win_event *evt) { - if (evt->ptr != NULL) { - nni_mtx_lock(&evt->mtx); + nni_aio *aio; - evt->fini = 1; + if (evt->ptr == NULL) { + return; // Never initialized + } + nni_mtx_lock(&evt->mtx); - // Use provider specific cancellation. - evt->ops.wev_cancel(evt); + evt->fini = 1; - // Wait for everything to stop referencing this. - while (evt->run) { - nni_cv_wait(&evt->cv); - } + // Use provider specific cancellation. + evt->ops.wev_cancel(evt); - nni_mtx_unlock(&evt->mtx); + // Wait for everything to stop referencing this. + while (evt->run) { + nni_cv_wait(&evt->cv); + } + + while ((aio = nni_list_first(&evt->aios)) != NULL) { + nni_aio_list_remove(aio); + nni_aio_finish_error(aio, NNG_ECLOSED); } if (evt->olpd.hEvent != NULL) { (void) CloseHandle(evt->olpd.hEvent); evt->olpd.hEvent = NULL; } + nni_mtx_unlock(&evt->mtx); nni_cv_fini(&evt->cv); nni_mtx_fini(&evt->mtx); } diff --git a/src/platform/windows/win_sockaddr.c b/src/platform/windows/win_sockaddr.c index 0fa6dd51..f66542c6 100644 --- a/src/platform/windows/win_sockaddr.c +++ b/src/platform/windows/win_sockaddr.c @@ -20,6 +20,9 @@ nni_win_nn2sockaddr(SOCKADDR_STORAGE *ss, const nni_sockaddr *sa) SOCKADDR_IN * sin; SOCKADDR_IN6 *sin6; + if ((ss == NULL) || (sa == NULL)) { + return (-1); + } switch (sa->s_un.s_family) { case NNG_AF_INET: sin = (void *) ss; @@ -46,6 +49,9 @@ nni_win_sockaddr2nn(nni_sockaddr *sa, const SOCKADDR_STORAGE *ss) SOCKADDR_IN * sin; SOCKADDR_IN6 *sin6; + if ((ss == NULL) || (sa == NULL)) { + return (-1); + } switch (ss->ss_family) { case PF_INET: sin = (void *) ss; diff --git a/tests/tcp.c b/tests/tcp.c index 92e50a8b..54ce26b0 100644 --- a/tests/tcp.c +++ b/tests/tcp.c @@ -31,7 +31,7 @@ check_props_v4(nng_msg *msg, nng_listener l, nng_dialer d) So(nng_pipe_getopt(p, NNG_OPT_LOCADDR, &la, &z) == 0); So(z == sizeof(la)); So(la.s_un.s_family == NNG_AF_INET); - // So(la.s_un.s_in.sa_port == (trantest_port - 1)); + So(la.s_un.s_in.sa_port == htons(trantest_port - 1)); So(la.s_un.s_in.sa_port != 0); So(la.s_un.s_in.sa_addr == htonl(0x7f000001)); }); @@ -44,8 +44,6 @@ check_props_v4(nng_msg *msg, nng_listener l, nng_dialer d) So(ra.s_un.s_family == NNG_AF_INET); So(ra.s_un.s_in.sa_port != 0); So(ra.s_un.s_in.sa_addr == htonl(0x7f000001)); - - So(nng_dialer_getopt(d, NNG_OPT_REMADDR, &ra, &z) != 0); }); return (0); diff --git a/tests/udp.c b/tests/udp.c index 7f681ae1..e343bbab 100644 --- a/tests/udp.c +++ b/tests/udp.c @@ -36,16 +36,16 @@ TestMain("UDP support", { loopback = htonl(0x7f000001); // 127.0.0.1 + p1 = trantest_port++; + p2 = trantest_port++; + sa1.s_un.s_in.sa_family = NNG_AF_INET; sa1.s_un.s_in.sa_addr = loopback; - sa1.s_un.s_in.sa_port = trantest_port++; + sa1.s_un.s_in.sa_port = htons(p1); sa2.s_un.s_in.sa_family = NNG_AF_INET; sa2.s_un.s_in.sa_addr = loopback; - sa2.s_un.s_in.sa_port = trantest_port++; - - p1 = sa1.s_un.s_in.sa_port; - p2 = sa2.s_un.s_in.sa_port; + sa2.s_un.s_in.sa_port = htons(p2); So(nni_plat_udp_open(&u1, &sa1) == 0); So(nni_plat_udp_open(&u2, &sa2) == 0); @@ -92,9 +92,196 @@ TestMain("UDP support", { So(from.s_un.s_in.sa_port == sa1.s_un.s_in.sa_port); So(from.s_un.s_in.sa_addr == sa1.s_un.s_in.sa_addr); + // Set up some calls that will ever complete, so + // we test cancellation, etc. + nni_plat_udp_recv(u2, aio2); + nni_plat_udp_send(u2, aio1); + + nni_aio_fini(aio1); + nni_aio_fini(aio2); + }); + + Convey("Sending without an address fails", { + nni_aio *aio1; + char * msg = "nope"; + + nni_aio_init(&aio1, NULL, NULL); + aio1->a_niov = 1; + aio1->a_iov[0].iov_buf = msg; + aio1->a_iov[0].iov_len = strlen(msg) + 1; + + nni_plat_udp_send(u1, aio1); + nni_aio_wait(aio1); + So(nni_aio_result(aio1) == NNG_EADDRINVAL); + nni_aio_fini(aio1); + }); + + Convey("Multiple operations work", { + char msg1[] = "hello"; + char msg2[] = "there"; + char rbuf1[32]; + char rbuf2[32]; + nng_sockaddr to1; + nng_sockaddr to2; + nng_sockaddr from1; + nng_sockaddr from2; + nni_aio * aio1; + nni_aio * aio2; + nni_aio * aio3; + nni_aio * aio4; + + nni_aio_init(&aio1, NULL, NULL); + nni_aio_init(&aio2, NULL, NULL); + nni_aio_init(&aio3, NULL, NULL); + nni_aio_init(&aio4, NULL, NULL); + + to1 = sa2; + aio1->a_niov = 1; + aio1->a_iov[0].iov_buf = msg1; + aio1->a_iov[0].iov_len = strlen(msg1) + 1; + aio1->a_addr = &to1; + + to2 = sa2; + aio2->a_niov = 1; + aio2->a_iov[0].iov_buf = msg2; + aio2->a_iov[0].iov_len = strlen(msg2) + 1; + aio2->a_addr = &to2; + + aio3->a_niov = 1; + aio3->a_iov[0].iov_buf = rbuf1; + aio3->a_iov[0].iov_len = 1024; + aio3->a_addr = &from1; + + aio4->a_niov = 1; + aio4->a_iov[0].iov_buf = rbuf2; + aio4->a_iov[0].iov_len = 1024; + aio4->a_addr = &from2; + + nni_plat_udp_recv(u2, aio4); + nni_plat_udp_recv(u2, aio3); + nni_plat_udp_send(u1, aio2); + // This delay here is required to test for a race + // condition that does not occur if it is absent. + nng_usleep(200); + nni_plat_udp_send(u1, aio1); + + nni_aio_wait(aio2); + nni_aio_wait(aio1); + nni_aio_wait(aio3); + nni_aio_wait(aio4); + + So(nni_aio_result(aio1) == 0); + So(nni_aio_result(aio2) == 0); + So(nni_aio_result(aio3) == 0); + So(nni_aio_result(aio4) == 0); + + So(from1.s_un.s_in.sa_family == + sa1.s_un.s_in.sa_family); + So(from1.s_un.s_in.sa_port == sa1.s_un.s_in.sa_port); + So(from1.s_un.s_in.sa_addr == sa1.s_un.s_in.sa_addr); + nni_aio_fini(aio1); nni_aio_fini(aio2); + nni_aio_fini(aio3); + nni_aio_fini(aio4); }); + + Convey("Sending without an address fails", { + nni_aio *aio1; + char * msg = "nope"; + + nni_aio_init(&aio1, NULL, NULL); + aio1->a_niov = 1; + aio1->a_iov[0].iov_buf = msg; + aio1->a_iov[0].iov_len = strlen(msg) + 1; + + nni_plat_udp_send(u1, aio1); + nni_aio_wait(aio1); + So(nni_aio_result(aio1) == NNG_EADDRINVAL); + nni_aio_fini(aio1); + }); + + Convey("Sending to an IPv6 address on IPv4 fails", { + nni_aio * aio1; + char * msg = "nope"; + nng_sockaddr sa; + int rv; + + sa.s_un.s_in6.sa_family = NNG_AF_INET6; + // address is for google.com + inet_ntop(AF_INET6, "2607:f8b0:4007:804::200e", + (void *) sa.s_un.s_in6.sa_addr, 16); + sa.s_un.s_in6.sa_port = 80; + nni_aio_init(&aio1, NULL, NULL); + aio1->a_niov = 1; + aio1->a_iov[0].iov_buf = msg; + aio1->a_iov[0].iov_len = strlen(msg) + 1; + aio1->a_addr = &sa; + + nni_plat_udp_send(u1, aio1); + nni_aio_wait(aio1); + So((rv = nni_aio_result(aio1)) != 0); + So(rv == NNG_EADDRINVAL || rv == NNG_ENOTSUP || + rv == NNG_EUNREACHABLE); + nni_aio_fini(aio1); + }); + + Convey("Sending to an IPC address fails", { + nni_aio * aio1; + char * msg = "nope"; + nng_sockaddr sa; + int rv; + + sa.s_un.s_in6.sa_family = NNG_AF_INET6; + // address is for google.com + inet_ntop(AF_INET6, "2607:f8b0:4007:804::200e", + (void *) sa.s_un.s_in6.sa_addr, 16); + sa.s_un.s_in6.sa_port = 80; + nni_aio_init(&aio1, NULL, NULL); + aio1->a_niov = 1; + aio1->a_iov[0].iov_buf = msg; + aio1->a_iov[0].iov_len = strlen(msg) + 1; + aio1->a_addr = &sa; + + nni_plat_udp_send(u1, aio1); + nni_aio_wait(aio1); + So((rv = nni_aio_result(aio1)) != 0); + So(rv == NNG_EADDRINVAL || rv == NNG_ENOTSUP || + rv == NNG_EUNREACHABLE); + nni_aio_fini(aio1); + }); + + }); + + Convey("Cannot open using bogus sockaddr", { + nni_plat_udp *u; + nng_sockaddr sa; + int rv; + + sa.s_un.s_path.sa_family = NNG_AF_IPC; + strcpy(sa.s_un.s_path.sa_path, "/tmp/t"); + So((rv = nni_plat_udp_open(&u, &sa)) != 0); + // Some platforms reject IPC addresses altogether (Windows), + // whereas others just say it's not supported with UDP. + So((rv == NNG_ENOTSUP) || (rv == NNG_EADDRINVAL)); + + // NULL address also bad. + So((rv = nni_plat_udp_open(&u, NULL)) == NNG_EADDRINVAL); + }); + + Convey("Duplicate binds fail", { + nni_plat_udp *u1; + nni_plat_udp *u2; + nng_sockaddr sa; + uint16_t p1; + + p1 = trantest_port++; + sa.s_un.s_in.sa_family = NNG_AF_INET; + sa.s_un.s_in.sa_port = htons(p1); + sa.s_un.s_in.sa_addr = htonl(0x7f000001); + So(nni_plat_udp_open(&u1, &sa) == 0); + So(nni_plat_udp_open(&u2, &sa) == NNG_EADDRINUSE); + nni_plat_udp_close(u1); }); }); |
