mirror of
https://github.com/signalwire/freeswitch.git
synced 2025-08-13 17:38:59 +00:00
merged new xmlrpc-c revision 1472 from https://xmlrpc-c.svn.sourceforge.net/svnroot/xmlrpc-c/trunk
git-svn-id: http://svn.freeswitch.org/svn/freeswitch/trunk@8545 d0543943-73ff-0310-b7d9-9358b9ac24b2
This commit is contained in:
940
libs/xmlrpc-c/lib/abyss/src/socket_win.c
Normal file
940
libs/xmlrpc-c/lib/abyss/src/socket_win.c
Normal file
@@ -0,0 +1,940 @@
|
||||
/*=============================================================================
|
||||
socket_win.c
|
||||
===============================================================================
|
||||
This is the implementation of TChanSwitch and TChannel
|
||||
for a Winsock socket.
|
||||
=============================================================================*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <winsock.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "xmlrpc_config.h"
|
||||
#include "xmlrpc-c/util_int.h"
|
||||
#include "xmlrpc-c/string_int.h"
|
||||
#include "mallocvar.h"
|
||||
#include "trace.h"
|
||||
#include "chanswitch.h"
|
||||
#include "channel.h"
|
||||
#include "socket.h"
|
||||
#include "xmlrpc-c/abyss.h"
|
||||
|
||||
#include "socket_win.h"
|
||||
|
||||
#ifndef socklen_t
|
||||
typedef int socklen_t;
|
||||
#endif
|
||||
|
||||
/* =============================================================
|
||||
Provided nice error strings, NOT available in system errors.
|
||||
============================================================= */
|
||||
|
||||
typedef struct tagSOCKERRS {
|
||||
int err; // WSAGetLastError() value
|
||||
char * desc; // description of error
|
||||
} SOCKERR;
|
||||
|
||||
/* could/should perhaps be by the actual call,
|
||||
but for now, just one big list, with some repeats
|
||||
*/
|
||||
|
||||
SOCKERR sSockErr[] = {
|
||||
{ WSANOTINITIALISED,
|
||||
"WSANOTINITIALISED - "
|
||||
"WSAStartup must be called before using this function." },
|
||||
{ WSAENETDOWN,
|
||||
"WSAENETDOWN - "
|
||||
"The network subsystem has failed." },
|
||||
{ WSAEACCES,
|
||||
"WSAEACCES - "
|
||||
"Attempt to connect datagram socket to broadcast address failed "
|
||||
"because setsockopt option SO_BROADCAST is not enabled." },
|
||||
{ WSAEADDRINUSE,
|
||||
"WSAEADDRINUSE - "
|
||||
"A process on the computer is already bound to the same fully-qualified "
|
||||
"address and the socket has not been marked to allow address reuse with "
|
||||
"SO_REUSEADDR. For example, the IP address and port are bound in the "
|
||||
"af_inet case). (See the SO_REUSEADDR socket option under setsockopt.)" },
|
||||
{ WSAEADDRNOTAVAIL,
|
||||
"WSAEADDRNOTAVAIL - "
|
||||
"The specified address is not a valid address for this computer." },
|
||||
{ WSAEFAULT,
|
||||
"WSAEFAULT - "
|
||||
"The name or namelen parameter is not a valid part of the user "
|
||||
"address space, the namelen parameter is too small, the name parameter "
|
||||
"contains an incorrect address format for the associated "
|
||||
"address family, or the first two bytes of the memory block "
|
||||
"specified by name does not match the address family associated with "
|
||||
"the socket descriptor s." },
|
||||
{ WSAEINPROGRESS,
|
||||
"WSAEINPROGRESS - "
|
||||
"A blocking Windows Sockets 1.1 call is in progress, or the "
|
||||
"service provider is still processing a callback function." },
|
||||
{ WSAEINVAL,
|
||||
"WSAEINVAL - "
|
||||
"The socket is already bound to an address." },
|
||||
{ WSAENOBUFS,
|
||||
"WSAENOBUFS - "
|
||||
"Not enough buffers available, too many connections." },
|
||||
{ WSAENOTSOCK,
|
||||
"WSAENOTSOCK - "
|
||||
"The descriptor is not a socket." },
|
||||
|
||||
// setsocketopt
|
||||
{ WSAENETRESET,
|
||||
"WSAENETRESET - "
|
||||
"Connection has timed out when SO_KEEPALIVE is set." },
|
||||
{ WSAENOPROTOOPT,
|
||||
"WSAENOPROTOOPT - "
|
||||
"The option is unknown or the specified provider "
|
||||
"or socket is not capable of implementing it "
|
||||
"(see SO_GROUP_PRIORITY limitations)." },
|
||||
{ WSAENOTCONN,
|
||||
"WSAENOTCONN - "
|
||||
"Connection has been reset when SO_KEEPALIVE is set." },
|
||||
|
||||
// WSAStartup
|
||||
{ WSASYSNOTREADY,
|
||||
"WSASYSNOTREADY - "
|
||||
"The underlying network subsystem is not ready for "
|
||||
"network communication." },
|
||||
{ WSAVERNOTSUPPORTED,
|
||||
"WSAVERNOTSUPPORTED - "
|
||||
"The version of Windows Sockets function requested is not provided "
|
||||
"by this particular Windows Sockets implementation." },
|
||||
{ WSAEINPROGRESS,
|
||||
"WSAEINPROGRESS - "
|
||||
"A blocking Windows Sockets 1.1 operation is in progress." },
|
||||
{ WSAEPROCLIM,
|
||||
"WSAEPROCLIM - "
|
||||
"Limit on the number of tasks allowed by the Windows Sockets "
|
||||
"implementation has been reached." },
|
||||
{ WSAEFAULT,
|
||||
"WSAEFAULT - "
|
||||
"The lpWSAData is not a valid pointer." },
|
||||
// listen
|
||||
{ WSANOTINITIALISED,
|
||||
"WSANOTINITIALISED - "
|
||||
"A successful WSAStartup call must occur before using this function." },
|
||||
{ WSAENETDOWN,
|
||||
"WSAENETDOWN - "
|
||||
"The network subsystem has failed." },
|
||||
{ WSAEADDRINUSE,
|
||||
"WSAEADDRINUSE - "
|
||||
"The socket's local address is already in use and the socket "
|
||||
"was not marked to allow address reuse with SO_REUSEADDR. "
|
||||
"This error usually occurs during execution of the bind function, "
|
||||
"but could be delayed until this function if the bind was to "
|
||||
"a partially wildcard address (involving ADDR_ANY) "
|
||||
"and if a specific address needs to be committed at the time "
|
||||
"of this function call." },
|
||||
{ WSAEINPROGRESS,
|
||||
"WSAEINPROGRESS - "
|
||||
"A blocking Windows Sockets 1.1 call is in progress, "
|
||||
"or the service provider is still processing a callback function." },
|
||||
{ WSAEINVAL,
|
||||
"WSAEINVAL - "
|
||||
"The socket has not been bound with bind." },
|
||||
{ WSAEISCONN,
|
||||
"WSAEISCONN - "
|
||||
"The socket is already connected." },
|
||||
{ WSAEMFILE,
|
||||
"WSAEMFILE - "
|
||||
"No more socket descriptors are available." },
|
||||
{ WSAENOBUFS,
|
||||
"WSAENOBUFS - "
|
||||
"No buffer space is available." },
|
||||
{ WSAENOTSOCK,
|
||||
"WSAENOTSOCK - "
|
||||
"The descriptor is not a socket." },
|
||||
{ WSAEOPNOTSUPP,
|
||||
"WSAEOPNOTSUPP - "
|
||||
"The referenced socket is not of a type that has a listen operation." },
|
||||
|
||||
// getpeername
|
||||
{ WSANOTINITIALISED,
|
||||
"WSANOTINITIALISED - "
|
||||
"A successful WSAStartup call must occur before using this function." },
|
||||
{ WSAENETDOWN,
|
||||
"WSAENETDOWN - "
|
||||
"The network subsystem has failed." },
|
||||
{ WSAEFAULT,
|
||||
"WSAEFAULT - "
|
||||
"The name or the namelen parameter is not a valid part of the "
|
||||
"user address space, or the namelen parameter is too small." },
|
||||
{ WSAEINPROGRESS,
|
||||
"WSAEINPROGRESS - "
|
||||
"A blocking Windows Sockets 1.1 call is in progress, "
|
||||
"or the service provider is still processing a callback function." },
|
||||
{ WSAENOTCONN,
|
||||
"WSAENOTCONN - "
|
||||
"The socket is not connected." },
|
||||
{ WSAENOTSOCK,
|
||||
"WSAENOTSOCK - "
|
||||
"The descriptor is not a socket." },
|
||||
|
||||
// accept
|
||||
{ WSANOTINITIALISED,
|
||||
"WSANOTINITIALISED - "
|
||||
"A successful WSAStartup call must occur before using this function." },
|
||||
{ WSAENETDOWN,
|
||||
"WSAENETDOWN - "
|
||||
"The network subsystem has failed." },
|
||||
{ WSAEFAULT,
|
||||
"WSAEFAULT - "
|
||||
"The addrlen parameter is too small or addr is not a valid part "
|
||||
"of the user address space." },
|
||||
{ WSAEINTR,
|
||||
"WSAEINTR - "
|
||||
"A blocking Windows Sockets 1.1 call was canceled through "
|
||||
"WSACancelBlockingCall." },
|
||||
{ WSAEINPROGRESS,
|
||||
"WSAEINPROGRESS - "
|
||||
"A blocking Windows Sockets 1.1 call is in progress, "
|
||||
"or the service provider is still processing a callback function." },
|
||||
{ WSAEINVAL,
|
||||
"WSAEINVAL - "
|
||||
"The listen function was not invoked prior to accept." },
|
||||
{ WSAEMFILE,
|
||||
"WSAEMFILE - "
|
||||
"The queue is nonempty upon entry to accept and "
|
||||
"there are no descriptors available." },
|
||||
{ WSAENOBUFS,
|
||||
"WSAENOBUFS - "
|
||||
"No buffer space is available." },
|
||||
{ WSAENOTSOCK,
|
||||
"WSAENOTSOCK - "
|
||||
"The descriptor is not a socket." },
|
||||
{ WSAEOPNOTSUPP,
|
||||
"WSAEOPNOTSUPP - "
|
||||
"The referenced socket is not a type that offers connection-oriented "
|
||||
"service." },
|
||||
{ WSAEWOULDBLOCK,
|
||||
"WSAEWOULDBLOCK - "
|
||||
"The socket is marked as nonblocking and no connections are present "
|
||||
"to be accepted." },
|
||||
|
||||
/* must be last entry */
|
||||
{ 0, 0 }
|
||||
};
|
||||
|
||||
|
||||
|
||||
static const char *
|
||||
getWSAError(int const wsaErrno) {
|
||||
|
||||
SOCKERR * pseP;
|
||||
|
||||
pseP = &sSockErr[0]; // initial value
|
||||
|
||||
while (pseP->desc) {
|
||||
if (pseP->err == wsaErrno)
|
||||
return pseP->desc;
|
||||
|
||||
++pseP;
|
||||
}
|
||||
|
||||
return "(no description available)";
|
||||
}
|
||||
|
||||
|
||||
|
||||
struct socketWin {
|
||||
/*----------------------------------------------------------------------------
|
||||
The properties/state of a TSocket unique to a Unix TSocket.
|
||||
-----------------------------------------------------------------------------*/
|
||||
SOCKET winsock;
|
||||
bool userSuppliedWinsock;
|
||||
/* 'socket' was supplied by the user; it belongs to him */
|
||||
};
|
||||
|
||||
static
|
||||
bool
|
||||
connected(SOCKET const fd) {
|
||||
/*----------------------------------------------------------------------------
|
||||
Return TRUE iff the socket on file descriptor 'fd' is in the connected
|
||||
state.
|
||||
If 'fd' does not identify a stream socket or we are unable to determine
|
||||
the state of the stream socket, the answer is "false".
|
||||
-----------------------------------------------------------------------------*/
|
||||
bool connected;
|
||||
struct sockaddr sockaddr;
|
||||
socklen_t nameLen;
|
||||
int rc;
|
||||
|
||||
nameLen = sizeof(sockaddr);
|
||||
|
||||
rc = getpeername(fd, &sockaddr, &nameLen);
|
||||
|
||||
if (rc == 0)
|
||||
connected = TRUE;
|
||||
else
|
||||
connected = FALSE;
|
||||
|
||||
return connected;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void
|
||||
SocketWinInit(const char ** const errorP) {
|
||||
|
||||
WORD wVersionRequested;
|
||||
WSADATA wsaData;
|
||||
int err;
|
||||
|
||||
wVersionRequested = MAKEWORD(1, 0);
|
||||
|
||||
err = WSAStartup(wVersionRequested, &wsaData);
|
||||
|
||||
if (err != 0) {
|
||||
int const lastError = WSAGetLastError();
|
||||
xmlrpc_asprintf(errorP, "WSAStartup() faild with error %d (%s)",
|
||||
lastError, getWSAError(lastError));
|
||||
} else
|
||||
*errorP = NULL;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void
|
||||
SocketWinTerm(void) {
|
||||
|
||||
WSACleanup();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*=============================================================================
|
||||
TChannel
|
||||
=============================================================================*/
|
||||
|
||||
static ChannelDestroyImpl channelDestroy;
|
||||
|
||||
static void
|
||||
channelDestroy(TChannel * const channelP) {
|
||||
|
||||
struct socketWin * const socketWinP = channelP->implP;
|
||||
|
||||
if (!socketWinP->userSuppliedWinsock)
|
||||
closesocket(socketWinP->winsock);
|
||||
|
||||
free(socketWinP);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static ChannelWriteImpl channelWrite;
|
||||
|
||||
static void
|
||||
channelWrite(TChannel * const channelP,
|
||||
const unsigned char * const buffer,
|
||||
uint32_t const len,
|
||||
bool * const failedP) {
|
||||
|
||||
struct socketWin * const socketWinP = channelP->implP;
|
||||
|
||||
size_t bytesLeft;
|
||||
bool error;
|
||||
|
||||
assert(sizeof(size_t) >= sizeof(len));
|
||||
|
||||
for (bytesLeft = len, error = FALSE;
|
||||
bytesLeft > 0 && !error;
|
||||
) {
|
||||
size_t const maxSend = (size_t)(-1) >> 1;
|
||||
|
||||
int rc;
|
||||
|
||||
rc = send(socketWinP->winsock, &buffer[len-bytesLeft],
|
||||
MIN(maxSend, bytesLeft), 0);
|
||||
|
||||
if (rc <= 0)
|
||||
/* 0 means connection closed; < 0 means severe error */
|
||||
error = TRUE;
|
||||
else
|
||||
bytesLeft -= rc;
|
||||
}
|
||||
*failedP = error;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static ChannelReadImpl channelRead;
|
||||
|
||||
static void
|
||||
channelRead(TChannel * const channelP,
|
||||
unsigned char * const buffer,
|
||||
uint32_t const bufferSize,
|
||||
uint32_t * const bytesReceivedP,
|
||||
bool * const failedP) {
|
||||
|
||||
struct socketWin * const socketWinP = channelP->implP;
|
||||
|
||||
int rc;
|
||||
rc = recv(socketWinP->winsock, buffer, bufferSize, 0);
|
||||
|
||||
if (rc < 0) {
|
||||
*failedP = TRUE;
|
||||
} else {
|
||||
*failedP = FALSE;
|
||||
*bytesReceivedP = rc;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
static ChannelWaitImpl channelWait;
|
||||
|
||||
static void
|
||||
channelWait(TChannel * const channelP,
|
||||
bool const waitForRead,
|
||||
bool const waitForWrite,
|
||||
uint32_t const timems,
|
||||
bool * const readyToReadP,
|
||||
bool * const readyToWriteP,
|
||||
bool * const failedP) {
|
||||
|
||||
struct socketWin * const socketWinP = channelP->implP;
|
||||
|
||||
fd_set rfds, wfds;
|
||||
TIMEVAL tv;
|
||||
bool failed, readRdy, writeRdy, timedOut;
|
||||
|
||||
FD_ZERO(&rfds);
|
||||
FD_ZERO(&wfds);
|
||||
|
||||
if (waitForRead)
|
||||
FD_SET(socketWinP->winsock, &rfds);
|
||||
|
||||
if (waitForWrite)
|
||||
FD_SET(socketWinP->winsock, &wfds);
|
||||
|
||||
tv.tv_sec = timems / 1000;
|
||||
tv.tv_usec = timems % 1000;
|
||||
|
||||
for (failed = FALSE, readRdy = FALSE, writeRdy = FALSE, timedOut = FALSE;
|
||||
!failed && !readRdy && !writeRdy && !timedOut;
|
||||
) {
|
||||
|
||||
int rc;
|
||||
|
||||
rc = select(socketWinP->winsock + 1, &rfds, &wfds, NULL,
|
||||
(timems == TIME_INFINITE ? NULL : &tv));
|
||||
|
||||
switch(rc) {
|
||||
case 0:
|
||||
timedOut = TRUE;
|
||||
break;
|
||||
case -1: /* socket error */
|
||||
if (errno != EINTR)
|
||||
failed = TRUE;
|
||||
break;
|
||||
default:
|
||||
if (FD_ISSET(socketWinP->winsock, &rfds))
|
||||
readRdy = TRUE;
|
||||
if (FD_ISSET(socketWinP->winsock, &wfds))
|
||||
writeRdy = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
if (failedP)
|
||||
*failedP = failed;
|
||||
if (readyToReadP)
|
||||
*readyToReadP = readRdy;
|
||||
if (readyToWriteP)
|
||||
*readyToWriteP = writeRdy;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static ChannelInterruptImpl channelInterrupt;
|
||||
|
||||
static void
|
||||
channelInterrupt(TChannel * const channelP) {
|
||||
/*----------------------------------------------------------------------------
|
||||
Interrupt any waiting that a thread might be doing in channelWait()
|
||||
now or in the future.
|
||||
|
||||
Actually, this is just a no-op because we don't yet know how to
|
||||
accomplish that.
|
||||
-----------------------------------------------------------------------------*/
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
void
|
||||
ChannelWinGetPeerName(TChannel * const channelP,
|
||||
struct sockaddr_in * const inAddrP,
|
||||
const char ** const errorP) {
|
||||
|
||||
struct socketWin * const socketWinP = channelP->implP;
|
||||
|
||||
socklen_t addrlen;
|
||||
int rc;
|
||||
struct sockaddr sockAddr;
|
||||
|
||||
addrlen = sizeof(sockAddr);
|
||||
|
||||
rc = getpeername(socketWinP->winsock, &sockAddr, &addrlen);
|
||||
|
||||
if (rc != 0) {
|
||||
int const lastError = WSAGetLastError();
|
||||
xmlrpc_asprintf(errorP, "getpeername() failed. WSAERROR = %d (%s)",
|
||||
lastError, getWSAError(lastError));
|
||||
} else {
|
||||
if (addrlen != sizeof(sockAddr))
|
||||
xmlrpc_asprintf(errorP, "getpeername() returned a socket address "
|
||||
"of the wrong size: %u. Expected %u",
|
||||
addrlen, sizeof(sockAddr));
|
||||
else {
|
||||
if (sockAddr.sa_family != AF_INET)
|
||||
xmlrpc_asprintf(errorP,
|
||||
"Socket does not use the Inet (IP) address "
|
||||
"family. Instead it uses family %d",
|
||||
sockAddr.sa_family);
|
||||
else {
|
||||
*inAddrP = *(struct sockaddr_in *)&sockAddr;
|
||||
|
||||
*errorP = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
static ChannelFormatPeerInfoImpl channelFormatPeerInfo;
|
||||
|
||||
static void
|
||||
channelFormatPeerInfo(TChannel * const channelP,
|
||||
const char ** const peerStringP) {
|
||||
|
||||
struct socketWin * const socketWinP = channelP->implP;
|
||||
|
||||
struct sockaddr sockaddr;
|
||||
socklen_t sockaddrLen;
|
||||
int rc;
|
||||
|
||||
sockaddrLen = sizeof(sockaddr);
|
||||
|
||||
rc = getpeername(socketWinP->winsock, &sockaddr, &sockaddrLen);
|
||||
|
||||
if (rc != 0) {
|
||||
int const lastError = WSAGetLastError();
|
||||
xmlrpc_asprintf(peerStringP, "?? getpeername() failed. "
|
||||
"WSAERROR %d (%s)",
|
||||
lastError, getWSAError(lastError));
|
||||
} else {
|
||||
switch (sockaddr.sa_family) {
|
||||
case AF_INET: {
|
||||
struct sockaddr_in * const sockaddrInP =
|
||||
(struct sockaddr_in *) &sockaddr;
|
||||
if (sockaddrLen < sizeof(*sockaddrInP))
|
||||
xmlrpc_asprintf(peerStringP, "??? getpeername() returned "
|
||||
"the wrong size");
|
||||
else {
|
||||
unsigned char * const ipaddr = (unsigned char *)
|
||||
&sockaddrInP->sin_addr.s_addr;
|
||||
xmlrpc_asprintf(peerStringP, "%u.%u.%u.%u:%hu",
|
||||
ipaddr[0], ipaddr[1], ipaddr[2], ipaddr[3],
|
||||
sockaddrInP->sin_port);
|
||||
}
|
||||
} break;
|
||||
default:
|
||||
xmlrpc_asprintf(peerStringP, "??? AF=%u", sockaddr.sa_family);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
static struct TChannelVtbl const channelVtbl = {
|
||||
&channelDestroy,
|
||||
&channelWrite,
|
||||
&channelRead,
|
||||
&channelWait,
|
||||
&channelInterrupt,
|
||||
&channelFormatPeerInfo,
|
||||
};
|
||||
|
||||
|
||||
|
||||
static void
|
||||
makeChannelFromWinsock(SOCKET const winsock,
|
||||
TChannel ** const channelPP,
|
||||
const char ** const errorP) {
|
||||
|
||||
struct socketWin * socketWinP;
|
||||
|
||||
MALLOCVAR(socketWinP);
|
||||
|
||||
if (socketWinP == NULL)
|
||||
xmlrpc_asprintf(errorP, "Unable to allocate memory for Windows "
|
||||
"socket descriptor");
|
||||
else {
|
||||
TChannel * channelP;
|
||||
|
||||
socketWinP->winsock = winsock;
|
||||
socketWinP->userSuppliedWinsock = TRUE;
|
||||
|
||||
ChannelCreate(&channelVtbl, socketWinP, &channelP);
|
||||
|
||||
if (channelP == NULL)
|
||||
xmlrpc_asprintf(errorP, "Unable to allocate memory for "
|
||||
"channel descriptor.");
|
||||
else {
|
||||
*channelPP = channelP;
|
||||
*errorP = NULL;
|
||||
}
|
||||
if (*errorP)
|
||||
free(socketWinP);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void
|
||||
makeChannelInfo(struct abyss_win_chaninfo ** const channelInfoPP,
|
||||
struct sockaddr const peerAddr,
|
||||
socklen_t const peerAddrLen,
|
||||
const char ** const errorP) {
|
||||
|
||||
struct abyss_win_chaninfo * channelInfoP;
|
||||
|
||||
MALLOCVAR(channelInfoP);
|
||||
|
||||
if (channelInfoP == NULL)
|
||||
xmlrpc_asprintf(errorP, "Unable to allocate memory");
|
||||
else {
|
||||
channelInfoP->peerAddrLen = peerAddrLen;
|
||||
channelInfoP->peerAddr = peerAddr;
|
||||
|
||||
*channelInfoPP = channelInfoP;
|
||||
|
||||
*errorP = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void
|
||||
ChannelWinCreateWinsock(SOCKET const fd,
|
||||
TChannel ** const channelPP,
|
||||
struct abyss_win_chaninfo ** const channelInfoPP,
|
||||
const char ** const errorP) {
|
||||
|
||||
struct sockaddr peerAddr;
|
||||
socklen_t peerAddrLen;
|
||||
int rc;
|
||||
|
||||
peerAddrLen = sizeof(peerAddrLen);
|
||||
|
||||
rc = getpeername(fd, &peerAddr, &peerAddrLen);
|
||||
|
||||
if (rc != 0) {
|
||||
int const lastError = WSAGetLastError();
|
||||
if (lastError == WSAENOTCONN) {
|
||||
/* NOTE: This specific string 'not in connected' is
|
||||
required by one of the rpctest suite items, in abyss.c
|
||||
(line 186), hence the separation of the error messages
|
||||
in this case ...
|
||||
*/
|
||||
xmlrpc_asprintf(errorP, "Socket on file descriptor %d "
|
||||
"is not in connected state. WSAERROR = %d (%s)",
|
||||
fd, lastError, getWSAError(lastError));
|
||||
} else
|
||||
xmlrpc_asprintf(errorP, "getpeername() failed. WSAERROR = %d (%s)",
|
||||
lastError, getWSAError(lastError));
|
||||
} else {
|
||||
makeChannelInfo(channelInfoPP, peerAddr, peerAddrLen, errorP);
|
||||
if (!*errorP) {
|
||||
makeChannelFromWinsock(fd, channelPP, errorP);
|
||||
|
||||
if (*errorP)
|
||||
free(*channelInfoPP);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*=============================================================================
|
||||
TChanSwitch
|
||||
=============================================================================*/
|
||||
|
||||
static SwitchDestroyImpl chanSwitchDestroy;
|
||||
|
||||
void
|
||||
chanSwitchDestroy(TChanSwitch * const chanSwitchP) {
|
||||
|
||||
struct socketWin * const socketWinP = chanSwitchP->implP;
|
||||
|
||||
if (!socketWinP->userSuppliedWinsock)
|
||||
closesocket(socketWinP->winsock);
|
||||
|
||||
free(socketWinP);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static SwitchListenImpl chanSwitchListen;
|
||||
|
||||
static void
|
||||
chanSwitchListen(TChanSwitch * const chanSwitchP,
|
||||
uint32_t const backlog,
|
||||
const char ** const errorP) {
|
||||
|
||||
struct socketWin * const socketWinP = chanSwitchP->implP;
|
||||
|
||||
int32_t const minus1 = -1;
|
||||
|
||||
int rc;
|
||||
|
||||
/* Disable the Nagle algorithm to make persistant connections faster */
|
||||
|
||||
setsockopt(socketWinP->winsock, IPPROTO_TCP, TCP_NODELAY,
|
||||
(const char *)&minus1, sizeof(minus1));
|
||||
|
||||
rc = listen(socketWinP->winsock, backlog);
|
||||
|
||||
if (rc != 0) {
|
||||
int const lastError = WSAGetLastError();
|
||||
xmlrpc_asprintf(errorP, "setsockopt() failed with WSAERROR %d (%s)",
|
||||
lastError, getWSAError(lastError));
|
||||
} else
|
||||
*errorP = NULL;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static SwitchAcceptImpl chanSwitchAccept;
|
||||
|
||||
static void
|
||||
chanSwitchAccept(TChanSwitch * const chanSwitchP,
|
||||
TChannel ** const channelPP,
|
||||
void ** const channelInfoPP,
|
||||
const char ** const errorP) {
|
||||
/*----------------------------------------------------------------------------
|
||||
Accept a connection via the channel switch *chanSwitchP. Return as
|
||||
*channelPP the channel for the accepted connection.
|
||||
|
||||
If no connection is waiting at *chanSwitchP, wait until one is.
|
||||
|
||||
If we receive a signal while waiting, return immediately with
|
||||
*channelPP == NULL.
|
||||
-----------------------------------------------------------------------------*/
|
||||
struct socketWin * const listenSocketP = chanSwitchP->implP;
|
||||
|
||||
bool interrupted;
|
||||
TChannel * channelP;
|
||||
|
||||
interrupted = FALSE; /* Haven't been interrupted yet */
|
||||
channelP = NULL; /* No connection yet */
|
||||
*errorP = NULL; /* No error yet */
|
||||
|
||||
while (!channelP && !*errorP && !interrupted) {
|
||||
struct sockaddr peerAddr;
|
||||
socklen_t size = sizeof(peerAddr);
|
||||
int rc;
|
||||
|
||||
rc = accept(listenSocketP->winsock, &peerAddr, &size);
|
||||
|
||||
if (rc >= 0) {
|
||||
int const acceptedWinsock = rc;
|
||||
struct socketWin * acceptedSocketP;
|
||||
|
||||
MALLOCVAR(acceptedSocketP);
|
||||
|
||||
if (!acceptedSocketP)
|
||||
xmlrpc_asprintf(errorP, "Unable to allocate memory");
|
||||
else {
|
||||
acceptedSocketP->winsock = acceptedWinsock;
|
||||
acceptedSocketP->userSuppliedWinsock = FALSE;
|
||||
|
||||
*channelInfoPP = NULL;
|
||||
|
||||
ChannelCreate(&channelVtbl, acceptedSocketP, &channelP);
|
||||
if (!channelP)
|
||||
xmlrpc_asprintf(errorP,
|
||||
"Failed to create TChannel object.");
|
||||
else
|
||||
*errorP = NULL;
|
||||
|
||||
if (*errorP)
|
||||
free(acceptedSocketP);
|
||||
}
|
||||
if (*errorP)
|
||||
closesocket(acceptedWinsock);
|
||||
} else if (errno == EINTR)
|
||||
interrupted = TRUE;
|
||||
else
|
||||
xmlrpc_asprintf(errorP, "accept() failed, errno = %d (%s)",
|
||||
errno, strerror(errno));
|
||||
}
|
||||
*channelPP = channelP;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static SwitchInterruptImpl chanSwitchInterrupt;
|
||||
|
||||
static void
|
||||
chanSwitchInterrupt(TChanSwitch * const chanSwitchP) {
|
||||
/*----------------------------------------------------------------------------
|
||||
Interrupt any waiting that a thread might be doing in chanSwitchAccept()
|
||||
now or in the future.
|
||||
|
||||
Actually, this is just a no-op because we don't yet know how to
|
||||
accomplish that.
|
||||
-----------------------------------------------------------------------------*/
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
static struct TChanSwitchVtbl const chanSwitchVtbl = {
|
||||
&chanSwitchDestroy,
|
||||
&chanSwitchListen,
|
||||
&chanSwitchAccept,
|
||||
&chanSwitchInterrupt,
|
||||
};
|
||||
|
||||
|
||||
|
||||
static void
|
||||
setSocketOptions(SOCKET const fd,
|
||||
const char ** const errorP) {
|
||||
|
||||
int32_t const n = 1;
|
||||
|
||||
int rc;
|
||||
|
||||
rc = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char*)&n, sizeof(n));
|
||||
|
||||
if (rc != 0) {
|
||||
int const lastError = WSAGetLastError();
|
||||
xmlrpc_asprintf(errorP, "Failed to set socket options. "
|
||||
"setsockopt() failed with WSAERROR %d (%s)",
|
||||
lastError, getWSAError(lastError));
|
||||
} else
|
||||
*errorP = NULL;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void
|
||||
bindSocketToPort(SOCKET const winsock,
|
||||
struct in_addr * const addrP,
|
||||
uint16_t const portNumber,
|
||||
const char ** const errorP) {
|
||||
|
||||
struct sockaddr_in name;
|
||||
int rc;
|
||||
|
||||
ZeroMemory(&name, sizeof(name));
|
||||
name.sin_family = AF_INET;
|
||||
name.sin_port = htons(portNumber);
|
||||
if (addrP)
|
||||
name.sin_addr = *addrP;
|
||||
|
||||
rc = bind(winsock, (struct sockaddr *)&name, sizeof(name));
|
||||
|
||||
if (rc != 0) {
|
||||
int const lastError = WSAGetLastError();
|
||||
xmlrpc_asprintf(errorP, "Unable to bind socket to port number %u. "
|
||||
"bind() failed with WSAERROR %i (%s)",
|
||||
portNumber, lastError, getWSAError(lastError));
|
||||
} else
|
||||
*errorP = NULL;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void
|
||||
ChanSwitchWinCreate(uint16_t const portNumber,
|
||||
TChanSwitch ** const chanSwitchPP,
|
||||
const char ** const errorP) {
|
||||
/*----------------------------------------------------------------------------
|
||||
Create a Winsock-based channel switch.
|
||||
|
||||
Set the socket's local address so that a subsequent "listen" will listen
|
||||
on all IP addresses, port number 'portNumber'.
|
||||
-----------------------------------------------------------------------------*/
|
||||
struct socketWin * socketWinP;
|
||||
|
||||
MALLOCVAR(socketWinP);
|
||||
|
||||
if (!socketWinP)
|
||||
xmlrpc_asprintf(errorP, "Unable to allocate memory for Windows socket "
|
||||
"descriptor structure.");
|
||||
else {
|
||||
SOCKET winsock;
|
||||
|
||||
winsock = socket(AF_INET, SOCK_STREAM, 0);
|
||||
|
||||
if (winsock == 0 || winsock == INVALID_SOCKET) {
|
||||
int const lastError = WSAGetLastError();
|
||||
xmlrpc_asprintf(errorP, "socket() failed with WSAERROR %d (%s)",
|
||||
lastError, getWSAError(lastError));
|
||||
} else {
|
||||
socketWinP->winsock = winsock;
|
||||
socketWinP->userSuppliedWinsock = FALSE;
|
||||
|
||||
setSocketOptions(socketWinP->winsock, errorP);
|
||||
if (!*errorP) {
|
||||
bindSocketToPort(socketWinP->winsock, NULL, portNumber,
|
||||
errorP);
|
||||
if (!*errorP)
|
||||
ChanSwitchCreate(&chanSwitchVtbl, socketWinP,
|
||||
chanSwitchPP);
|
||||
}
|
||||
|
||||
if (*errorP)
|
||||
closesocket(winsock);
|
||||
}
|
||||
if (*errorP)
|
||||
free(socketWinP);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void
|
||||
ChanSwitchWinCreateWinsock(SOCKET const winsock,
|
||||
TChanSwitch ** const chanSwitchPP,
|
||||
const char ** const errorP) {
|
||||
|
||||
struct socketWin * socketWinP;
|
||||
|
||||
if (connected(winsock))
|
||||
xmlrpc_asprintf(errorP, "Socket is in connected state.");
|
||||
else {
|
||||
MALLOCVAR(socketWinP);
|
||||
|
||||
if (socketWinP == NULL)
|
||||
xmlrpc_asprintf(errorP, "unable to allocate memory for Windows "
|
||||
"socket descriptor.");
|
||||
else {
|
||||
TChanSwitch * chanSwitchP;
|
||||
|
||||
socketWinP->winsock = winsock;
|
||||
socketWinP->userSuppliedWinsock = TRUE;
|
||||
|
||||
ChanSwitchCreate(&chanSwitchVtbl, socketWinP, &chanSwitchP);
|
||||
|
||||
if (chanSwitchP == NULL)
|
||||
xmlrpc_asprintf(errorP, "Unable to allocate memory for "
|
||||
"channel switch descriptor");
|
||||
else {
|
||||
*chanSwitchPP = chanSwitchP;
|
||||
*errorP = NULL;
|
||||
}
|
||||
if (*errorP)
|
||||
free(socketWinP);
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user