#include "tclWinInt.h"
static int initialized = 0;
static int hostnameInitialized = 0;
static char hostname[255];
TCL_DECLARE_MUTEX(socketMutex)
static struct {
HINSTANCE hInstance;
SOCKET (PASCAL FAR *accept)(SOCKET s, struct sockaddr FAR *addr,
int FAR *addrlen);
int (PASCAL FAR *bind)(SOCKET s, const struct sockaddr FAR *addr,
int namelen);
int (PASCAL FAR *closesocket)(SOCKET s);
int (PASCAL FAR *connect)(SOCKET s, const struct sockaddr FAR *name,
int namelen);
int (PASCAL FAR *ioctlsocket)(SOCKET s, long cmd, u_long FAR *argp);
int (PASCAL FAR *getsockopt)(SOCKET s, int level, int optname,
char FAR * optval, int FAR *optlen);
u_short (PASCAL FAR *htons)(u_short hostshort);
unsigned long (PASCAL FAR *inet_addr)(const char FAR * cp);
char FAR * (PASCAL FAR *inet_ntoa)(struct in_addr in);
int (PASCAL FAR *listen)(SOCKET s, int backlog);
u_short (PASCAL FAR *ntohs)(u_short netshort);
int (PASCAL FAR *recv)(SOCKET s, char FAR * buf, int len, int flags);
int (PASCAL FAR *select)(int nfds, fd_set FAR * readfds,
fd_set FAR * writefds, fd_set FAR * exceptfds,
const struct timeval FAR * tiemout);
int (PASCAL FAR *send)(SOCKET s, const char FAR * buf, int len, int flags);
int (PASCAL FAR *setsockopt)(SOCKET s, int level, int optname,
const char FAR * optval, int optlen);
int (PASCAL FAR *shutdown)(SOCKET s, int how);
SOCKET (PASCAL FAR *socket)(int af, int type, int protocol);
struct hostent FAR * (PASCAL FAR *gethostbyname)(const char FAR * name);
struct hostent FAR * (PASCAL FAR *gethostbyaddr)(const char FAR *addr,
int addrlen, int addrtype);
int (PASCAL FAR *gethostname)(char FAR * name, int namelen);
int (PASCAL FAR *getpeername)(SOCKET sock, struct sockaddr FAR *name,
int FAR *namelen);
struct servent FAR * (PASCAL FAR *getservbyname)(const char FAR * name,
const char FAR * proto);
int (PASCAL FAR *getsockname)(SOCKET sock, struct sockaddr FAR *name,
int FAR *namelen);
int (PASCAL FAR *WSAStartup)(WORD wVersionRequired, LPWSADATA lpWSAData);
int (PASCAL FAR *WSACleanup)(void);
int (PASCAL FAR *WSAGetLastError)(void);
int (PASCAL FAR *WSAAsyncSelect)(SOCKET s, HWND hWnd, u_int wMsg,
long lEvent);
} winSock;
#define SOCKET_MESSAGE WM_USER+1
#define SOCKET_SELECT WM_USER+2
#define SOCKET_TERMINATE WM_USER+3
#define SELECT TRUE
#define UNSELECT FALSE
typedef struct SocketInfo {
Tcl_Channel channel;
SOCKET socket;
int flags;
int watchEvents;
int readyEvents;
int selectEvents;
int acceptEventCount;
Tcl_TcpAcceptProc *acceptProc;
ClientData acceptProcData;
int lastError;
struct SocketInfo *nextPtr;
} SocketInfo;
typedef struct SocketEvent {
Tcl_Event header;
SOCKET socket;
} SocketEvent;
#define TCP_BUFFER_SIZE 4096
#define SOCKET_ASYNC (1<<0)
#define SOCKET_EOF (1<<1)
#define SOCKET_ASYNC_CONNECT (1<<2)
#define SOCKET_PENDING (1<<3)
typedef struct ThreadSpecificData {
HWND hwnd;
HANDLE socketThread;
Tcl_ThreadId threadId;
HANDLE readyEvent;
HANDLE socketListLock;
SocketInfo *socketList;
} ThreadSpecificData;
static Tcl_ThreadDataKey dataKey;
static WNDCLASSA windowClass;
static SocketInfo * CreateSocket _ANSI_ARGS_((Tcl_Interp *interp,
int port, CONST char *host, int server,
CONST char *myaddr, int myport, int async));
static int CreateSocketAddress _ANSI_ARGS_(
(struct sockaddr_in *sockaddrPtr,
CONST char *host, int port));
static void InitSockets _ANSI_ARGS_((void));
static SocketInfo * NewSocketInfo _ANSI_ARGS_((SOCKET socket));
static void SocketCheckProc _ANSI_ARGS_((ClientData clientData,
int flags));
static int SocketEventProc _ANSI_ARGS_((Tcl_Event *evPtr,
int flags));
static void SocketExitHandler _ANSI_ARGS_((ClientData clientData));
static LRESULT CALLBACK SocketProc _ANSI_ARGS_((HWND hwnd, UINT message,
WPARAM wParam, LPARAM lParam));
static void SocketSetupProc _ANSI_ARGS_((ClientData clientData,
int flags));
static void SocketThreadExitHandler _ANSI_ARGS_((ClientData clientData));
static int SocketsEnabled _ANSI_ARGS_((void));
static void TcpAccept _ANSI_ARGS_((SocketInfo *infoPtr));
static int TcpBlockProc _ANSI_ARGS_((ClientData instanceData,
int mode));
static int TcpCloseProc _ANSI_ARGS_((ClientData instanceData,
Tcl_Interp *interp));
static int TcpGetOptionProc _ANSI_ARGS_((ClientData instanceData,
Tcl_Interp *interp, CONST char *optionName,
Tcl_DString *optionValue));
static int TcpInputProc _ANSI_ARGS_((ClientData instanceData,
char *buf, int toRead, int *errorCode));
static int TcpOutputProc _ANSI_ARGS_((ClientData instanceData,
CONST char *buf, int toWrite, int *errorCode));
static void TcpWatchProc _ANSI_ARGS_((ClientData instanceData,
int mask));
static int TcpGetHandleProc _ANSI_ARGS_((ClientData instanceData,
int direction, ClientData *handlePtr));
static int WaitForSocketEvent _ANSI_ARGS_((SocketInfo *infoPtr,
int events, int *errorCodePtr));
static DWORD WINAPI SocketThread _ANSI_ARGS_((LPVOID arg));
static Tcl_ChannelType tcpChannelType = {
"tcp",
TCL_CHANNEL_VERSION_2,
TcpCloseProc,
TcpInputProc,
TcpOutputProc,
NULL,
NULL,
TcpGetOptionProc,
TcpWatchProc,
TcpGetHandleProc,
NULL,
TcpBlockProc,
NULL,
NULL,
};
#define WSA_VERSION_REQD MAKEWORD(1,1)
static void
InitSockets()
{
DWORD id;
WSADATA wsaData;
ThreadSpecificData *tsdPtr =
(ThreadSpecificData *)TclThreadDataKeyGet(&dataKey);
if (! initialized) {
initialized = 1;
Tcl_CreateExitHandler(SocketExitHandler, (ClientData) NULL);
winSock.hInstance = LoadLibraryA("wsock32.dll");
if (!SocketsEnabled()) {
return;
}
winSock.accept = (SOCKET (PASCAL FAR *)(SOCKET s,
struct sockaddr FAR *addr, int FAR *addrlen))
GetProcAddress(winSock.hInstance, "accept");
winSock.bind = (int (PASCAL FAR *)(SOCKET s,
const struct sockaddr FAR *addr, int namelen))
GetProcAddress(winSock.hInstance, "bind");
winSock.closesocket = (int (PASCAL FAR *)(SOCKET s))
GetProcAddress(winSock.hInstance, "closesocket");
winSock.connect = (int (PASCAL FAR *)(SOCKET s,
const struct sockaddr FAR *name, int namelen))
GetProcAddress(winSock.hInstance, "connect");
winSock.ioctlsocket = (int (PASCAL FAR *)(SOCKET s, long cmd,
u_long FAR *argp))
GetProcAddress(winSock.hInstance, "ioctlsocket");
winSock.getsockopt = (int (PASCAL FAR *)(SOCKET s,
int level, int optname, char FAR * optval, int FAR *optlen))
GetProcAddress(winSock.hInstance, "getsockopt");
winSock.htons = (u_short (PASCAL FAR *)(u_short hostshort))
GetProcAddress(winSock.hInstance, "htons");
winSock.inet_addr = (unsigned long (PASCAL FAR *)(const char FAR *cp))
GetProcAddress(winSock.hInstance, "inet_addr");
winSock.inet_ntoa = (char FAR * (PASCAL FAR *)(struct in_addr in))
GetProcAddress(winSock.hInstance, "inet_ntoa");
winSock.listen = (int (PASCAL FAR *)(SOCKET s, int backlog))
GetProcAddress(winSock.hInstance, "listen");
winSock.ntohs = (u_short (PASCAL FAR *)(u_short netshort))
GetProcAddress(winSock.hInstance, "ntohs");
winSock.recv = (int (PASCAL FAR *)(SOCKET s, char FAR * buf,
int len, int flags)) GetProcAddress(winSock.hInstance, "recv");
winSock.select = (int (PASCAL FAR *)(int nfds, fd_set FAR * readfds,
fd_set FAR * writefds, fd_set FAR * exceptfds,
const struct timeval FAR * tiemout))
GetProcAddress(winSock.hInstance, "select");
winSock.send = (int (PASCAL FAR *)(SOCKET s, const char FAR * buf,
int len, int flags)) GetProcAddress(winSock.hInstance, "send");
winSock.setsockopt = (int (PASCAL FAR *)(SOCKET s, int level,
int optname, const char FAR * optval, int optlen))
GetProcAddress(winSock.hInstance, "setsockopt");
winSock.shutdown = (int (PASCAL FAR *)(SOCKET s, int how))
GetProcAddress(winSock.hInstance, "shutdown");
winSock.socket = (SOCKET (PASCAL FAR *)(int af, int type,
int protocol)) GetProcAddress(winSock.hInstance, "socket");
winSock.gethostbyaddr = (struct hostent FAR * (PASCAL FAR *)
(const char FAR *addr, int addrlen, int addrtype))
GetProcAddress(winSock.hInstance, "gethostbyaddr");
winSock.gethostbyname = (struct hostent FAR * (PASCAL FAR *)
(const char FAR *name))
GetProcAddress(winSock.hInstance, "gethostbyname");
winSock.gethostname = (int (PASCAL FAR *)(char FAR * name,
int namelen)) GetProcAddress(winSock.hInstance, "gethostname");
winSock.getpeername = (int (PASCAL FAR *)(SOCKET sock,
struct sockaddr FAR *name, int FAR *namelen))
GetProcAddress(winSock.hInstance, "getpeername");
winSock.getservbyname = (struct servent FAR * (PASCAL FAR *)
(const char FAR * name, const char FAR * proto))
GetProcAddress(winSock.hInstance, "getservbyname");
winSock.getsockname = (int (PASCAL FAR *)(SOCKET sock,
struct sockaddr FAR *name, int FAR *namelen))
GetProcAddress(winSock.hInstance, "getsockname");
winSock.WSAStartup = (int (PASCAL FAR *)(WORD wVersionRequired,
LPWSADATA lpWSAData)) GetProcAddress(winSock.hInstance, "WSAStartup");
winSock.WSACleanup = (int (PASCAL FAR *)(void))
GetProcAddress(winSock.hInstance, "WSACleanup");
winSock.WSAGetLastError = (int (PASCAL FAR *)(void))
GetProcAddress(winSock.hInstance, "WSAGetLastError");
winSock.WSAAsyncSelect = (int (PASCAL FAR *)(SOCKET s, HWND hWnd,
u_int wMsg, long lEvent))
GetProcAddress(winSock.hInstance, "WSAAsyncSelect");
if ((winSock.hInstance == NULL) ||
(winSock.accept == NULL) ||
(winSock.bind == NULL) ||
(winSock.closesocket == NULL) ||
(winSock.connect == NULL) ||
(winSock.ioctlsocket == NULL) ||
(winSock.getsockopt == NULL) ||
(winSock.htons == NULL) ||
(winSock.inet_addr == NULL) ||
(winSock.inet_ntoa == NULL) ||
(winSock.listen == NULL) ||
(winSock.ntohs == NULL) ||
(winSock.recv == NULL) ||
(winSock.select == NULL) ||
(winSock.send == NULL) ||
(winSock.setsockopt == NULL) ||
(winSock.socket == NULL) ||
(winSock.gethostbyname == NULL) ||
(winSock.gethostbyaddr == NULL) ||
(winSock.gethostname == NULL) ||
(winSock.getpeername == NULL) ||
(winSock.getservbyname == NULL) ||
(winSock.getsockname == NULL) ||
(winSock.WSAStartup == NULL) ||
(winSock.WSACleanup == NULL) ||
(winSock.WSAGetLastError == NULL) ||
(winSock.WSAAsyncSelect == NULL)) {
goto unloadLibrary;
}
windowClass.style = 0;
windowClass.cbClsExtra = 0;
windowClass.cbWndExtra = 0;
windowClass.hInstance = TclWinGetTclInstance();
windowClass.hbrBackground = NULL;
windowClass.lpszMenuName = NULL;
windowClass.lpszClassName = "TclSocket";
windowClass.lpfnWndProc = SocketProc;
windowClass.hIcon = NULL;
windowClass.hCursor = NULL;
if (!RegisterClassA(&windowClass)) {
TclWinConvertError(GetLastError());
(*winSock.WSACleanup)();
goto unloadLibrary;
}
if ((*winSock.WSAStartup)(WSA_VERSION_REQD, &wsaData) != 0) {
goto unloadLibrary;
}
if (wsaData.wVersion != WSA_VERSION_REQD) {
(*winSock.WSACleanup)();
goto unloadLibrary;
}
}
if (tsdPtr == NULL) {
tsdPtr = TCL_TSD_INIT(&dataKey);
tsdPtr->socketList = NULL;
tsdPtr->hwnd = NULL;
tsdPtr->threadId = Tcl_GetCurrentThread();
tsdPtr->readyEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
tsdPtr->socketListLock = CreateEvent(NULL, FALSE, TRUE, NULL);
tsdPtr->socketThread = CreateThread(NULL, 8000, SocketThread,
tsdPtr, 0, &id);
SetThreadPriority(tsdPtr->socketThread, THREAD_PRIORITY_HIGHEST);
if (tsdPtr->socketThread == NULL) {
goto unloadLibrary;
}
if (WaitForSingleObject(tsdPtr->readyEvent, 20000) == WAIT_TIMEOUT) {
goto unloadLibrary;
}
if (tsdPtr->hwnd == NULL) {
goto unloadLibrary;
}
Tcl_CreateEventSource(SocketSetupProc, SocketCheckProc, NULL);
Tcl_CreateThreadExitHandler(SocketThreadExitHandler, NULL);
}
return;
unloadLibrary:
if (tsdPtr != NULL) {
if (tsdPtr->hwnd != NULL) {
DestroyWindow(tsdPtr->hwnd);
}
if (tsdPtr->socketThread != NULL) {
TerminateThread(tsdPtr->socketThread, 0);
tsdPtr->socketThread = NULL;
}
CloseHandle(tsdPtr->readyEvent);
CloseHandle(tsdPtr->socketListLock);
}
FreeLibrary(winSock.hInstance);
winSock.hInstance = NULL;
return;
}
static int
SocketsEnabled()
{
int enabled;
Tcl_MutexLock(&socketMutex);
enabled = (winSock.hInstance != NULL);
Tcl_MutexUnlock(&socketMutex);
return enabled;
}
static void
SocketExitHandler(clientData)
ClientData clientData;
{
Tcl_MutexLock(&socketMutex);
if (winSock.hInstance) {
UnregisterClassA("TclSocket", TclWinGetTclInstance());
(*winSock.WSACleanup)();
FreeLibrary(winSock.hInstance);
winSock.hInstance = NULL;
}
initialized = 0;
hostnameInitialized = 0;
Tcl_MutexUnlock(&socketMutex);
}
static void
SocketThreadExitHandler(clientData)
ClientData clientData;
{
ThreadSpecificData *tsdPtr =
(ThreadSpecificData *)TclThreadDataKeyGet(&dataKey);
if (tsdPtr->socketThread != NULL) {
PostMessage(tsdPtr->hwnd, SOCKET_TERMINATE, 0, 0);
WaitForSingleObject(tsdPtr->socketThread, INFINITE);
CloseHandle(tsdPtr->socketThread);
CloseHandle(tsdPtr->readyEvent);
CloseHandle(tsdPtr->socketListLock);
}
if (tsdPtr->hwnd != NULL) {
DestroyWindow(tsdPtr->hwnd);
}
Tcl_DeleteEventSource(SocketSetupProc, SocketCheckProc, NULL);
}
int
TclpHasSockets(interp)
Tcl_Interp *interp;
{
Tcl_MutexLock(&socketMutex);
InitSockets();
Tcl_MutexUnlock(&socketMutex);
if (SocketsEnabled()) {
return TCL_OK;
}
if (interp != NULL) {
Tcl_AppendResult(interp, "sockets are not available on this system",
NULL);
}
return TCL_ERROR;
}
void
SocketSetupProc(data, flags)
ClientData data;
int flags;
{
SocketInfo *infoPtr;
Tcl_Time blockTime = { 0, 0 };
ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
if (!(flags & TCL_FILE_EVENTS)) {
return;
}
WaitForSingleObject(tsdPtr->socketListLock, INFINITE);
for (infoPtr = tsdPtr->socketList; infoPtr != NULL;
infoPtr = infoPtr->nextPtr) {
if (infoPtr->readyEvents & infoPtr->watchEvents) {
Tcl_SetMaxBlockTime(&blockTime);
break;
}
}
SetEvent(tsdPtr->socketListLock);
}
static void
SocketCheckProc(data, flags)
ClientData data;
int flags;
{
SocketInfo *infoPtr;
SocketEvent *evPtr;
ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
if (!(flags & TCL_FILE_EVENTS)) {
return;
}
WaitForSingleObject(tsdPtr->socketListLock, INFINITE);
for (infoPtr = tsdPtr->socketList; infoPtr != NULL;
infoPtr = infoPtr->nextPtr) {
if ((infoPtr->readyEvents & infoPtr->watchEvents)
&& !(infoPtr->flags & SOCKET_PENDING)) {
infoPtr->flags |= SOCKET_PENDING;
evPtr = (SocketEvent *) ckalloc(sizeof(SocketEvent));
evPtr->header.proc = SocketEventProc;
evPtr->socket = infoPtr->socket;
Tcl_QueueEvent((Tcl_Event *) evPtr, TCL_QUEUE_TAIL);
}
}
SetEvent(tsdPtr->socketListLock);
}
static int
SocketEventProc(evPtr, flags)
Tcl_Event *evPtr;
int flags;
{
SocketInfo *infoPtr;
SocketEvent *eventPtr = (SocketEvent *) evPtr;
int mask = 0;
int events;
ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
if (!(flags & TCL_FILE_EVENTS)) {
return 0;
}
WaitForSingleObject(tsdPtr->socketListLock, INFINITE);
for (infoPtr = tsdPtr->socketList; infoPtr != NULL;
infoPtr = infoPtr->nextPtr) {
if (infoPtr->socket == eventPtr->socket) {
break;
}
}
SetEvent(tsdPtr->socketListLock);
if (!infoPtr) {
return 1;
}
infoPtr->flags &= ~SOCKET_PENDING;
if (infoPtr->readyEvents & FD_ACCEPT) {
TcpAccept(infoPtr);
return 1;
}
events = infoPtr->readyEvents & infoPtr->watchEvents;
if (events & FD_CLOSE) {
Tcl_Time blockTime = { 0, 0 };
Tcl_SetMaxBlockTime(&blockTime);
mask |= TCL_READABLE;
} else if (events & FD_READ) {
fd_set readFds;
struct timeval timeout;
SendMessage(tsdPtr->hwnd, SOCKET_SELECT,
(WPARAM) UNSELECT, (LPARAM) infoPtr);
FD_ZERO(&readFds);
FD_SET(infoPtr->socket, &readFds);
timeout.tv_usec = 0;
timeout.tv_sec = 0;
if ((*winSock.select)(0, &readFds, NULL, NULL, &timeout) != 0) {
mask |= TCL_READABLE;
} else {
infoPtr->readyEvents &= ~(FD_READ);
SendMessage(tsdPtr->hwnd, SOCKET_SELECT,
(WPARAM) SELECT, (LPARAM) infoPtr);
}
}
if (events & (FD_WRITE | FD_CONNECT)) {
mask |= TCL_WRITABLE;
}
if (mask) {
Tcl_NotifyChannel(infoPtr->channel, mask);
}
return 1;
}
static int
TcpBlockProc(instanceData, mode)
ClientData instanceData;
int mode;
{
SocketInfo *infoPtr = (SocketInfo *) instanceData;
if (mode == TCL_MODE_NONBLOCKING) {
infoPtr->flags |= SOCKET_ASYNC;
} else {
infoPtr->flags &= ~(SOCKET_ASYNC);
}
return 0;
}
static int
TcpCloseProc(instanceData, interp)
ClientData instanceData;
Tcl_Interp *interp;
{
SocketInfo *infoPtr = (SocketInfo *) instanceData;
SocketInfo **nextPtrPtr;
int errorCode = 0;
ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
if (SocketsEnabled()) {
if ((*winSock.closesocket)(infoPtr->socket) == SOCKET_ERROR) {
TclWinConvertWSAError((*winSock.WSAGetLastError)());
errorCode = Tcl_GetErrno();
}
}
WaitForSingleObject(tsdPtr->socketListLock, INFINITE);
for (nextPtrPtr = &(tsdPtr->socketList); (*nextPtrPtr) != NULL;
nextPtrPtr = &((*nextPtrPtr)->nextPtr)) {
if ((*nextPtrPtr) == infoPtr) {
(*nextPtrPtr) = infoPtr->nextPtr;
break;
}
}
SetEvent(tsdPtr->socketListLock);
ckfree((char *) infoPtr);
return errorCode;
}
static SocketInfo *
NewSocketInfo(socket)
SOCKET socket;
{
SocketInfo *infoPtr;
ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
infoPtr = (SocketInfo *) ckalloc((unsigned) sizeof(SocketInfo));
infoPtr->socket = socket;
infoPtr->flags = 0;
infoPtr->watchEvents = 0;
infoPtr->readyEvents = 0;
infoPtr->selectEvents = 0;
infoPtr->acceptEventCount = 0;
infoPtr->acceptProc = NULL;
infoPtr->lastError = 0;
WaitForSingleObject(tsdPtr->socketListLock, INFINITE);
infoPtr->nextPtr = tsdPtr->socketList;
tsdPtr->socketList = infoPtr;
SetEvent(tsdPtr->socketListLock);
return infoPtr;
}
static SocketInfo *
CreateSocket(interp, port, host, server, myaddr, myport, async)
Tcl_Interp *interp;
int port;
CONST char *host;
int server;
CONST char *myaddr;
int myport;
int async;
{
u_long flag = 1;
int asyncConnect = 0;
struct sockaddr_in sockaddr;
struct sockaddr_in mysockaddr;
SOCKET sock;
SocketInfo *infoPtr;
ThreadSpecificData *tsdPtr =
(ThreadSpecificData *)TclThreadDataKeyGet(&dataKey);
if (!SocketsEnabled()) {
return NULL;
}
if (! CreateSocketAddress(&sockaddr, host, port)) {
goto error;
}
if ((myaddr != NULL || myport != 0) &&
! CreateSocketAddress(&mysockaddr, myaddr, myport)) {
goto error;
}
sock = (*winSock.socket)(AF_INET, SOCK_STREAM, 0);
if (sock == INVALID_SOCKET) {
goto error;
}
SetHandleInformation( (HANDLE) sock, HANDLE_FLAG_INHERIT, 0 );
TclSockMinimumBuffers(sock, TCP_BUFFER_SIZE);
if (server) {
if ((*winSock.bind)(sock, (struct sockaddr *) &sockaddr,
sizeof(sockaddr)) == SOCKET_ERROR) {
goto error;
}
if ((*winSock.listen)(sock, SOMAXCONN) == SOCKET_ERROR) {
goto error;
}
infoPtr = NewSocketInfo(sock);
infoPtr->selectEvents = FD_ACCEPT;
infoPtr->watchEvents |= FD_ACCEPT;
} else {
if (myaddr != NULL || myport != 0) {
if ((*winSock.bind)(sock, (struct sockaddr *) &mysockaddr,
sizeof(struct sockaddr)) == SOCKET_ERROR) {
goto error;
}
}
if (async) {
if ((*winSock.ioctlsocket)(sock, FIONBIO, &flag) == SOCKET_ERROR) {
goto error;
}
}
if ((*winSock.connect)(sock, (struct sockaddr *) &sockaddr,
sizeof(sockaddr)) == SOCKET_ERROR) {
TclWinConvertWSAError((*winSock.WSAGetLastError)());
if (Tcl_GetErrno() != EWOULDBLOCK) {
goto error;
}
asyncConnect = 1;
}
infoPtr = NewSocketInfo(sock);
infoPtr->selectEvents = FD_READ | FD_WRITE | FD_CLOSE;
if (asyncConnect) {
infoPtr->flags |= SOCKET_ASYNC_CONNECT;
infoPtr->selectEvents |= FD_CONNECT;
}
}
(*winSock.ioctlsocket)(sock, FIONBIO, &flag);
SendMessage(tsdPtr->hwnd, SOCKET_SELECT,
(WPARAM) SELECT, (LPARAM) infoPtr);
return infoPtr;
error:
TclWinConvertWSAError((*winSock.WSAGetLastError)());
if (interp != NULL) {
Tcl_AppendResult(interp, "couldn't open socket: ",
Tcl_PosixError(interp), (char *) NULL);
}
if (sock != INVALID_SOCKET) {
(*winSock.closesocket)(sock);
}
return NULL;
}
static int
CreateSocketAddress(sockaddrPtr, host, port)
struct sockaddr_in *sockaddrPtr;
CONST char *host;
int port;
{
struct hostent *hostent;
struct in_addr addr;
if (!SocketsEnabled()) {
Tcl_SetErrno(EFAULT);
return 0;
}
(void) memset((char *) sockaddrPtr, '\0', sizeof(struct sockaddr_in));
sockaddrPtr->sin_family = AF_INET;
sockaddrPtr->sin_port = (*winSock.htons)((short) (port & 0xFFFF));
if (host == NULL) {
addr.s_addr = INADDR_ANY;
} else {
addr.s_addr = (*winSock.inet_addr)(host);
if (addr.s_addr == INADDR_NONE) {
hostent = (*winSock.gethostbyname)(host);
if (hostent != NULL) {
memcpy((char *) &addr,
(char *) hostent->h_addr_list[0],
(size_t) hostent->h_length);
} else {
#ifdef EHOSTUNREACH
Tcl_SetErrno(EHOSTUNREACH);
#else
#ifdef ENXIO
Tcl_SetErrno(ENXIO);
#endif
#endif
return 0;
}
}
}
sockaddrPtr->sin_addr.s_addr = addr.s_addr;
return 1;
}
static int
WaitForSocketEvent(infoPtr, events, errorCodePtr)
SocketInfo *infoPtr;
int events;
int *errorCodePtr;
{
int result = 1;
int oldMode;
ThreadSpecificData *tsdPtr =
(ThreadSpecificData *)TclThreadDataKeyGet(&dataKey);
oldMode = Tcl_SetServiceMode(TCL_SERVICE_NONE);
SendMessage(tsdPtr->hwnd, SOCKET_SELECT,
(WPARAM) UNSELECT, (LPARAM) infoPtr);
SendMessage(tsdPtr->hwnd, SOCKET_SELECT,
(WPARAM) SELECT, (LPARAM) infoPtr);
while (1) {
if (infoPtr->lastError) {
*errorCodePtr = infoPtr->lastError;
result = 0;
break;
} else if (infoPtr->readyEvents & events) {
break;
} else if (infoPtr->flags & SOCKET_ASYNC) {
*errorCodePtr = EWOULDBLOCK;
result = 0;
break;
}
WaitForSingleObject(tsdPtr->readyEvent, INFINITE);
}
(void) Tcl_SetServiceMode(oldMode);
return result;
}
Tcl_Channel
Tcl_OpenTcpClient(interp, port, host, myaddr, myport, async)
Tcl_Interp *interp;
int port;
CONST char *host;
CONST char *myaddr;
int myport;
int async;
{
SocketInfo *infoPtr;
char channelName[16 + TCL_INTEGER_SPACE];
if (TclpHasSockets(interp) != TCL_OK) {
return NULL;
}
infoPtr = CreateSocket(interp, port, host, 0, myaddr, myport, async);
if (infoPtr == NULL) {
return NULL;
}
wsprintfA(channelName, "sock%d", infoPtr->socket);
infoPtr->channel = Tcl_CreateChannel(&tcpChannelType, channelName,
(ClientData) infoPtr, (TCL_READABLE | TCL_WRITABLE));
if (Tcl_SetChannelOption(interp, infoPtr->channel, "-translation",
"auto crlf") == TCL_ERROR) {
Tcl_Close((Tcl_Interp *) NULL, infoPtr->channel);
return (Tcl_Channel) NULL;
}
if (Tcl_SetChannelOption(NULL, infoPtr->channel, "-eofchar", "")
== TCL_ERROR) {
Tcl_Close((Tcl_Interp *) NULL, infoPtr->channel);
return (Tcl_Channel) NULL;
}
return infoPtr->channel;
}
Tcl_Channel
Tcl_MakeTcpClientChannel(sock)
ClientData sock;
{
SocketInfo *infoPtr;
char channelName[16 + TCL_INTEGER_SPACE];
ThreadSpecificData *tsdPtr;
if (TclpHasSockets(NULL) != TCL_OK) {
return NULL;
}
tsdPtr = (ThreadSpecificData *)TclThreadDataKeyGet(&dataKey);
TclSockMinimumBuffers((SOCKET) sock, TCP_BUFFER_SIZE);
infoPtr = NewSocketInfo((SOCKET) sock);
infoPtr->selectEvents = FD_READ | FD_CLOSE | FD_WRITE;
SendMessage(tsdPtr->hwnd, SOCKET_SELECT,
(WPARAM) SELECT, (LPARAM) infoPtr);
wsprintfA(channelName, "sock%d", infoPtr->socket);
infoPtr->channel = Tcl_CreateChannel(&tcpChannelType, channelName,
(ClientData) infoPtr, (TCL_READABLE | TCL_WRITABLE));
Tcl_SetChannelOption(NULL, infoPtr->channel, "-translation", "auto crlf");
return infoPtr->channel;
}
Tcl_Channel
Tcl_OpenTcpServer(interp, port, host, acceptProc, acceptProcData)
Tcl_Interp *interp;
int port;
CONST char *host;
Tcl_TcpAcceptProc *acceptProc;
ClientData acceptProcData;
{
SocketInfo *infoPtr;
char channelName[16 + TCL_INTEGER_SPACE];
if (TclpHasSockets(interp) != TCL_OK) {
return NULL;
}
infoPtr = CreateSocket(interp, port, host, 1, NULL, 0, 0);
if (infoPtr == NULL) {
return NULL;
}
infoPtr->acceptProc = acceptProc;
infoPtr->acceptProcData = acceptProcData;
wsprintfA(channelName, "sock%d", infoPtr->socket);
infoPtr->channel = Tcl_CreateChannel(&tcpChannelType, channelName,
(ClientData) infoPtr, 0);
if (Tcl_SetChannelOption(interp, infoPtr->channel, "-eofchar", "")
== TCL_ERROR) {
Tcl_Close((Tcl_Interp *) NULL, infoPtr->channel);
return (Tcl_Channel) NULL;
}
return infoPtr->channel;
}
static void
TcpAccept(infoPtr)
SocketInfo *infoPtr;
{
SOCKET newSocket;
SocketInfo *newInfoPtr;
struct sockaddr_in addr;
int len;
char channelName[16 + TCL_INTEGER_SPACE];
ThreadSpecificData *tsdPtr =
(ThreadSpecificData *)TclThreadDataKeyGet(&dataKey);
len = sizeof(struct sockaddr_in);
newSocket = (*winSock.accept)(infoPtr->socket,
(struct sockaddr *)&addr,
&len);
if (newSocket == INVALID_SOCKET) {
infoPtr->acceptEventCount = 0;
infoPtr->readyEvents &= ~(FD_ACCEPT);
return;
}
infoPtr->acceptEventCount--;
if (infoPtr->acceptEventCount <= 0) {
infoPtr->readyEvents &= ~(FD_ACCEPT);
}
SetHandleInformation( (HANDLE) newSocket, HANDLE_FLAG_INHERIT, 0 );
newInfoPtr = NewSocketInfo(newSocket);
newInfoPtr->selectEvents = (FD_READ | FD_WRITE | FD_CLOSE);
SendMessage(tsdPtr->hwnd, SOCKET_SELECT,
(WPARAM) SELECT, (LPARAM) newInfoPtr);
wsprintfA(channelName, "sock%d", newInfoPtr->socket);
newInfoPtr->channel = Tcl_CreateChannel(&tcpChannelType, channelName,
(ClientData) newInfoPtr, (TCL_READABLE | TCL_WRITABLE));
if (Tcl_SetChannelOption(NULL, newInfoPtr->channel, "-translation",
"auto crlf") == TCL_ERROR) {
Tcl_Close((Tcl_Interp *) NULL, newInfoPtr->channel);
return;
}
if (Tcl_SetChannelOption(NULL, newInfoPtr->channel, "-eofchar", "")
== TCL_ERROR) {
Tcl_Close((Tcl_Interp *) NULL, newInfoPtr->channel);
return;
}
if (infoPtr->acceptProc != NULL) {
(infoPtr->acceptProc) (infoPtr->acceptProcData,
newInfoPtr->channel,
(*winSock.inet_ntoa)(addr.sin_addr),
(*winSock.ntohs)(addr.sin_port));
}
}
static int
TcpInputProc(instanceData, buf, toRead, errorCodePtr)
ClientData instanceData;
char *buf;
int toRead;
int *errorCodePtr;
{
SocketInfo *infoPtr = (SocketInfo *) instanceData;
int bytesRead;
int error;
ThreadSpecificData *tsdPtr =
(ThreadSpecificData *)TclThreadDataKeyGet(&dataKey);
*errorCodePtr = 0;
if (!SocketsEnabled()) {
*errorCodePtr = EFAULT;
return -1;
}
if (infoPtr->flags & SOCKET_EOF) {
return 0;
}
if ((infoPtr->flags & SOCKET_ASYNC_CONNECT)
&& ! WaitForSocketEvent(infoPtr, FD_CONNECT, errorCodePtr)) {
return -1;
}
while (1) {
SendMessage(tsdPtr->hwnd, SOCKET_SELECT,
(WPARAM) UNSELECT, (LPARAM) infoPtr);
bytesRead = (*winSock.recv)(infoPtr->socket, buf, toRead, 0);
infoPtr->readyEvents &= ~(FD_READ);
if (bytesRead == 0) {
infoPtr->flags |= SOCKET_EOF;
}
if (bytesRead != SOCKET_ERROR) {
break;
}
if (infoPtr->readyEvents & FD_CLOSE) {
infoPtr->flags |= SOCKET_EOF;
bytesRead = 0;
break;
}
error = (*winSock.WSAGetLastError)();
if ((infoPtr->flags & SOCKET_ASYNC) || (error != WSAEWOULDBLOCK)) {
TclWinConvertWSAError(error);
*errorCodePtr = Tcl_GetErrno();
bytesRead = -1;
break;
}
if (!WaitForSocketEvent(infoPtr, FD_READ|FD_CLOSE, errorCodePtr)) {
bytesRead = -1;
break;
}
}
SendMessage(tsdPtr->hwnd, SOCKET_SELECT,
(WPARAM) SELECT, (LPARAM) infoPtr);
return bytesRead;
}
static int
TcpOutputProc(instanceData, buf, toWrite, errorCodePtr)
ClientData instanceData;
CONST char *buf;
int toWrite;
int *errorCodePtr;
{
SocketInfo *infoPtr = (SocketInfo *) instanceData;
int bytesWritten;
int error;
ThreadSpecificData *tsdPtr =
(ThreadSpecificData *)TclThreadDataKeyGet(&dataKey);
*errorCodePtr = 0;
if (!SocketsEnabled()) {
*errorCodePtr = EFAULT;
return -1;
}
if ((infoPtr->flags & SOCKET_ASYNC_CONNECT)
&& ! WaitForSocketEvent(infoPtr, FD_CONNECT, errorCodePtr)) {
return -1;
}
while (1) {
SendMessage(tsdPtr->hwnd, SOCKET_SELECT,
(WPARAM) UNSELECT, (LPARAM) infoPtr);
bytesWritten = (*winSock.send)(infoPtr->socket, buf, toWrite, 0);
if (bytesWritten != SOCKET_ERROR) {
if (infoPtr->watchEvents & FD_WRITE) {
Tcl_Time blockTime = { 0, 0 };
Tcl_SetMaxBlockTime(&blockTime);
}
break;
}
error = (*winSock.WSAGetLastError)();
if (error == WSAEWOULDBLOCK) {
infoPtr->readyEvents &= ~(FD_WRITE);
if (infoPtr->flags & SOCKET_ASYNC) {
*errorCodePtr = EWOULDBLOCK;
bytesWritten = -1;
break;
}
} else {
TclWinConvertWSAError(error);
*errorCodePtr = Tcl_GetErrno();
bytesWritten = -1;
break;
}
if (!WaitForSocketEvent(infoPtr, FD_WRITE|FD_CLOSE, errorCodePtr)) {
bytesWritten = -1;
break;
}
}
SendMessage(tsdPtr->hwnd, SOCKET_SELECT,
(WPARAM) SELECT, (LPARAM) infoPtr);
return bytesWritten;
}
static int
TcpGetOptionProc(instanceData, interp, optionName, dsPtr)
ClientData instanceData;
Tcl_Interp *interp;
CONST char *optionName;
Tcl_DString *dsPtr;
{
SocketInfo *infoPtr;
struct sockaddr_in sockname;
struct sockaddr_in peername;
struct hostent *hostEntPtr;
SOCKET sock;
int size = sizeof(struct sockaddr_in);
size_t len = 0;
char buf[TCL_INTEGER_SPACE];
if (!SocketsEnabled()) {
if (interp) {
Tcl_AppendResult(interp, "winsock is not initialized", NULL);
}
return TCL_ERROR;
}
infoPtr = (SocketInfo *) instanceData;
sock = (int) infoPtr->socket;
if (optionName != (char *) NULL) {
len = strlen(optionName);
}
if ((len > 1) && (optionName[1] == 'e') &&
(strncmp(optionName, "-error", len) == 0)) {
int optlen;
int err, ret;
optlen = sizeof(int);
ret = TclWinGetSockOpt(sock, SOL_SOCKET, SO_ERROR,
(char *)&err, &optlen);
if (ret == SOCKET_ERROR) {
err = (*winSock.WSAGetLastError)();
}
if (err) {
TclWinConvertWSAError(err);
Tcl_DStringAppend(dsPtr, Tcl_ErrnoMsg(Tcl_GetErrno()), -1);
}
return TCL_OK;
}
if ((len == 0) ||
((len > 1) && (optionName[1] == 'p') &&
(strncmp(optionName, "-peername", len) == 0))) {
if ((*winSock.getpeername)(sock, (struct sockaddr *) &peername, &size)
== 0) {
if (len == 0) {
Tcl_DStringAppendElement(dsPtr, "-peername");
Tcl_DStringStartSublist(dsPtr);
}
Tcl_DStringAppendElement(dsPtr,
(*winSock.inet_ntoa)(peername.sin_addr));
if (peername.sin_addr.s_addr == 0) {
hostEntPtr = (struct hostent *) NULL;
} else {
hostEntPtr = (*winSock.gethostbyaddr)(
(char *) &(peername.sin_addr), sizeof(peername.sin_addr),
AF_INET);
}
if (hostEntPtr != (struct hostent *) NULL) {
Tcl_DStringAppendElement(dsPtr, hostEntPtr->h_name);
} else {
Tcl_DStringAppendElement(dsPtr,
(*winSock.inet_ntoa)(peername.sin_addr));
}
TclFormatInt(buf, (*winSock.ntohs)(peername.sin_port));
Tcl_DStringAppendElement(dsPtr, buf);
if (len == 0) {
Tcl_DStringEndSublist(dsPtr);
} else {
return TCL_OK;
}
} else {
if (len) {
TclWinConvertWSAError((*winSock.WSAGetLastError)());
if (interp) {
Tcl_AppendResult(interp, "can't get peername: ",
Tcl_PosixError(interp),
(char *) NULL);
}
return TCL_ERROR;
}
}
}
if ((len == 0) ||
((len > 1) && (optionName[1] == 's') &&
(strncmp(optionName, "-sockname", len) == 0))) {
if ((*winSock.getsockname)(sock, (struct sockaddr *) &sockname, &size)
== 0) {
if (len == 0) {
Tcl_DStringAppendElement(dsPtr, "-sockname");
Tcl_DStringStartSublist(dsPtr);
}
Tcl_DStringAppendElement(dsPtr,
(*winSock.inet_ntoa)(sockname.sin_addr));
if (sockname.sin_addr.s_addr == 0) {
hostEntPtr = (struct hostent *) NULL;
} else {
hostEntPtr = (*winSock.gethostbyaddr)(
(char *) &(sockname.sin_addr), sizeof(peername.sin_addr),
AF_INET);
}
if (hostEntPtr != (struct hostent *) NULL) {
Tcl_DStringAppendElement(dsPtr, hostEntPtr->h_name);
} else {
Tcl_DStringAppendElement(dsPtr,
(*winSock.inet_ntoa)(sockname.sin_addr));
}
TclFormatInt(buf, (*winSock.ntohs)(sockname.sin_port));
Tcl_DStringAppendElement(dsPtr, buf);
if (len == 0) {
Tcl_DStringEndSublist(dsPtr);
} else {
return TCL_OK;
}
} else {
if (interp) {
TclWinConvertWSAError((*winSock.WSAGetLastError)());
Tcl_AppendResult(interp, "can't get sockname: ",
Tcl_PosixError(interp),
(char *) NULL);
}
return TCL_ERROR;
}
}
if (len > 0) {
return Tcl_BadChannelOption(interp, optionName, "peername sockname");
}
return TCL_OK;
}
static void
TcpWatchProc(instanceData, mask)
ClientData instanceData;
int mask;
{
SocketInfo *infoPtr = (SocketInfo *) instanceData;
if (!infoPtr->acceptProc) {
infoPtr->watchEvents = 0;
if (mask & TCL_READABLE) {
infoPtr->watchEvents |= (FD_READ|FD_CLOSE|FD_ACCEPT);
}
if (mask & TCL_WRITABLE) {
infoPtr->watchEvents |= (FD_WRITE|FD_CONNECT);
}
if (infoPtr->readyEvents & infoPtr->watchEvents) {
Tcl_Time blockTime = { 0, 0 };
Tcl_SetMaxBlockTime(&blockTime);
}
}
}
static int
TcpGetHandleProc(instanceData, direction, handlePtr)
ClientData instanceData;
int direction;
ClientData *handlePtr;
{
SocketInfo *statePtr = (SocketInfo *) instanceData;
*handlePtr = (ClientData) statePtr->socket;
return TCL_OK;
}
static DWORD WINAPI
SocketThread(LPVOID arg)
{
MSG msg;
ThreadSpecificData *tsdPtr = (ThreadSpecificData *)(arg);
tsdPtr->hwnd = CreateWindowA("TclSocket", "TclSocket",
WS_TILED, 0, 0, 0, 0, NULL, NULL, windowClass.hInstance, NULL);
SetEvent(tsdPtr->readyEvent);
if (tsdPtr->hwnd == NULL) {
return 1;
} else {
#ifdef _WIN64
SetWindowLongPtr(tsdPtr->hwnd, GWLP_USERDATA, (LONG_PTR) tsdPtr);
#else
SetWindowLong(tsdPtr->hwnd, GWL_USERDATA, (LONG) tsdPtr);
#endif
}
while (1) {
while (PeekMessage(&msg, tsdPtr->hwnd, 0, 0, PM_REMOVE)) {
DispatchMessage(&msg);
}
WaitMessage();
}
}
static LRESULT CALLBACK
SocketProc(hwnd, message, wParam, lParam)
HWND hwnd;
UINT message;
WPARAM wParam;
LPARAM lParam;
{
int event, error;
SOCKET socket;
SocketInfo *infoPtr;
ThreadSpecificData *tsdPtr =
#ifdef _WIN64
(ThreadSpecificData *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
#else
(ThreadSpecificData *) GetWindowLong(hwnd, GWL_USERDATA);
#endif
switch (message) {
default:
return DefWindowProc(hwnd, message, wParam, lParam);
break;
case SOCKET_MESSAGE:
event = WSAGETSELECTEVENT(lParam);
error = WSAGETSELECTERROR(lParam);
socket = (SOCKET) wParam;
WaitForSingleObject(tsdPtr->socketListLock, INFINITE);
for (infoPtr = tsdPtr->socketList; infoPtr != NULL;
infoPtr = infoPtr->nextPtr) {
if (infoPtr->socket == socket) {
if (event & FD_CLOSE) {
infoPtr->acceptEventCount = 0;
infoPtr->readyEvents &= ~(FD_WRITE|FD_ACCEPT);
} else if (event & FD_ACCEPT) {
infoPtr->acceptEventCount++;
}
if (event & FD_CONNECT) {
infoPtr->flags &= ~(SOCKET_ASYNC_CONNECT);
if (error != ERROR_SUCCESS) {
TclWinConvertWSAError(error);
infoPtr->lastError = Tcl_GetErrno();
}
}
if(infoPtr->flags & SOCKET_ASYNC_CONNECT) {
infoPtr->flags &= ~(SOCKET_ASYNC_CONNECT);
if (error != ERROR_SUCCESS) {
TclWinConvertWSAError(error);
infoPtr->lastError = Tcl_GetErrno();
}
infoPtr->readyEvents |= FD_WRITE;
}
infoPtr->readyEvents |= event;
SetEvent(tsdPtr->readyEvent);
Tcl_ThreadAlert(tsdPtr->threadId);
break;
}
}
SetEvent(tsdPtr->socketListLock);
break;
case SOCKET_SELECT:
infoPtr = (SocketInfo *) lParam;
if (wParam == SELECT) {
(void) (*winSock.WSAAsyncSelect)(infoPtr->socket, hwnd,
SOCKET_MESSAGE, infoPtr->selectEvents);
} else {
(void) (*winSock.WSAAsyncSelect)(infoPtr->socket, hwnd, 0, 0);
}
break;
case SOCKET_TERMINATE:
ExitThread(0);
break;
}
return 0;
}
CONST char *
Tcl_GetHostName()
{
DWORD length;
WCHAR wbuf[MAX_COMPUTERNAME_LENGTH + 1];
Tcl_MutexLock(&socketMutex);
InitSockets();
if (hostnameInitialized) {
Tcl_MutexUnlock(&socketMutex);
return hostname;
}
Tcl_MutexUnlock(&socketMutex);
if (TclpHasSockets(NULL) == TCL_OK) {
if ((*winSock.gethostname)(hostname, sizeof(hostname)) == 0) {
Tcl_MutexLock(&socketMutex);
hostnameInitialized = 1;
Tcl_MutexUnlock(&socketMutex);
return hostname;
}
}
Tcl_MutexLock(&socketMutex);
length = sizeof(hostname);
if ((*tclWinProcs->getComputerNameProc)(wbuf, &length) != 0) {
Tcl_DString ds;
lstrcpynA(hostname, Tcl_WinTCharToUtf((TCHAR *) wbuf, -1, &ds),
sizeof(hostname));
Tcl_DStringFree(&ds);
Tcl_UtfToLower(hostname);
} else {
hostname[0] = '\0';
}
hostnameInitialized = 1;
Tcl_MutexUnlock(&socketMutex);
return hostname;
}
int
TclWinGetSockOpt(SOCKET s, int level, int optname, char * optval,
int FAR *optlen)
{
if (!SocketsEnabled()) {
return SOCKET_ERROR;
}
return (*winSock.getsockopt)(s, level, optname, optval, optlen);
}
int
TclWinSetSockOpt(SOCKET s, int level, int optname, const char * optval,
int optlen)
{
if (!SocketsEnabled()) {
return SOCKET_ERROR;
}
return (*winSock.setsockopt)(s, level, optname, optval, optlen);
}
u_short
TclWinNToHS(u_short netshort)
{
if (!SocketsEnabled()) {
return (u_short) -1;
}
return (*winSock.ntohs)(netshort);
}
struct servent *
TclWinGetServByName(const char * name, const char * proto)
{
if (!SocketsEnabled()) {
return (struct servent *) NULL;
}
return (*winSock.getservbyname)(name, proto);
}