* PERFORMANCE OF THIS SOFTWARE.
*/
-/* $Id: socket.c,v 1.5.2.13.2.15 2005/06/09 23:54:32 marka Exp $ */
+/* $Id: socket.c,v 1.5.2.13.2.16 2005/09/01 03:16:12 marka Exp $ */
/* This code has been rewritten to take advantage of Windows Sockets
* I/O Completion Ports and Events. I/O Completion Ports is ONLY
* NOTE: This requires that Windows 2000 systems install Service Pack 2
* or later.
*/
-#ifndef SIO_UDP_CONNRESET
-#define SIO_UDP_CONNRESET _WSAIOW(IOC_VENDOR,12)
+#ifndef SIO_UDP_CONNRESET
+#define SIO_UDP_CONNRESET _WSAIOW(IOC_VENDOR,12)
#endif
/*
/*
* Define what the possible "soft" errors can be. These are non-fatal returns
* of various network related functions, like recv() and so on.
- *
- * For some reason, BSDI (and perhaps others) will sometimes return <0
- * from recv() but will have errno==0. This is broken, but we have to
- * work around it here.
*/
#define SOFT_ERROR(e) ((e) == WSAEINTR || \
- (e) == WSA_IO_PENDING || \
(e) == WSAEWOULDBLOCK || \
(e) == EWOULDBLOCK || \
(e) == EINTR || \
(e) == EAGAIN || \
(e) == 0)
+/*
+ * Pending errors are not really errors and should be
+ * kept separate
+ */
+#define PENDING_ERROR(e) ((e) == WSA_IO_PENDING || (e) == 0)
+
+#define DOIO_SUCCESS 0 /* i/o ok, event sent */
+#define DOIO_SOFT 1 /* i/o ok, soft error, no event sent */
+#define DOIO_HARD 2 /* i/o error, event sent */
+#define DOIO_EOF 3 /* EOF, no event sent */
+#define DOIO_PENDING 4 /* status when i/o is in process */
+
#define DLVL(x) ISC_LOGCATEGORY_GENERAL, ISC_LOGMODULE_SOCKET, ISC_LOG_DEBUG(x)
/*
#endif
#endif
-/*
- * NetBSD and FreeBSD can timestamp packets. XXXMLG Should we have
- * a setsockopt() like interface to request timestamps, and if the OS
- * doesn't do it for us, call gettimeofday() on every UDP receive?
- */
-
/*
* We really don't want to try and use these control messages. Win32
- * doesn't have this mechanism
+ * doesn't have this mechanism before XP.
*/
#undef USE_CMSG
struct msghdr {
- void *msg_name; /* optional address */
- u_int msg_namelen; /* size of address */
- WSABUF *msg_iov; /* scatter/gather array */
- u_int msg_iovlen; /* # elements in msg_iov */
- void *msg_control; /* ancillary data, see below */
- u_int msg_controllen; /* ancillary data buffer len */
- int msg_flags; /* flags on received message */
+ void *msg_name; /* optional address */
+ u_int msg_namelen; /* size of address */
+ WSABUF *msg_iov; /* scatter/gather array */
+ u_int msg_iovlen; /* # elements in msg_iov */
+ void *msg_control; /* ancillary data, see below */
+ u_int msg_controllen; /* ancillary data buffer len */
+ int msg_flags; /* flags on received message */
+ int msg_totallen; /* total length of this message */
} msghdr;
-
+
/*
- * The number of times a send operation is repeated if the result is EINTR.
+ * The number of times a send operation is repeated if the result
+ * is WSAEINTR.
*/
#define NRETRIES 10
OVERLAPPED overlapped;
/* Pointers to scatter/gather buffers */
WSABUF iov[ISC_SOCKET_MAXSCATTERGATHER];
- size_t totalBytes;
WSAEVENT hEvent; /* Event Handle */
long wait_type; /* Events to wait on */
WSAEVENT hAlert; /* Alert Event Handle */
DWORD evthread_id; /* Event Thread Id for socket */
-
/* Locked by socket lock. */
ISC_LINK(isc_socket_t) link;
unsigned int references;
/*
* Note: We are using an array here since *WaitForMultiple* wants an array
- * WARNING: This value may not be greater than 64 since the
+ * WARNING: This value may not be greater than 64 since the
* WSAWaitForMultipleEvents function is limited to 64 events.
*/
DWORD dwIOCPThreadIds[MAX_IOCPTHREADS];
};
-#define CLOSED 0 /* this one must be zero */
-#define MANAGED 1
-#define CLOSE_PENDING 2
-
/*
* send() and recv() iovec counts
*/
strbuf);
exit(1);
}
-
+
/*
* Worker threads for servicing the I/O
*/
iocompletionport_createthreads(manager->maxIOCPThreads, manager);
}
-
+
void
iocompletionport_exit(isc_socketmgr_t *manager) {
REQUIRE(VALID_MANAGER(manager));
REQUIRE(sock != NULL);
-
+
evchange = HeapAlloc(hHeapHandle, HEAP_ZERO_MEMORY,
sizeof(event_change_t));
evchange->sock = sock;
if (hEvent == WSA_INVALID_EVENT) {
stat = WSAGetLastError();
isc__strerror(stat, strbuf, sizeof(strbuf));
- msg = isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL,
+ msg = isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL,
ISC_MSG_FAILED, "failed"),
UNEXPECTED_ERROR(__FILE__, __LINE__, "WSACreateEvent: %s: %s",
msg, strbuf);
if (WSAEventSelect(sock->fd, hEvent, type) != 0) {
stat = WSAGetLastError();
isc__strerror(stat, strbuf, sizeof(strbuf));
- msg = isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL,
+ msg = isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL,
ISC_MSG_FAILED, "failed");
UNEXPECTED_ERROR(__FILE__, __LINE__, "WSAEventSelect: %s: %s",
msg, strbuf);
/* Need Winsock 2.0 or better */
wVersionRequested = MAKEWORD(2, 0);
-
+
err = WSAStartup(wVersionRequested, &wsaData);
if ( err != 0 ) {
/* Tell the user that we could not find a usable Winsock DLL */
int total_sent;
*Error = 0;
- Result = WSASendTo((SOCKET) sock->fd,
- messagehdr->msg_iov,
- messagehdr->msg_iovlen,
- &BytesSent,
- Flags,
- messagehdr->msg_name,
- messagehdr->msg_namelen,
- (LPOVERLAPPED) lpo,
- NULL);
+ Result = WSASendTo((SOCKET) sock->fd, messagehdr->msg_iov,
+ messagehdr->msg_iovlen, &BytesSent,
+ Flags, messagehdr->msg_name,
+ messagehdr->msg_namelen, (LPOVERLAPPED) lpo,
+ NULL);
total_sent = (int) BytesSent;
-
+
/* Check for errors.*/
if (Result == SOCKET_ERROR) {
*Error = WSAGetLastError();
-
- switch (*Error) {
+
+ switch (*Error) {
case WSA_IO_INCOMPLETE :
case WSA_WAIT_IO_COMPLETION :
case WSA_IO_PENDING :
if (Result == SOCKET_ERROR) {
*Error = WSAGetLastError();
-
- switch (*Error) {
+
+ switch (*Error) {
case WSA_IO_INCOMPLETE:
case WSA_WAIT_IO_COMPLETION:
case WSA_IO_PENDING:
return (-1);
else
return (total_bytes);
-}
+}
static void
manager_log(isc_socketmgr_t *sockmgr, isc_logcategory_t *category,
*
* Nothing can be NULL, and the done event must list at least one buffer
* on the buffer linked list for this function to be meaningful.
- *
- * If write_countp != NULL, *write_countp will hold the number of bytes
- * this transaction can send.
*/
static void
build_msghdr_send(isc_socket_t *sock, isc_socketevent_t *dev,
- struct msghdr *msg, char *cmsg,
- WSABUF *iov, size_t *write_countp)
+ struct msghdr *msg, char *cmsg, WSABUF *iov)
{
unsigned int iovcount;
isc_buffer_t *buffer;
config:
msg->msg_iov = iov;
msg->msg_iovlen = iovcount;
-
- if (write_countp != NULL)
- *write_countp = write_count;
+ msg->msg_totallen = write_count;
}
/*
*
* Nothing can be NULL, and the done event must list at least one buffer
* on the buffer linked list for this function to be meaningful.
- *
- * If read_countp != NULL, *read_countp will hold the number of bytes
- * this transaction can receive.
*/
static void
build_msghdr_recv(isc_socket_t *sock, isc_socketevent_t *dev,
- struct msghdr *msg, char *cmsg,
- WSABUF *iov, size_t *read_countp)
+ struct msghdr *msg, char *cmsg, WSABUF *iov)
{
unsigned int iovcount;
isc_buffer_t *buffer;
msg->msg_iov = iov;
msg->msg_iovlen = iovcount;
-
- if (read_countp != NULL)
- *read_countp = read_count;
+ msg->msg_totallen = read_count;
}
static void
}
#endif
-#define DOIO_SUCCESS 0 /* i/o ok, event sent */
-#define DOIO_SOFT 1 /* i/o ok, soft error, no event sent */
-#define DOIO_HARD 2 /* i/o error, event sent */
-#define DOIO_EOF 3 /* EOF, no event sent */
-
static int
completeio_recv(isc_socket_t *sock, isc_socketevent_t *dev,
struct msghdr *messagehdr, int cc, int recv_errno)
ALWAYS_HARD(ERROR_PORT_UNREACHABLE, ISC_R_HOSTUNREACH);
ALWAYS_HARD(ERROR_HOST_UNREACHABLE, ISC_R_HOSTUNREACH);
ALWAYS_HARD(ERROR_NETWORK_UNREACHABLE, ISC_R_NETUNREACH);
+ ALWAYS_HARD(ERROR_NETNAME_DELETED, ISC_R_NETUNREACH);
ALWAYS_HARD(WSAENOBUFS, ISC_R_NORESOURCES);
#undef SOFT_OR_HARD
if (isc_log_wouldlog(isc_lctx, IOEVENT_LEVEL)) {
socket_log(sock, &dev->address, IOEVENT,
isc_msgcat, ISC_MSGSET_SOCKET,
- ISC_MSG_ZEROPORT,
+ ISC_MSG_ZEROPORT,
"dropping source port zero packet");
}
return (DOIO_SOFT);
* If we read less than we expected, update counters,
* and let the upper layer handle it.
*/
- if (((size_t)cc != sock->totalBytes) && (dev->n < dev->minimum))
+ if ((cc != messagehdr->msg_totallen) && (dev->n < dev->minimum))
return (DOIO_SOFT);
/*
msghdr = &lpo->messagehdr;
memset(msghdr, 0, sizeof(struct msghdr));
- build_msghdr_recv(sock, dev, msghdr, cmsg, sock->iov,
- &(sock->totalBytes));
+ build_msghdr_recv(sock, dev, msghdr, cmsg, sock->iov);
#if defined(ISC_SOCKET_DEBUG)
dump_msg(msghdr, sock);
*nbytes = internal_recvmsg(sock, lpo, msghdr, 0, recv_errno);
if (*nbytes < 0) {
+ /*
+ * I/O has been initiated
+ * return will be via the completion port
+ */
+ if (PENDING_ERROR(*recv_errno)) {
+ status = DOIO_PENDING;
+ goto done;
+ }
if (SOFT_ERROR(*recv_errno)) {
status = DOIO_SOFT;
goto done;
}
+ /*
+ * If we got this far something is wrong
+ */
if (isc_log_wouldlog(isc_lctx, IOEVENT_LEVEL)) {
isc__strerror(*recv_errno, strbuf, sizeof(strbuf));
socket_log(sock, NULL, IOEVENT,
isc_msgcat, ISC_MSGSET_SOCKET,
- ISC_MSG_DOIORECV,
+ ISC_MSG_DOIORECV,
"startio_recv: recvmsg(%d) %d bytes, "
"err %d/%s",
sock->fd, *nbytes, *recv_errno, strbuf);
}
- status = completeio_recv(sock, dev, msghdr,
- *nbytes, *recv_errno);
+ status = DOIO_HARD;
goto done;
}
dev->result = ISC_R_SUCCESS;
if(send_errno != 0) {
+
if (SOFT_ERROR(send_errno))
return (DOIO_SOFT);
* If we write less than we expected, update counters, poke.
*/
dev->n += cc;
- if ((size_t)cc != sock->totalBytes)
+ if (cc != messagehdr->msg_totallen)
return (DOIO_SOFT);
/*
msghdr = &lpo->messagehdr;
memset(msghdr, 0, sizeof(struct msghdr));
- build_msghdr_send(sock, dev, msghdr, cmsg, sock->iov,
- &(sock->totalBytes));
+ build_msghdr_send(sock, dev, msghdr, cmsg, sock->iov);
*nbytes = internal_sendmsg(sock, lpo, msghdr, 0, send_errno);
+
if (*nbytes < 0) {
+ /*
+ * I/O has been initiated
+ * completion will be through the completion port
+ */
+ if (PENDING_ERROR(*send_errno)) {
+ status = DOIO_PENDING;
+ goto done;
+ }
+
if (SOFT_ERROR(*send_errno)) {
status = DOIO_SOFT;
goto done;
}
+ /*
+ * If we got this far then something is wrong
+ */
if (isc_log_wouldlog(isc_lctx, IOEVENT_LEVEL)) {
isc__strerror(*send_errno, strbuf, sizeof(strbuf));
socket_log(sock, NULL, IOEVENT,
isc_msgcat, ISC_MSGSET_SOCKET,
- ISC_MSG_INTERNALSEND,
+ ISC_MSG_INTERNALSEND,
"startio_send: internal_sendmsg(%d) %d "
"bytes, err %d/%s",
sock->fd, *nbytes, *send_errno, strbuf);
}
- status = completeio_send(sock, dev, msghdr,
- *nbytes, *send_errno);
goto done;
}
dev->result = ISC_R_SUCCESS;
sock->fd = socket(pf, SOCK_STREAM, IPPROTO_TCP);
break;
}
-
+
if (sock->fd == INVALID_SOCKET) {
socket_errno = WSAGetLastError();
free_socket(&sock);
strbuf);
}
#endif /* IPV6_RECVPKTINFO */
-#ifdef IPV6_USE_MIN_MTU /*2292bis, not too common yet*/
+#ifdef IPV6_USE_MIN_MTU /*2292bis, not too common yet*/
/* use minimum MTU */
if (pf == AF_INET6) {
(void)setsockopt(sock->fd, IPPROTO_IPV6,
UNLOCK(&sock->lock);
return;
}
-
+
/*
* Get the first item off the accept list.
* If it is empty, unlock the socket and return.
/*
* Try to accept the new connection. If the accept fails with
- * EAGAIN or EINTR, the event wait will be notified again since
+ * WSAEINTR, the event wait will be notified again since
* the event will be reset on return to caller.
*/
addrlen = sizeof(dev->newsocket->address.type);
UNEXPECTED_ERROR(__FILE__, __LINE__,
"internal_accept(): "
"accept() returned peer address "
- "family %u (expected %u)",
+ "family %u (expected %u)",
dev->newsocket->address.
type.sa.sa_family,
sock->pf);
const char *msg;
stat = WSAGetLastError();
isc__strerror(stat, strbuf, sizeof(strbuf));
- msg = isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL,
+ msg = isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL,
ISC_MSG_FAILED, "failed");
UNEXPECTED_ERROR(__FILE__, __LINE__, "WSAEventSelect: %s: %s",
msg, strbuf);
const char *msg;
stat = WSAGetLastError();
isc__strerror(stat, strbuf, sizeof(strbuf));
- msg = isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL,
+ msg = isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL,
ISC_MSG_FAILED, "failed");
UNEXPECTED_ERROR(__FILE__, __LINE__,
"WSAEventSelect: %s: %s", msg, strbuf);
*/
if (connect_errno != 0) {
/*
- * If the error is EAGAIN, just try again on this
+ * If the error is SOFT, just try again on this
* fd and pretend nothing strange happened.
*/
if (SOFT_ERROR(connect_errno) ||
struct msghdr *messagehdr, int nbytes, int send_errno)
{
isc_socketevent_t *ldev;
- int io_state;
- int cc;
/*
* Find out what socket this is and lock it.
*/
switch (completeio_send(sock, dev, messagehdr, nbytes, send_errno)) {
case DOIO_SOFT:
- cc = 0;
- send_errno = 0;
- io_state = startio_send(sock, dev, &cc, &send_errno);
- goto done;
-
+ break;
case DOIO_HARD:
case DOIO_SUCCESS:
send_senddone_event(sock, &dev);
* Loop forever waiting on I/O Completions and then processing them
*/
while (TRUE) {
- bSuccess = GetQueuedCompletionStatus (
- manager->hIoCompletionPort,
- &nbytes,
- (LPDWORD) &sock,
- (LPOVERLAPPED *)&lpo,
- INFINITE);
+ bSuccess = GetQueuedCompletionStatus(manager->hIoCompletionPort,
+ &nbytes, (LPDWORD) &sock,
+ (LPOVERLAPPED *)&lpo,
+ INFINITE);
if (lpo == NULL) {
/*
* Received request to exit
/*
* Was this the socket closed under us?
*/
- errstatus = WSAGetLastError();
+ errstatus = GetLastError();
if (nbytes == 0 && errstatus == WSA_OPERATION_ABORTED) {
LOCK(&sock->lock);
switch (lpo->request_type) {
if (cc == WSA_WAIT_FAILED) {
event_errno = WSAGetLastError();
if (!SOFT_ERROR(event_errno)) {
- isc__strerror(event_errno, strbuf,
+ isc__strerror(event_errno, strbuf,
sizeof(strbuf));
FATAL_ERROR(__FILE__, __LINE__,
"WSAWaitForMultipleEvents() %s: %s",
io_state = startio_recv(sock, dev, &cc, &recv_errno);
switch (io_state) {
+ case DOIO_PENDING: /* I/O Started. Nothing to be done */
case DOIO_SOFT:
/*
* We couldn't read all or part of the request right now, so
io_state = startio_send(sock, dev, &cc, &send_errno);
switch (io_state) {
+ case DOIO_PENDING: /* I/O started. Nothing more to do */
case DOIO_SOFT:
/*
* We couldn't send all or part of the request right now, so
* queue it unless ISC_SOCKFLAG_NORETRY is set.
*/
- isc_task_attach(task, &ntask);
- dev->attributes |= ISC_SOCKEVENTATTR_ATTACHED;
-
- if (!have_lock) {
- LOCK(&sock->lock);
- have_lock = ISC_TRUE;
- }
+ if ((flags & ISC_SOCKFLAG_NORETRY) == 0) {
+ isc_task_attach(task, &ntask);
+ dev->attributes |= ISC_SOCKEVENTATTR_ATTACHED;
+ if (!have_lock) {
+ LOCK(&sock->lock);
+ have_lock = ISC_TRUE;
+ }
- /*
- * Enqueue the request.
- */
- ISC_LIST_ENQUEUE(sock->send_list, dev, ev_link);
+ /*
+ * Enqueue the request.
+ */
+ ISC_LIST_ENQUEUE(sock->send_list, dev, ev_link);
- socket_log(sock, NULL, EVENT, NULL, 0, 0,
- "socket_send: event %p -> task %p",
- dev, ntask);
+ socket_log(sock, NULL, EVENT, NULL, 0, 0,
+ "socket_send: event %p -> task %p",
+ dev, ntask);
- if ((flags & ISC_SOCKFLAG_IMMEDIATE) != 0)
- result = ISC_R_INPROGRESS;
- break;
+ if ((flags & ISC_SOCKFLAG_IMMEDIATE) != 0)
+ result = ISC_R_INPROGRESS;
+ break;
+ }
case DOIO_SUCCESS:
break;
isc_result_t
isc_socket_filter(isc_socket_t *sock, const char *filter) {
- UNUSED(sock);
- UNUSED(filter);
+ UNUSED(sock);
+ UNUSED(filter);
- REQUIRE(VALID_SOCKET(sock));
+ REQUIRE(VALID_SOCKET(sock));
return (ISC_R_NOTIMPLEMENTED);
}
return (retstat);
}
-
UNLOCK(&sock->lock);
return (ISC_R_SUCCESS);
}
const char *msg;
stat = WSAGetLastError();
isc__strerror(stat, strbuf, sizeof(strbuf));
- msg = isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL,
+ msg = isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL,
ISC_MSG_FAILED, "failed");
UNEXPECTED_ERROR(__FILE__, __LINE__, "WSAEventSelect: %s: %s",
msg, strbuf);
const char *msg;
stat = WSAGetLastError();
isc__strerror(stat, strbuf, sizeof(strbuf));
- msg = isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL,
+ msg = isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL,
ISC_MSG_FAILED, "failed");
UNEXPECTED_ERROR(__FILE__, __LINE__,
"WSAEventSelect: %s: %s", msg, strbuf);