summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBenny Prijono <bennylp@teluu.com>2006-07-10 21:35:27 +0000
committerBenny Prijono <bennylp@teluu.com>2006-07-10 21:35:27 +0000
commit1e26f458f4e9c7d6547a613e66c9df94abeb913a (patch)
treec640c4842a31914bc36518e6afe140fab2cd08f4
parent29912cad95b2725acbe030abed625998307eae0f (diff)
Fixed several bugs in WinNT IOCP: (1) accept() does not return correct addresses, (2) handle error condition when accept() returns -1 as the new socket
git-svn-id: http://svn.pjsip.org/repos/pjproject/trunk@599 74dad513-b988-da41-8d7b-12977e46ad98
-rw-r--r--pjlib/src/pj/ioqueue_winnt.c13
-rw-r--r--pjlib/src/pjlib-test/ioq_tcp.c292
-rw-r--r--pjlib/src/pjlib-test/test.h8
3 files changed, 307 insertions, 6 deletions
diff --git a/pjlib/src/pj/ioqueue_winnt.c b/pjlib/src/pj/ioqueue_winnt.c
index fbf308e1..71977cbd 100644
--- a/pjlib/src/pj/ioqueue_winnt.c
+++ b/pjlib/src/pj/ioqueue_winnt.c
@@ -26,6 +26,7 @@
#include <pj/log.h>
#include <pj/assert.h>
#include <pj/errno.h>
+#include <pj/compat/socket.h>
#if defined(PJ_HAS_WINSOCK2_H) && PJ_HAS_WINSOCK2_H != 0
@@ -173,7 +174,7 @@ static void ioqueue_on_accept_complete(ioqueue_accept_rec *accept_overlapped)
&locallen,
&remote,
&remotelen);
- if (*accept_overlapped->addrlen > locallen) {
+ if (*accept_overlapped->addrlen >= locallen) {
pj_memcpy(accept_overlapped->local, local, locallen);
pj_memcpy(accept_overlapped->remote, remote, locallen);
} else {
@@ -627,10 +628,18 @@ static pj_bool_t poll_iocp( HANDLE hIocp, DWORD dwTimeout,
ioqueue_on_accept_complete((ioqueue_accept_rec*)pOv);
if (key->cb.on_accept_complete) {
ioqueue_accept_rec *accept_rec = (ioqueue_accept_rec*)pOv;
+ pj_status_t status = PJ_SUCCESS;
+
+ if (accept_rec->newsock == PJ_INVALID_SOCKET) {
+ int dwError = WSAGetLastError();
+ if (dwError == 0) dwError = OSERR_ENOTCONN;
+ status = PJ_RETURN_OS_ERROR(dwError);
+ }
+
key->cb.on_accept_complete(key,
(pj_ioqueue_op_key_t*)pOv,
accept_rec->newsock,
- PJ_SUCCESS);
+ status);
accept_rec->newsock = PJ_INVALID_SOCKET;
}
break;
diff --git a/pjlib/src/pjlib-test/ioq_tcp.c b/pjlib/src/pjlib-test/ioq_tcp.c
index 4a546f03..ef958d77 100644
--- a/pjlib/src/pjlib-test/ioq_tcp.c
+++ b/pjlib/src/pjlib-test/ioq_tcp.c
@@ -530,6 +530,291 @@ on_error:
return status;
}
+
+/*
+ * Repeated connect/accept on the same listener socket.
+ */
+static int compliance_test_2(void)
+{
+ enum { MAX_PAIR = 4, TEST_LOOP = 2 };
+
+ struct listener
+ {
+ pj_sock_t sock;
+ pj_ioqueue_key_t *key;
+ pj_sockaddr_in addr;
+ int addr_len;
+ } listener;
+
+ struct server
+ {
+ pj_sock_t sock;
+ pj_ioqueue_key_t *key;
+ pj_sockaddr_in local_addr;
+ pj_sockaddr_in rem_addr;
+ int rem_addr_len;
+ pj_ioqueue_op_key_t accept_op;
+ } server[MAX_PAIR];
+
+ struct client
+ {
+ pj_sock_t sock;
+ pj_ioqueue_key_t *key;
+ } client[MAX_PAIR];
+
+ pj_pool_t *pool = NULL;
+ char *send_buf, *recv_buf;
+ pj_ioqueue_t *ioque = NULL;
+ int i, bufsize = BUF_MIN_SIZE;
+ pj_ssize_t status;
+ int test_loop, pending_op = 0;
+ pj_timestamp t_elapsed;
+ pj_str_t s;
+ pj_status_t rc;
+
+ // Create pool.
+ pool = pj_pool_create(mem, NULL, POOL_SIZE, 4000, NULL);
+
+
+ // Create I/O Queue.
+ rc = pj_ioqueue_create(pool, PJ_IOQUEUE_MAX_HANDLES, &ioque);
+ if (rc != PJ_SUCCESS) {
+ app_perror("...ERROR in pj_ioqueue_create()", rc);
+ return -10;
+ }
+
+
+ // Allocate buffers for send and receive.
+ send_buf = (char*)pj_pool_alloc(pool, bufsize);
+ recv_buf = (char*)pj_pool_alloc(pool, bufsize);
+
+ // Create listener socket
+ rc = pj_sock_socket(PJ_AF_INET, PJ_SOCK_STREAM, 0, &listener.sock);
+ if (rc != PJ_SUCCESS) {
+ app_perror("...error creating socket", rc);
+ status=-20; goto on_error;
+ }
+
+ // Bind listener socket.
+ pj_sockaddr_in_init(&listener.addr, 0, 0);
+ if ((rc=pj_sock_bind(listener.sock, &listener.addr, sizeof(listener.addr))) != 0 ) {
+ app_perror("...bind error", rc);
+ status=-30; goto on_error;
+ }
+
+ // Get listener address.
+ listener.addr_len = sizeof(listener.addr);
+ rc = pj_sock_getsockname(listener.sock, &listener.addr, &listener.addr_len);
+ if (rc != PJ_SUCCESS) {
+ app_perror("...ERROR in pj_sock_getsockname()", rc);
+ status=-40; goto on_error;
+ }
+ listener.addr.sin_addr = pj_inet_addr(pj_cstr(&s, "127.0.0.1"));
+
+
+ // Register listener socket.
+ rc = pj_ioqueue_register_sock(pool, ioque, listener.sock, NULL, &test_cb,
+ &listener.key);
+ if (rc != PJ_SUCCESS) {
+ app_perror("...ERROR", rc);
+ status=-50; goto on_error;
+ }
+
+
+ // Listener socket listen().
+ if (pj_sock_listen(listener.sock, 5)) {
+ app_perror("...ERROR in pj_sock_listen()", rc);
+ status=-60; goto on_error;
+ }
+
+
+ for (test_loop=0; test_loop < TEST_LOOP; ++test_loop) {
+ // Client connect and server accept.
+ for (i=0; i<MAX_PAIR; ++i) {
+ rc = pj_sock_socket(PJ_AF_INET, PJ_SOCK_STREAM, 0, &client[i].sock);
+ if (rc != PJ_SUCCESS) {
+ app_perror("...error creating socket", rc);
+ status=-70; goto on_error;
+ }
+
+ rc = pj_ioqueue_register_sock(pool, ioque, client[i].sock, NULL,
+ &test_cb, &client[i].key);
+ if (rc != PJ_SUCCESS) {
+ app_perror("...error ", rc);
+ status=-80; goto on_error;
+ }
+
+ // Server socket accept()
+ pj_ioqueue_op_key_init(&server[i].accept_op,
+ sizeof(server[i].accept_op));
+ server[i].rem_addr_len = sizeof(pj_sockaddr_in);
+ status = pj_ioqueue_accept(listener.key, &server[i].accept_op,
+ &server[i].sock, &server[i].local_addr,
+ &server[i].rem_addr,
+ &server[i].rem_addr_len);
+ if (status!=PJ_SUCCESS && status != PJ_EPENDING) {
+ app_perror("...ERROR in pj_ioqueue_accept()", rc);
+ status=-90; goto on_error;
+ }
+ if (status==PJ_EPENDING) {
+ ++pending_op;
+ }
+
+
+ // Client socket connect()
+ status = pj_ioqueue_connect(client[i].key, &listener.addr,
+ sizeof(listener.addr));
+ if (status!=PJ_SUCCESS && status != PJ_EPENDING) {
+ app_perror("...ERROR in pj_ioqueue_connect()", rc);
+ status=-100; goto on_error;
+ }
+ if (status==PJ_EPENDING) {
+ ++pending_op;
+ }
+
+ }
+
+
+ // Poll until all connected
+ while (pending_op) {
+ pj_time_val timeout = {1, 0};
+
+ status=pj_ioqueue_poll(ioque, &timeout);
+ if (status > 0) {
+ if (status > pending_op) {
+ PJ_LOG(3,(THIS_FILE,
+ "...error: pj_ioqueue_poll() returned %d "
+ "(only expecting %d)",
+ status, pending_op));
+ return -110;
+ }
+ pending_op -= status;
+
+ if (pending_op == 0) {
+ status = 0;
+ }
+ }
+ }
+
+ // There's no pending operation.
+ // When we poll the ioqueue, there must not be events.
+ if (pending_op == 0) {
+ pj_time_val timeout = {1, 0};
+ status = pj_ioqueue_poll(ioque, &timeout);
+ if (status != 0) {
+ status=-120; goto on_error;
+ }
+ }
+
+ for (i=0; i<MAX_PAIR; ++i) {
+ // Check server socket.
+ if (server[i].sock == PJ_INVALID_SOCKET) {
+ status = -130;
+ app_perror("...accept() error", pj_get_os_error());
+ goto on_error;
+ }
+
+ // Check addresses
+ if (server[i].local_addr.sin_family != PJ_AF_INET ||
+ server[i].local_addr.sin_addr.s_addr == 0 ||
+ server[i].local_addr.sin_port == 0)
+ {
+ app_perror("...ERROR address not set", rc);
+ status = -140;
+ goto on_error;
+ }
+
+ if (server[i].rem_addr.sin_family != PJ_AF_INET ||
+ server[i].rem_addr.sin_addr.s_addr == 0 ||
+ server[i].rem_addr.sin_port == 0)
+ {
+ app_perror("...ERROR address not set", rc);
+ status = -150;
+ goto on_error;
+ }
+
+
+ // Register newly accepted socket.
+ rc = pj_ioqueue_register_sock(pool, ioque, server[i].sock, NULL,
+ &test_cb, &server[i].key);
+ if (rc != PJ_SUCCESS) {
+ app_perror("...ERROR in pj_ioqueue_register_sock", rc);
+ status = -160;
+ goto on_error;
+ }
+
+ // Test send and receive.
+ t_elapsed.u32.lo = 0;
+ status = send_recv_test(ioque, server[i].key, client[i].key,
+ send_buf, recv_buf, bufsize, &t_elapsed);
+ if (status != 0) {
+ goto on_error;
+ }
+ }
+
+ // Success
+ status = 0;
+
+ for (i=0; i<MAX_PAIR; ++i) {
+ if (server[i].key != NULL) {
+ pj_ioqueue_unregister(server[i].key);
+ server[i].key = NULL;
+ server[i].sock = PJ_INVALID_SOCKET;
+ } else if (server[i].sock != PJ_INVALID_SOCKET) {
+ pj_sock_close(server[i].sock);
+ server[i].sock = PJ_INVALID_SOCKET;
+ }
+
+ if (client[i].key != NULL) {
+ pj_ioqueue_unregister(client[i].key);
+ client[i].key = NULL;
+ client[i].sock = PJ_INVALID_SOCKET;
+ } else if (client[i].sock != PJ_INVALID_SOCKET) {
+ pj_sock_close(client[i].sock);
+ client[i].sock = PJ_INVALID_SOCKET;
+ }
+ }
+ }
+
+ status = 0;
+
+on_error:
+ for (i=0; i<MAX_PAIR; ++i) {
+ if (server[i].key != NULL) {
+ pj_ioqueue_unregister(server[i].key);
+ server[i].key = NULL;
+ server[i].sock = PJ_INVALID_SOCKET;
+ } else if (server[i].sock != PJ_INVALID_SOCKET) {
+ pj_sock_close(server[i].sock);
+ server[i].sock = PJ_INVALID_SOCKET;
+ }
+
+ if (client[i].key != NULL) {
+ pj_ioqueue_unregister(client[i].key);
+ client[i].key = NULL;
+ server[i].sock = PJ_INVALID_SOCKET;
+ } else if (client[i].sock != PJ_INVALID_SOCKET) {
+ pj_sock_close(client[i].sock);
+ client[i].sock = PJ_INVALID_SOCKET;
+ }
+ }
+
+ if (listener.key) {
+ pj_ioqueue_unregister(listener.key);
+ listener.key = NULL;
+ } else if (listener.sock != PJ_INVALID_SOCKET) {
+ pj_sock_close(listener.sock);
+ listener.sock = PJ_INVALID_SOCKET;
+ }
+
+ if (ioque != NULL)
+ pj_ioqueue_destroy(ioque);
+ pj_pool_release(pool);
+ return status;
+
+}
+
+
int tcp_ioqueue_test()
{
int status;
@@ -547,6 +832,13 @@ int tcp_ioqueue_test()
return status;
}
+ PJ_LOG(3, (THIS_FILE, "..%s compliance test 2 (repeated accept)",
+ pj_ioqueue_name()));
+ if ((status=compliance_test_2()) != 0) {
+ PJ_LOG(1, (THIS_FILE, "....FAILED (status=%d)\n", status));
+ return status;
+ }
+
return 0;
}
diff --git a/pjlib/src/pjlib-test/test.h b/pjlib/src/pjlib-test/test.h
index d552ea40..52600866 100644
--- a/pjlib/src/pjlib-test/test.h
+++ b/pjlib/src/pjlib-test/test.h
@@ -21,11 +21,11 @@
#include <pj/types.h>
-#define GROUP_LIBC 0
-#define GROUP_OS 0
-#define GROUP_DATA_STRUCTURE 0
+#define GROUP_LIBC 1
+#define GROUP_OS 1
+#define GROUP_DATA_STRUCTURE 1
#define GROUP_NETWORK 1
-#define GROUP_FILE 0
+#define GROUP_FILE 1
#define INCLUDE_ERRNO_TEST GROUP_LIBC
#define INCLUDE_TIMESTAMP_TEST GROUP_OS