Browse Source

Support for non-overlapped sockets

By default windows creates sockets with the WSA_FLAG_OVERLAPPED flag set.
Because child processes don't expect to have overlapped stdio (it never happens)
it won't work with them.
v0.7.4-release
Bert Belder 14 years ago
parent
commit
bd62203d13
  1. 3
      TODO.win32
  2. 14
      src/node.cc
  3. 14
      src/node_net.cc
  4. 1
      src/platform_win32.cc
  5. 331
      src/platform_win32_winsock.cc
  6. 21
      src/platform_win32_winsock.h

3
TODO.win32

@ -10,7 +10,8 @@
- Child processes - Child processes
Should not be too hard using CreatePipe, CreateProcessW and GetExitCodeProcess. Should not be too hard using CreatePipe, CreateProcessW and GetExitCodeProcess.
Hooking up the child to a socket is tricky but it can be done (http://www.spinellis.gr/sw/unix/socketpipe/socketpipe-win.c) Hooking up a child process to a file handle can be done; hooking up to a normal socket won't work;
we'd need some sort of pump() mechanism.
Waiting for the child to exit is tricky, probably would require a wait thread to wait for the child, then ev_async notify. Waiting for the child to exit is tricky, probably would require a wait thread to wait for the child, then ev_async notify.
How can we distinguish between the exit code and exception number after calling GetExitCodeProcess? How can we distinguish between the exit code and exception number after calling GetExitCodeProcess?

14
src/node.cc

@ -17,7 +17,8 @@
#include "platform.h" #include "platform.h"
#ifdef __MINGW32__ #ifdef __MINGW32__
# include "platform_win32.h" /* winapi_perror() */ # include <platform_win32.h> /* winapi_perror() */
# include <platform_win32_winsock.h> /* wsa_init() */
#else // __POSIX__ #else // __POSIX__
# include <dlfcn.h> /* dlopen(), dlsym() */ # include <dlfcn.h> /* dlopen(), dlsym() */
# include <pwd.h> /* getpwnam() */ # include <pwd.h> /* getpwnam() */
@ -96,10 +97,6 @@ static ev_async eio_want_poll_notifier;
static ev_async eio_done_poll_notifier; static ev_async eio_done_poll_notifier;
static ev_idle eio_poller; static ev_idle eio_poller;
#ifdef __MINGW32__
WSAData winsockData;
#endif
// Buffer for getpwnam_r(), getgrpam_r() and other misc callers; keep this // Buffer for getpwnam_r(), getgrpam_r() and other misc callers; keep this
// scoped at file-level rather than method-level to avoid excess stack usage. // scoped at file-level rather than method-level to avoid excess stack usage.
static char getbuf[PATH_MAX + 1]; static char getbuf[PATH_MAX + 1];
@ -1952,11 +1949,8 @@ int Start(int argc, char *argv[]) {
#endif // __POSIX__ #endif // __POSIX__
#ifdef __MINGW32__ #ifdef __MINGW32__
// On windows, to use winsock it must be initialized // Initialize winsock and soem related caches
WORD winsockVersion = MAKEWORD(2, 2); wsa_init();
if (WSAStartup(winsockVersion, &winsockData)) {
winapi_perror("WSAStartup");
}
#endif // __MINGW32__ #endif // __MINGW32__
// Initialize the default ev loop. // Initialize the default ev loop.

14
src/node_net.cc

@ -716,11 +716,8 @@ static Handle<Value> Read(const Arguments& args) {
return ThrowException(ErrnoException(errno, "read")); return ThrowException(ErrnoException(errno, "read"));
} }
#else // __MINGW32__ #else // __MINGW32__
/* // read() doesn't work for overlapped sockets (the only usable
* read() _should_ work for sockets in mingw, but always gives EINVAL; // type of sockets) so recv() is used here.
* someone should really file a bug about it.
* We'll use recv() for sockets however, it's faster as well.
*/
ssize_t bytes_read = recv(_get_osfhandle(fd), (char*)buffer_data + off, len, 0); ssize_t bytes_read = recv(_get_osfhandle(fd), (char*)buffer_data + off, len, 0);
if (bytes_read < 0) { if (bytes_read < 0) {
@ -938,11 +935,8 @@ static Handle<Value> Write(const Arguments& args) {
return ThrowException(ErrnoException(errno, "write")); return ThrowException(ErrnoException(errno, "write"));
} }
#else // __MINGW32__ #else // __MINGW32__
/* // write() doesn't work for overlapped sockets (the only usable
* write() _should_ work for sockets in mingw, but always gives EINVAL; // type of sockets) so send() is used.
* someone should really file a bug about it.
* We'll use send() for sockets however, it's faster as well.
*/
ssize_t written = send(_get_osfhandle(fd), buffer_data + off, len, 0); ssize_t written = send(_get_osfhandle(fd), buffer_data + off, len, 0);
if (written < 0) { if (written < 0) {

1
src/platform_win32.cc

@ -7,6 +7,7 @@
#include <unistd.h> // getpagesize #include <unistd.h> // getpagesize
#include <windows.h> #include <windows.h>
#include "platform_win32_winsock.cc"
namespace node { namespace node {

331
src/platform_win32_winsock.cc

@ -0,0 +1,331 @@
/*
* This file contains all winsock-related stuff.
* Socketpair() for winsock is implemented here.
* There are also functions to create a non-overlapped socket (which windows normally doesn't do)
* and to create a socketpair that has one synchronous and one async socket.
* Synchronous sockets are required because async sockets can't be used by child processes.
*/
#include <windows.h>
#include <winsock2.h>
#include <mswsock.h>
#include <ws2tcpip.h>
#include <ws2spi.h>
#include <platform_win32_winsock.h>
namespace node {
/*
* Winsock version data goes here
*/
static WSAData winsock_info;
/*
* Cache for WSAPROTOCOL_INFOW structures for protocols used in node
* [0] TCP/IP
* [1] UDP/IP
* [2] TCP/IPv6
* [3] UDP/IPv6
*/
static WSAPROTOCOL_INFOW proto_info_cache[4];
/*
* Does the about the same as perror(), but for winsock errors
*/
void wsa_perror(const char *prefix) {
DWORD errorno = WSAGetLastError();
char *errmsg;
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
NULL, errorno, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&errmsg, 0, NULL);
// FormatMessage messages include a newline character
if (prefix) {
fprintf(stderr, "%s: %s", prefix, errmsg);
} else {
fputs(errmsg, stderr);
}
}
/*
* Retrieves a pointer to a WSAPROTOCOL_INFOW structure
* related to a certain winsock protocol from the cache
*/
inline static WSAPROTOCOL_INFOW *wsa_get_cached_proto_info(int af, int type, int proto) {
assert(proto == IPPROTO_IP
|| proto == IPPROTO_TCP
|| proto == IPPROTO_UDP);
switch (af) {
case AF_INET:
switch (type) {
case SOCK_STREAM:
return &proto_info_cache[0];
case SOCK_DGRAM:
return &proto_info_cache[1];
}
break;
case AF_INET6:
switch (type) {
case SOCK_STREAM:
return &proto_info_cache[2];
case SOCK_DGRAM:
return &proto_info_cache[3];
}
break;
}
WSASetLastError(WSAEPROTONOSUPPORT);
return NULL;
}
/*
* Creates a synchronous, non-overlapped socket.
* (The sockets that are created with socket() or accept() are always in overlapped mode.)
* Doubles the winsock api, e.g. returns a SOCKET handle, not an FD
*/
SOCKET wsa_sync_socket(int af, int type, int proto) {
WSAPROTOCOL_INFOW *protoInfo = wsa_get_cached_proto_info(af, type, proto);
if (protoInfo == NULL)
return INVALID_SOCKET;
return WSASocketW(af, type, proto, protoInfo, 0, 0);
}
/*
* Create a socketpair using the protocol specified
* This function uses winsock semantics, it returns SOCKET handles, not FDs
* Currently supports TCP/IPv4 socket pairs only
*/
int wsa_socketpair(int af, int type, int proto, SOCKET sock[2]) {
assert(af == AF_INET
&& type == SOCK_STREAM
&& (proto == IPPROTO_IP || proto == IPPROTO_TCP));
SOCKET listen_sock;
SOCKADDR_IN addr1;
SOCKADDR_IN addr2;
int addr1_len = sizeof (addr1);
int addr2_len = sizeof (addr2);
sock[1] = INVALID_SOCKET;
sock[2] = INVALID_SOCKET;
if ((listen_sock = socket(af, type, proto)) == INVALID_SOCKET)
goto error;
memset((void*)&addr1, 0, sizeof(addr1));
addr1.sin_family = af;
addr1.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
addr1.sin_port = 0;
if (bind(listen_sock, (SOCKADDR*)&addr1, addr1_len) == SOCKET_ERROR)
goto error;
if (getsockname(listen_sock, (SOCKADDR*)&addr1, &addr1_len) == SOCKET_ERROR)
goto error;
if (listen(listen_sock, 1))
goto error;
if ((sock[0] = socket(af, type, proto)) == INVALID_SOCKET)
goto error;
if (connect(sock[0], (SOCKADDR*)&addr1, addr1_len))
goto error;
if ((sock[1] = accept(listen_sock, 0, 0)) == INVALID_SOCKET)
goto error;
if (getpeername(sock[0], (SOCKADDR*)&addr1, &addr1_len) == INVALID_SOCKET)
goto error;
if (getsockname(sock[1], (SOCKADDR*)&addr2, &addr2_len) == INVALID_SOCKET)
goto error;
if (addr1_len != addr2_len
|| addr1.sin_addr.s_addr != addr2.sin_addr.s_addr
|| addr1.sin_port != addr2.sin_port)
goto error;
closesocket(listen_sock);
return 0;
error:
int error = WSAGetLastError();
if (listen_sock != INVALID_SOCKET)
closesocket(listen_sock);
if (sock[0] != INVALID_SOCKET)
closesocket(sock[0]);
if (sock[1] != INVALID_SOCKET)
closesocket(sock[1]);
WSASetLastError(error);
return SOCKET_ERROR;
}
/*
* Create a sync-async socketpair using the protocol specified,
* returning a synchronous socket and an asynchronous socket.
* Upon completion asyncSocket is opened with the WSA_FLAG_OVERLAPPED flag set,
* syncSocket won't have it set.
* Currently supports TCP/IPv4 socket pairs only
*/
int wsa_sync_async_socketpair(int af, int type, int proto, SOCKET *syncSocket, SOCKET *asyncSocket) {
assert(af == AF_INET
&& type == SOCK_STREAM
&& (proto == IPPROTO_IP || proto == IPPROTO_TCP));
SOCKET listen_sock;
SOCKET sock1 = INVALID_SOCKET;
SOCKET sock2 = INVALID_SOCKET;
SOCKADDR_IN addr1;
SOCKADDR_IN addr2;
int addr1_len = sizeof (addr1);
int addr2_len = sizeof (addr2);
if ((listen_sock = socket(af, type, proto)) == INVALID_SOCKET)
goto error;
memset((void*)&addr1, 0, sizeof(addr1));
addr1.sin_family = af;
addr1.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
addr1.sin_port = 0;
if (bind(listen_sock, (SOCKADDR*)&addr1, addr1_len) == SOCKET_ERROR)
goto error;
if (getsockname(listen_sock, (SOCKADDR*)&addr1, &addr1_len) == SOCKET_ERROR)
goto error;
if (listen(listen_sock, 1))
goto error;
if ((sock1 = wsa_sync_socket(af, type, proto)) == INVALID_SOCKET)
goto error;
if (connect(sock1, (SOCKADDR*)&addr1, addr1_len))
goto error;
if ((sock2 = accept(listen_sock, 0, 0)) == INVALID_SOCKET)
goto error;
if (getpeername(sock1, (SOCKADDR*)&addr1, &addr1_len) == INVALID_SOCKET)
goto error;
if (getsockname(sock2, (SOCKADDR*)&addr2, &addr2_len) == INVALID_SOCKET)
goto error;
if (addr1_len != addr2_len
|| addr1.sin_addr.s_addr != addr2.sin_addr.s_addr
|| addr1.sin_port != addr2.sin_port)
goto error;
closesocket(listen_sock);
*syncSocket = sock1;
*asyncSocket = sock2;
return 0;
error:
int error = WSAGetLastError();
if (listen_sock != INVALID_SOCKET)
closesocket(listen_sock);
if (sock1 != INVALID_SOCKET)
closesocket(sock1);
if (sock2 != INVALID_SOCKET)
closesocket(sock2);
WSASetLastError(error);
return SOCKET_ERROR;
}
/*
* Retrieves a WSAPROTOCOL_INFOW structure for a certain protocol
*/
static void wsa_get_proto_info(int af, int type, int proto, WSAPROTOCOL_INFOW *target) {
WSAPROTOCOL_INFOW *info_buffer = NULL;
unsigned long info_buffer_length = 0;
int protocol_count, i, error;
if (WSCEnumProtocols(NULL, NULL, &info_buffer_length, &error) != SOCKET_ERROR) {
error = WSAEOPNOTSUPP;
goto error;
}
info_buffer = (WSAPROTOCOL_INFOW *)malloc(info_buffer_length);
if ((protocol_count = WSCEnumProtocols(NULL, info_buffer, &info_buffer_length, &error)) == SOCKET_ERROR)
goto error;
for (i = 0; i < protocol_count; i++) {
if (af == info_buffer[i].iAddressFamily
&& type == info_buffer[i].iSocketType
&& proto == info_buffer[i].iProtocol
&& info_buffer[i].dwServiceFlags1 & XP1_IFS_HANDLES) {
memcpy(target, (WSAPROTOCOL_INFOW*)&info_buffer[i], sizeof(WSAPROTOCOL_INFOW));
free(info_buffer);
return;
}
}
error = WSAEPROTONOSUPPORT;
error:
WSASetLastError(error);
wsa_perror("Error obtaining winsock protocol information");
if (info_buffer != NULL) {
free(info_buffer);
}
}
/*
* Initializes (fills) the WSAPROTOCOL_INFOW structure cache
*/
static void wsa_init_proto_info_cache() {
WSAPROTOCOL_INFOW *cache = (WSAPROTOCOL_INFOW*)&proto_info_cache;
wsa_get_proto_info(AF_INET, SOCK_STREAM, IPPROTO_TCP, &proto_info_cache[0]);
wsa_get_proto_info(AF_INET, SOCK_DGRAM, IPPROTO_UDP, &proto_info_cache[1]);
wsa_get_proto_info(AF_INET6, SOCK_STREAM, IPPROTO_TCP, &proto_info_cache[2]);
wsa_get_proto_info(AF_INET6, SOCK_DGRAM, IPPROTO_UDP, &proto_info_cache[3]);
}
/*
* Initializes winsock and winsock-related stuff
*/
void wsa_init() {
WORD version = MAKEWORD(2, 2);
if (WSAStartup(version, &winsock_info)) {
wsa_perror("WSAStartup");
}
wsa_init_proto_info_cache();
}
} // namespace node

21
src/platform_win32_winsock.h

@ -0,0 +1,21 @@
#ifndef NODE_PLATFORM_WIN32_WINSOCK_H_
#define NODE_PLATFORM_WIN32_WINSOCK_H_
#include <windows.h>
#include <winsock.h>
namespace node {
void wsa_init();
void wsa_perror(const char* prefix = "");
SOCKET wsa_sync_socket(int af, int type, int proto);
int wsa_socketpair(int af, int type, int proto, SOCKET sock[2]);
int wsa_sync_async_socketpair(int af, int type, int proto, SOCKET *syncSocket, SOCKET *asyncSocket);
} // namespace node
#endif // NODE_PLATFORM_WIN32_WINSOCK_H_
Loading…
Cancel
Save