sl@0: /* sl@0: * tclWinSock.c -- sl@0: * sl@0: * This file contains Windows-specific socket related code. sl@0: * sl@0: * Copyright (c) 1995-1997 Sun Microsystems, Inc. sl@0: * sl@0: * See the file "license.terms" for information on usage and redistribution sl@0: * of this file, and for a DISCLAIMER OF ALL WARRANTIES. sl@0: * sl@0: * RCS: @(#) $Id: tclWinSock.c,v 1.36.2.6 2006/09/26 21:40:37 patthoyts Exp $ sl@0: */ sl@0: sl@0: #include "tclWinInt.h" sl@0: sl@0: /* sl@0: * Make sure to remove the redirection defines set in tclWinPort.h sl@0: * that is in use in other sections of the core, except for us. sl@0: */ sl@0: #undef getservbyname sl@0: #undef getsockopt sl@0: #undef ntohs sl@0: #undef setsockopt sl@0: sl@0: /* sl@0: * The following variable is used to tell whether this module has been sl@0: * initialized. sl@0: */ sl@0: sl@0: static int initialized = 0; sl@0: sl@0: static int hostnameInitialized = 0; sl@0: static char hostname[255]; /* This buffer should be big enough for sl@0: * hostname plus domain name. */ sl@0: sl@0: TCL_DECLARE_MUTEX(socketMutex) sl@0: sl@0: sl@0: /* sl@0: * Mingw and Cygwin may not have LPFN_* typedefs. sl@0: */ sl@0: sl@0: #ifdef HAVE_NO_LPFN_DECLS sl@0: typedef SOCKET (PASCAL FAR *LPFN_ACCEPT)(SOCKET s, sl@0: struct sockaddr FAR * addr, int FAR * addrlen); sl@0: typedef int (PASCAL FAR *LPFN_BIND)(SOCKET s, sl@0: const struct sockaddr FAR *addr, int namelen); sl@0: typedef int (PASCAL FAR *LPFN_CLOSESOCKET)(SOCKET s); sl@0: typedef int (PASCAL FAR *LPFN_CONNECT)(SOCKET s, sl@0: const struct sockaddr FAR *name, int namelen); sl@0: typedef struct hostent FAR * (PASCAL FAR *LPFN_GETHOSTBYADDR) sl@0: (const char FAR *addr, int addrlen, int addrtype); sl@0: typedef struct hostent FAR * (PASCAL FAR *LPFN_GETHOSTBYNAME) sl@0: (const char FAR * name); sl@0: typedef int (PASCAL FAR *LPFN_GETHOSTNAME)(char FAR * name, sl@0: int namelen); sl@0: typedef int (PASCAL FAR *LPFN_GETPEERNAME)(SOCKET sock, sl@0: struct sockaddr FAR *name, int FAR *namelen); sl@0: typedef struct servent FAR * (PASCAL FAR *LPFN_GETSERVBYNAME) sl@0: (const char FAR * name, const char FAR * proto); sl@0: typedef int (PASCAL FAR *LPFN_GETSOCKNAME)(SOCKET sock, sl@0: struct sockaddr FAR *name, int FAR *namelen); sl@0: typedef int (PASCAL FAR *LPFN_GETSOCKOPT)(SOCKET s, int level, sl@0: int optname, char FAR * optval, int FAR *optlen); sl@0: typedef u_short (PASCAL FAR *LPFN_HTONS)(u_short hostshort); sl@0: typedef unsigned long (PASCAL FAR *LPFN_INET_ADDR) sl@0: (const char FAR * cp); sl@0: typedef char FAR * (PASCAL FAR *LPFN_INET_NTOA) sl@0: (struct in_addr in); sl@0: typedef int (PASCAL FAR *LPFN_IOCTLSOCKET)(SOCKET s, sl@0: long cmd, u_long FAR *argp); sl@0: typedef int (PASCAL FAR *LPFN_LISTEN)(SOCKET s, int backlog); sl@0: typedef u_short (PASCAL FAR *LPFN_NTOHS)(u_short netshort); sl@0: typedef int (PASCAL FAR *LPFN_RECV)(SOCKET s, char FAR * buf, sl@0: int len, int flags); sl@0: typedef int (PASCAL FAR *LPFN_SELECT)(int nfds, sl@0: fd_set FAR * readfds, fd_set FAR * writefds, sl@0: fd_set FAR * exceptfds, sl@0: const struct timeval FAR * timeout); sl@0: typedef int (PASCAL FAR *LPFN_SEND)(SOCKET s, sl@0: const char FAR * buf, int len, int flags); sl@0: typedef int (PASCAL FAR *LPFN_SETSOCKOPT)(SOCKET s, sl@0: int level, int optname, const char FAR * optval, sl@0: int optlen); sl@0: typedef SOCKET (PASCAL FAR *LPFN_SOCKET)(int af, sl@0: int type, int protocol); sl@0: typedef int (PASCAL FAR *LPFN_WSAASYNCSELECT)(SOCKET s, sl@0: HWND hWnd, u_int wMsg, long lEvent); sl@0: typedef int (PASCAL FAR *LPFN_WSACLEANUP)(void); sl@0: typedef int (PASCAL FAR *LPFN_WSAGETLASTERROR)(void); sl@0: typedef int (PASCAL FAR *LPFN_WSASTARTUP)(WORD wVersionRequired, sl@0: LPWSADATA lpWSAData); sl@0: #endif sl@0: sl@0: sl@0: /* sl@0: * The following structure contains pointers to all of the WinSock API sl@0: * entry points used by Tcl. It is initialized by InitSockets. Since sl@0: * we dynamically load the Winsock DLL on demand, we must use this sl@0: * function table to refer to functions in the winsock API. sl@0: */ sl@0: sl@0: static struct { sl@0: HMODULE hModule; /* Handle to WinSock library. */ sl@0: sl@0: /* Winsock 1.1 functions */ sl@0: LPFN_ACCEPT accept; sl@0: LPFN_BIND bind; sl@0: LPFN_CLOSESOCKET closesocket; sl@0: LPFN_CONNECT connect; sl@0: LPFN_GETHOSTBYADDR gethostbyaddr; sl@0: LPFN_GETHOSTBYNAME gethostbyname; sl@0: LPFN_GETHOSTNAME gethostname; sl@0: LPFN_GETPEERNAME getpeername; sl@0: LPFN_GETSERVBYNAME getservbyname; sl@0: LPFN_GETSOCKNAME getsockname; sl@0: LPFN_GETSOCKOPT getsockopt; sl@0: LPFN_HTONS htons; sl@0: LPFN_INET_ADDR inet_addr; sl@0: LPFN_INET_NTOA inet_ntoa; sl@0: LPFN_IOCTLSOCKET ioctlsocket; sl@0: LPFN_LISTEN listen; sl@0: LPFN_NTOHS ntohs; sl@0: LPFN_RECV recv; sl@0: LPFN_SELECT select; sl@0: LPFN_SEND send; sl@0: LPFN_SETSOCKOPT setsockopt; sl@0: LPFN_SOCKET socket; sl@0: LPFN_WSAASYNCSELECT WSAAsyncSelect; sl@0: LPFN_WSACLEANUP WSACleanup; sl@0: LPFN_WSAGETLASTERROR WSAGetLastError; sl@0: LPFN_WSASTARTUP WSAStartup; sl@0: sl@0: } winSock; sl@0: sl@0: /* sl@0: * The following defines declare the messages used on socket windows. sl@0: */ sl@0: sl@0: #define SOCKET_MESSAGE WM_USER+1 sl@0: #define SOCKET_SELECT WM_USER+2 sl@0: #define SOCKET_TERMINATE WM_USER+3 sl@0: #define SELECT TRUE sl@0: #define UNSELECT FALSE sl@0: sl@0: /* sl@0: * The following structure is used to store the data associated with sl@0: * each socket. sl@0: */ sl@0: sl@0: typedef struct SocketInfo { sl@0: Tcl_Channel channel; /* Channel associated with this sl@0: * socket. */ sl@0: SOCKET socket; /* Windows SOCKET handle. */ sl@0: int flags; /* Bit field comprised of the flags sl@0: * described below. */ sl@0: int watchEvents; /* OR'ed combination of FD_READ, sl@0: * FD_WRITE, FD_CLOSE, FD_ACCEPT and sl@0: * FD_CONNECT that indicate which sl@0: * events are interesting. */ sl@0: int readyEvents; /* OR'ed combination of FD_READ, sl@0: * FD_WRITE, FD_CLOSE, FD_ACCEPT and sl@0: * FD_CONNECT that indicate which sl@0: * events have occurred. */ sl@0: int selectEvents; /* OR'ed combination of FD_READ, sl@0: * FD_WRITE, FD_CLOSE, FD_ACCEPT and sl@0: * FD_CONNECT that indicate which sl@0: * events are currently being sl@0: * selected. */ sl@0: int acceptEventCount; /* Count of the current number of sl@0: * FD_ACCEPTs that have arrived and sl@0: * not yet processed. */ sl@0: Tcl_TcpAcceptProc *acceptProc; /* Proc to call on accept. */ sl@0: ClientData acceptProcData; /* The data for the accept proc. */ sl@0: int lastError; /* Error code from last message. */ sl@0: struct SocketInfo *nextPtr; /* The next socket on the per-thread sl@0: * socket list. */ sl@0: } SocketInfo; sl@0: sl@0: /* sl@0: * The following structure is what is added to the Tcl event queue when sl@0: * a socket event occurs. sl@0: */ sl@0: sl@0: typedef struct SocketEvent { sl@0: Tcl_Event header; /* Information that is standard for sl@0: * all events. */ sl@0: SOCKET socket; /* Socket descriptor that is ready. Used sl@0: * to find the SocketInfo structure for sl@0: * the file (can't point directly to the sl@0: * SocketInfo structure because it could sl@0: * go away while the event is queued). */ sl@0: } SocketEvent; sl@0: sl@0: /* sl@0: * This defines the minimum buffersize maintained by the kernel. sl@0: */ sl@0: sl@0: #define TCP_BUFFER_SIZE 4096 sl@0: sl@0: /* sl@0: * The following macros may be used to set the flags field of sl@0: * a SocketInfo structure. sl@0: */ sl@0: sl@0: #define SOCKET_ASYNC (1<<0) /* The socket is in blocking sl@0: * mode. */ sl@0: #define SOCKET_EOF (1<<1) /* A zero read happened on sl@0: * the socket. */ sl@0: #define SOCKET_ASYNC_CONNECT (1<<2) /* This socket uses async sl@0: * connect. */ sl@0: #define SOCKET_PENDING (1<<3) /* A message has been sent sl@0: * for this socket */ sl@0: sl@0: typedef struct ThreadSpecificData { sl@0: HWND hwnd; /* Handle to window for socket messages. */ sl@0: HANDLE socketThread; /* Thread handling the window */ sl@0: Tcl_ThreadId threadId; /* Parent thread. */ sl@0: HANDLE readyEvent; /* Event indicating that a socket event is sl@0: * ready. Also used to indicate that the sl@0: * socketThread has been initialized and has sl@0: * started. */ sl@0: HANDLE socketListLock; /* Win32 Event to lock the socketList */ sl@0: SocketInfo *socketList; /* Every open socket in this thread has an sl@0: * entry on this list. */ sl@0: } ThreadSpecificData; sl@0: sl@0: static Tcl_ThreadDataKey dataKey; sl@0: static WNDCLASS windowClass; sl@0: sl@0: /* sl@0: * Static functions defined in this file. sl@0: */ sl@0: sl@0: static SocketInfo * CreateSocket _ANSI_ARGS_((Tcl_Interp *interp, sl@0: int port, CONST char *host, sl@0: int server, CONST char *myaddr, sl@0: int myport, int async)); sl@0: static int CreateSocketAddress _ANSI_ARGS_( sl@0: (LPSOCKADDR_IN sockaddrPtr, sl@0: CONST char *host, int port)); sl@0: static void InitSockets _ANSI_ARGS_((void)); sl@0: static SocketInfo * NewSocketInfo _ANSI_ARGS_((SOCKET socket)); sl@0: static Tcl_EventCheckProc SocketCheckProc; sl@0: static Tcl_EventProc SocketEventProc; sl@0: static void SocketExitHandler _ANSI_ARGS_(( sl@0: ClientData clientData)); sl@0: static LRESULT CALLBACK SocketProc _ANSI_ARGS_((HWND hwnd, sl@0: UINT message, WPARAM wParam, sl@0: LPARAM lParam)); sl@0: static Tcl_EventSetupProc SocketSetupProc; sl@0: static int SocketsEnabled _ANSI_ARGS_((void)); sl@0: static void TcpAccept _ANSI_ARGS_((SocketInfo *infoPtr)); sl@0: static Tcl_DriverBlockModeProc TcpBlockProc; sl@0: static Tcl_DriverCloseProc TcpCloseProc; sl@0: static Tcl_DriverSetOptionProc TcpSetOptionProc; sl@0: static Tcl_DriverGetOptionProc TcpGetOptionProc; sl@0: static Tcl_DriverInputProc TcpInputProc; sl@0: static Tcl_DriverOutputProc TcpOutputProc; sl@0: static Tcl_DriverWatchProc TcpWatchProc; sl@0: static Tcl_DriverGetHandleProc TcpGetHandleProc; sl@0: static int WaitForSocketEvent _ANSI_ARGS_(( sl@0: SocketInfo *infoPtr, int events, sl@0: int *errorCodePtr)); sl@0: static DWORD WINAPI SocketThread _ANSI_ARGS_((LPVOID arg)); sl@0: sl@0: static void TcpThreadActionProc _ANSI_ARGS_ (( sl@0: ClientData instanceData, int action)); sl@0: sl@0: sl@0: /* sl@0: * This structure describes the channel type structure for TCP socket sl@0: * based IO. sl@0: */ sl@0: sl@0: static Tcl_ChannelType tcpChannelType = { sl@0: "tcp", /* Type name. */ sl@0: TCL_CHANNEL_VERSION_4, /* v4 channel */ sl@0: TcpCloseProc, /* Close proc. */ sl@0: TcpInputProc, /* Input proc. */ sl@0: TcpOutputProc, /* Output proc. */ sl@0: NULL, /* Seek proc. */ sl@0: TcpSetOptionProc, /* Set option proc. */ sl@0: TcpGetOptionProc, /* Get option proc. */ sl@0: TcpWatchProc, /* Set up notifier to watch this channel. */ sl@0: TcpGetHandleProc, /* Get an OS handle from channel. */ sl@0: NULL, /* close2proc. */ sl@0: TcpBlockProc, /* Set socket into (non-)blocking mode. */ sl@0: NULL, /* flush proc. */ sl@0: NULL, /* handler proc. */ sl@0: NULL, /* wide seek proc */ sl@0: TcpThreadActionProc, /* thread action proc */ sl@0: }; sl@0: sl@0: sl@0: /* sl@0: *---------------------------------------------------------------------- sl@0: * sl@0: * InitSockets -- sl@0: * sl@0: * Initialize the socket module. Attempts to load the wsock32.dll sl@0: * library and set up the winSock function table. If successful, sl@0: * registers the event window for the socket notifier code. sl@0: * sl@0: * Assumes socketMutex is held. sl@0: * sl@0: * Results: sl@0: * None. sl@0: * sl@0: * Side effects: sl@0: * Dynamically loads wsock32.dll, and registers a new window sl@0: * class and creates a window for use in asynchronous socket sl@0: * notification. sl@0: * sl@0: *---------------------------------------------------------------------- sl@0: */ sl@0: sl@0: static void sl@0: InitSockets() sl@0: { sl@0: DWORD id; sl@0: WSADATA wsaData; sl@0: DWORD err; sl@0: ThreadSpecificData *tsdPtr = sl@0: (ThreadSpecificData *)TclThreadDataKeyGet(&dataKey); sl@0: sl@0: if (!initialized) { sl@0: initialized = 1; sl@0: Tcl_CreateExitHandler(SocketExitHandler, (ClientData) NULL); sl@0: sl@0: winSock.hModule = LoadLibraryA("wsock32.dll"); sl@0: sl@0: if (winSock.hModule == NULL) { sl@0: return; sl@0: } sl@0: sl@0: /* sl@0: * Initialize the function table. sl@0: */ sl@0: sl@0: winSock.accept = (LPFN_ACCEPT) sl@0: GetProcAddress(winSock.hModule, "accept"); sl@0: winSock.bind = (LPFN_BIND) sl@0: GetProcAddress(winSock.hModule, "bind"); sl@0: winSock.closesocket = (LPFN_CLOSESOCKET) sl@0: GetProcAddress(winSock.hModule, "closesocket"); sl@0: winSock.connect = (LPFN_CONNECT) sl@0: GetProcAddress(winSock.hModule, "connect"); sl@0: winSock.gethostbyaddr = (LPFN_GETHOSTBYADDR) sl@0: GetProcAddress(winSock.hModule, "gethostbyaddr"); sl@0: winSock.gethostbyname = (LPFN_GETHOSTBYNAME) sl@0: GetProcAddress(winSock.hModule, "gethostbyname"); sl@0: winSock.gethostname = (LPFN_GETHOSTNAME) sl@0: GetProcAddress(winSock.hModule, "gethostname"); sl@0: winSock.getpeername = (LPFN_GETPEERNAME) sl@0: GetProcAddress(winSock.hModule, "getpeername"); sl@0: winSock.getservbyname = (LPFN_GETSERVBYNAME) sl@0: GetProcAddress(winSock.hModule, "getservbyname"); sl@0: winSock.getsockname = (LPFN_GETSOCKNAME) sl@0: GetProcAddress(winSock.hModule, "getsockname"); sl@0: winSock.getsockopt = (LPFN_GETSOCKOPT) sl@0: GetProcAddress(winSock.hModule, "getsockopt"); sl@0: winSock.htons = (LPFN_HTONS) sl@0: GetProcAddress(winSock.hModule, "htons"); sl@0: winSock.inet_addr = (LPFN_INET_ADDR) sl@0: GetProcAddress(winSock.hModule, "inet_addr"); sl@0: winSock.inet_ntoa = (LPFN_INET_NTOA) sl@0: GetProcAddress(winSock.hModule, "inet_ntoa"); sl@0: winSock.ioctlsocket = (LPFN_IOCTLSOCKET) sl@0: GetProcAddress(winSock.hModule, "ioctlsocket"); sl@0: winSock.listen = (LPFN_LISTEN) sl@0: GetProcAddress(winSock.hModule, "listen"); sl@0: winSock.ntohs = (LPFN_NTOHS) sl@0: GetProcAddress(winSock.hModule, "ntohs"); sl@0: winSock.recv = (LPFN_RECV) sl@0: GetProcAddress(winSock.hModule, "recv"); sl@0: winSock.select = (LPFN_SELECT) sl@0: GetProcAddress(winSock.hModule, "select"); sl@0: winSock.send = (LPFN_SEND) sl@0: GetProcAddress(winSock.hModule, "send"); sl@0: winSock.setsockopt = (LPFN_SETSOCKOPT) sl@0: GetProcAddress(winSock.hModule, "setsockopt"); sl@0: winSock.socket = (LPFN_SOCKET) sl@0: GetProcAddress(winSock.hModule, "socket"); sl@0: winSock.WSAAsyncSelect = (LPFN_WSAASYNCSELECT) sl@0: GetProcAddress(winSock.hModule, "WSAAsyncSelect"); sl@0: winSock.WSACleanup = (LPFN_WSACLEANUP) sl@0: GetProcAddress(winSock.hModule, "WSACleanup"); sl@0: winSock.WSAGetLastError = (LPFN_WSAGETLASTERROR) sl@0: GetProcAddress(winSock.hModule, "WSAGetLastError"); sl@0: winSock.WSAStartup = (LPFN_WSASTARTUP) sl@0: GetProcAddress(winSock.hModule, "WSAStartup"); sl@0: sl@0: /* sl@0: * Now check that all fields are properly initialized. If not, sl@0: * return zero to indicate that we failed to initialize sl@0: * properly. sl@0: */ sl@0: sl@0: if ((winSock.accept == NULL) || sl@0: (winSock.bind == NULL) || sl@0: (winSock.closesocket == NULL) || sl@0: (winSock.connect == NULL) || sl@0: (winSock.gethostbyname == NULL) || sl@0: (winSock.gethostbyaddr == NULL) || sl@0: (winSock.gethostname == NULL) || sl@0: (winSock.getpeername == NULL) || sl@0: (winSock.getservbyname == NULL) || sl@0: (winSock.getsockname == NULL) || sl@0: (winSock.getsockopt == NULL) || sl@0: (winSock.htons == NULL) || sl@0: (winSock.inet_addr == NULL) || sl@0: (winSock.inet_ntoa == NULL) || sl@0: (winSock.ioctlsocket == NULL) || sl@0: (winSock.listen == NULL) || sl@0: (winSock.ntohs == NULL) || sl@0: (winSock.recv == NULL) || sl@0: (winSock.select == NULL) || sl@0: (winSock.send == NULL) || sl@0: (winSock.setsockopt == NULL) || sl@0: (winSock.socket == NULL) || sl@0: (winSock.WSAAsyncSelect == NULL) || sl@0: (winSock.WSACleanup == NULL) || sl@0: (winSock.WSAGetLastError == NULL) || sl@0: (winSock.WSAStartup == NULL)) sl@0: { sl@0: goto unloadLibrary; sl@0: } sl@0: sl@0: /* sl@0: * Create the async notification window with a new class. We sl@0: * must create a new class to avoid a Windows 95 bug that causes sl@0: * us to get the wrong message number for socket events if the sl@0: * message window is a subclass of a static control. sl@0: */ sl@0: sl@0: windowClass.style = 0; sl@0: windowClass.cbClsExtra = 0; sl@0: windowClass.cbWndExtra = 0; sl@0: windowClass.hInstance = TclWinGetTclInstance(); sl@0: windowClass.hbrBackground = NULL; sl@0: windowClass.lpszMenuName = NULL; sl@0: windowClass.lpszClassName = "TclSocket"; sl@0: windowClass.lpfnWndProc = SocketProc; sl@0: windowClass.hIcon = NULL; sl@0: windowClass.hCursor = NULL; sl@0: sl@0: if (!RegisterClassA(&windowClass)) { sl@0: TclWinConvertError(GetLastError()); sl@0: goto unloadLibrary; sl@0: } sl@0: sl@0: /* sl@0: * Initialize the winsock library and check the interface sl@0: * version actually loaded. We only ask for the 1.1 interface sl@0: * and do require that it not be less than 1.1. sl@0: */ sl@0: sl@0: #define WSA_VERSION_MAJOR 1 sl@0: #define WSA_VERSION_MINOR 1 sl@0: #define WSA_VERSION_REQD MAKEWORD(WSA_VERSION_MAJOR, WSA_VERSION_MINOR) sl@0: sl@0: if ((err = winSock.WSAStartup(WSA_VERSION_REQD, &wsaData)) != 0) { sl@0: TclWinConvertWSAError(err); sl@0: goto unloadLibrary; sl@0: } sl@0: sl@0: /* sl@0: * Note the byte positions are swapped for the comparison, so sl@0: * that 0x0002 (2.0, MAKEWORD(2,0)) doesn't look less than 0x0101 sl@0: * (1.1). We want the comparison to be 0x0200 < 0x0101. sl@0: */ sl@0: sl@0: if (MAKEWORD(HIBYTE(wsaData.wVersion), LOBYTE(wsaData.wVersion)) sl@0: < MAKEWORD(WSA_VERSION_MINOR, WSA_VERSION_MAJOR)) { sl@0: TclWinConvertWSAError(WSAVERNOTSUPPORTED); sl@0: winSock.WSACleanup(); sl@0: goto unloadLibrary; sl@0: } sl@0: sl@0: #undef WSA_VERSION_REQD sl@0: #undef WSA_VERSION_MAJOR sl@0: #undef WSA_VERSION_MINOR sl@0: } sl@0: sl@0: /* sl@0: * Check for per-thread initialization. sl@0: */ sl@0: sl@0: if (tsdPtr == NULL) { sl@0: tsdPtr = TCL_TSD_INIT(&dataKey); sl@0: tsdPtr->socketList = NULL; sl@0: tsdPtr->hwnd = NULL; sl@0: tsdPtr->threadId = Tcl_GetCurrentThread(); sl@0: tsdPtr->readyEvent = CreateEvent(NULL, FALSE, FALSE, NULL); sl@0: if (tsdPtr->readyEvent == NULL) { sl@0: goto unloadLibrary; sl@0: } sl@0: tsdPtr->socketListLock = CreateEvent(NULL, FALSE, TRUE, NULL); sl@0: if (tsdPtr->socketListLock == NULL) { sl@0: goto unloadLibrary; sl@0: } sl@0: tsdPtr->socketThread = CreateThread(NULL, 256, SocketThread, sl@0: tsdPtr, 0, &id); sl@0: if (tsdPtr->socketThread == NULL) { sl@0: goto unloadLibrary; sl@0: } sl@0: sl@0: SetThreadPriority(tsdPtr->socketThread, THREAD_PRIORITY_HIGHEST); sl@0: sl@0: /* sl@0: * Wait for the thread to signal when the window has sl@0: * been created and if it is ready to go. sl@0: */ sl@0: sl@0: WaitForSingleObject(tsdPtr->readyEvent, INFINITE); sl@0: sl@0: if (tsdPtr->hwnd == NULL) { sl@0: goto unloadLibrary; /* Trouble creating the window */ sl@0: } sl@0: sl@0: Tcl_CreateEventSource(SocketSetupProc, SocketCheckProc, NULL); sl@0: } sl@0: return; sl@0: sl@0: unloadLibrary: sl@0: TclpFinalizeSockets(); sl@0: FreeLibrary(winSock.hModule); sl@0: winSock.hModule = NULL; sl@0: return; sl@0: } sl@0: sl@0: /* sl@0: *---------------------------------------------------------------------- sl@0: * sl@0: * SocketsEnabled -- sl@0: * sl@0: * Check that the WinSock DLL is loaded and ready. sl@0: * sl@0: * Results: sl@0: * 1 if it is. sl@0: * sl@0: * Side effects: sl@0: * None. sl@0: * sl@0: *---------------------------------------------------------------------- sl@0: */ sl@0: sl@0: /* ARGSUSED */ sl@0: static int sl@0: SocketsEnabled() sl@0: { sl@0: int enabled; sl@0: Tcl_MutexLock(&socketMutex); sl@0: enabled = (winSock.hModule != NULL); sl@0: Tcl_MutexUnlock(&socketMutex); sl@0: return enabled; sl@0: } sl@0: sl@0: sl@0: /* sl@0: *---------------------------------------------------------------------- sl@0: * sl@0: * SocketExitHandler -- sl@0: * sl@0: * Callback invoked during app exit clean up to delete the socket sl@0: * communication window and to release the WinSock DLL. sl@0: * sl@0: * Results: sl@0: * None. sl@0: * sl@0: * Side effects: sl@0: * None. sl@0: * sl@0: *---------------------------------------------------------------------- sl@0: */ sl@0: sl@0: /* ARGSUSED */ sl@0: static void sl@0: SocketExitHandler(clientData) sl@0: ClientData clientData; /* Not used. */ sl@0: { sl@0: Tcl_MutexLock(&socketMutex); sl@0: if (winSock.hModule) { sl@0: /* sl@0: * Make sure the socket event handling window is cleaned-up sl@0: * for, at most, this thread. sl@0: */ sl@0: TclpFinalizeSockets(); sl@0: UnregisterClass("TclSocket", TclWinGetTclInstance()); sl@0: winSock.WSACleanup(); sl@0: FreeLibrary(winSock.hModule); sl@0: winSock.hModule = NULL; sl@0: } sl@0: initialized = 0; sl@0: hostnameInitialized = 0; sl@0: Tcl_MutexUnlock(&socketMutex); sl@0: } sl@0: sl@0: /* sl@0: *---------------------------------------------------------------------- sl@0: * sl@0: * TclpFinalizeSockets -- sl@0: * sl@0: * This function is called from Tcl_FinalizeThread to finalize sl@0: * the platform specific socket subsystem. sl@0: * Also, it may be called from within this module to cleanup sl@0: * the state if unable to initialize the sockets subsystem. sl@0: * sl@0: * Results: sl@0: * None. sl@0: * sl@0: * Side effects: sl@0: * Deletes the event source and destroys the socket thread. sl@0: * sl@0: *---------------------------------------------------------------------- sl@0: */ sl@0: sl@0: void sl@0: TclpFinalizeSockets() sl@0: { sl@0: ThreadSpecificData *tsdPtr; sl@0: sl@0: tsdPtr = (ThreadSpecificData *)TclThreadDataKeyGet(&dataKey); sl@0: if (tsdPtr != NULL) { sl@0: if (tsdPtr->socketThread != NULL) { sl@0: if (tsdPtr->hwnd != NULL) { sl@0: PostMessage(tsdPtr->hwnd, SOCKET_TERMINATE, 0, 0); sl@0: /* sl@0: * Wait for the thread to exit. This ensures that we are sl@0: * completely cleaned up before we leave this function. sl@0: */ sl@0: WaitForSingleObject(tsdPtr->readyEvent, INFINITE); sl@0: tsdPtr->hwnd = NULL; sl@0: } sl@0: CloseHandle(tsdPtr->socketThread); sl@0: tsdPtr->socketThread = NULL; sl@0: } sl@0: if (tsdPtr->readyEvent != NULL) { sl@0: CloseHandle(tsdPtr->readyEvent); sl@0: tsdPtr->readyEvent = NULL; sl@0: } sl@0: if (tsdPtr->socketListLock != NULL) { sl@0: CloseHandle(tsdPtr->socketListLock); sl@0: tsdPtr->socketListLock = NULL; sl@0: } sl@0: Tcl_DeleteEventSource(SocketSetupProc, SocketCheckProc, NULL); sl@0: } sl@0: } sl@0: sl@0: /* sl@0: *---------------------------------------------------------------------- sl@0: * sl@0: * TclpHasSockets -- sl@0: * sl@0: * This function determines whether sockets are available on the sl@0: * current system and returns an error in interp if they are not. sl@0: * Note that interp may be NULL. sl@0: * sl@0: * Results: sl@0: * Returns TCL_OK if the system supports sockets, or TCL_ERROR with sl@0: * an error in interp. sl@0: * sl@0: * Side effects: sl@0: * If not already prepared, initializes the TSD structure and sl@0: * socket message handling thread associated to the calling thread sl@0: * for the subsystem of the driver. sl@0: * sl@0: *---------------------------------------------------------------------- sl@0: */ sl@0: sl@0: int sl@0: TclpHasSockets(interp) sl@0: Tcl_Interp *interp; sl@0: { sl@0: Tcl_MutexLock(&socketMutex); sl@0: InitSockets(); sl@0: Tcl_MutexUnlock(&socketMutex); sl@0: sl@0: if (SocketsEnabled()) { sl@0: return TCL_OK; sl@0: } sl@0: if (interp != NULL) { sl@0: Tcl_AppendResult(interp, "sockets are not available on this system", sl@0: NULL); sl@0: } sl@0: return TCL_ERROR; sl@0: } sl@0: sl@0: /* sl@0: *---------------------------------------------------------------------- sl@0: * sl@0: * SocketSetupProc -- sl@0: * sl@0: * This procedure is invoked before Tcl_DoOneEvent blocks waiting sl@0: * for an event. sl@0: * sl@0: * Results: sl@0: * None. sl@0: * sl@0: * Side effects: sl@0: * Adjusts the block time if needed. sl@0: * sl@0: *---------------------------------------------------------------------- sl@0: */ sl@0: sl@0: void sl@0: SocketSetupProc(data, flags) sl@0: ClientData data; /* Not used. */ sl@0: int flags; /* Event flags as passed to Tcl_DoOneEvent. */ sl@0: { sl@0: SocketInfo *infoPtr; sl@0: Tcl_Time blockTime = { 0, 0 }; sl@0: ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); sl@0: sl@0: if (!(flags & TCL_FILE_EVENTS)) { sl@0: return; sl@0: } sl@0: sl@0: /* sl@0: * Check to see if there is a ready socket. If so, poll. sl@0: */ sl@0: sl@0: WaitForSingleObject(tsdPtr->socketListLock, INFINITE); sl@0: for (infoPtr = tsdPtr->socketList; infoPtr != NULL; sl@0: infoPtr = infoPtr->nextPtr) { sl@0: if (infoPtr->readyEvents & infoPtr->watchEvents) { sl@0: Tcl_SetMaxBlockTime(&blockTime); sl@0: break; sl@0: } sl@0: } sl@0: SetEvent(tsdPtr->socketListLock); sl@0: } sl@0: sl@0: /* sl@0: *---------------------------------------------------------------------- sl@0: * sl@0: * SocketCheckProc -- sl@0: * sl@0: * This procedure is called by Tcl_DoOneEvent to check the socket sl@0: * event source for events. sl@0: * sl@0: * Results: sl@0: * None. sl@0: * sl@0: * Side effects: sl@0: * May queue an event. sl@0: * sl@0: *---------------------------------------------------------------------- sl@0: */ sl@0: sl@0: static void sl@0: SocketCheckProc(data, flags) sl@0: ClientData data; /* Not used. */ sl@0: int flags; /* Event flags as passed to Tcl_DoOneEvent. */ sl@0: { sl@0: SocketInfo *infoPtr; sl@0: SocketEvent *evPtr; sl@0: ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); sl@0: sl@0: if (!(flags & TCL_FILE_EVENTS)) { sl@0: return; sl@0: } sl@0: sl@0: /* sl@0: * Queue events for any ready sockets that don't already have events sl@0: * queued (caused by persistent states that won't generate WinSock sl@0: * events). sl@0: */ sl@0: sl@0: WaitForSingleObject(tsdPtr->socketListLock, INFINITE); sl@0: for (infoPtr = tsdPtr->socketList; infoPtr != NULL; sl@0: infoPtr = infoPtr->nextPtr) { sl@0: if ((infoPtr->readyEvents & infoPtr->watchEvents) sl@0: && !(infoPtr->flags & SOCKET_PENDING)) { sl@0: infoPtr->flags |= SOCKET_PENDING; sl@0: evPtr = (SocketEvent *) ckalloc(sizeof(SocketEvent)); sl@0: evPtr->header.proc = SocketEventProc; sl@0: evPtr->socket = infoPtr->socket; sl@0: Tcl_QueueEvent((Tcl_Event *) evPtr, TCL_QUEUE_TAIL); sl@0: } sl@0: } sl@0: SetEvent(tsdPtr->socketListLock); sl@0: } sl@0: sl@0: /* sl@0: *---------------------------------------------------------------------- sl@0: * sl@0: * SocketEventProc -- sl@0: * sl@0: * This procedure is called by Tcl_ServiceEvent when a socket event sl@0: * reaches the front of the event queue. This procedure is sl@0: * responsible for notifying the generic channel code. sl@0: * sl@0: * Results: sl@0: * Returns 1 if the event was handled, meaning it should be removed sl@0: * from the queue. Returns 0 if the event was not handled, meaning sl@0: * it should stay on the queue. The only time the event isn't sl@0: * handled is if the TCL_FILE_EVENTS flag bit isn't set. sl@0: * sl@0: * Side effects: sl@0: * Whatever the channel callback procedures do. sl@0: * sl@0: *---------------------------------------------------------------------- sl@0: */ sl@0: sl@0: static int sl@0: SocketEventProc(evPtr, flags) sl@0: Tcl_Event *evPtr; /* Event to service. */ sl@0: int flags; /* Flags that indicate what events to sl@0: * handle, such as TCL_FILE_EVENTS. */ sl@0: { sl@0: SocketInfo *infoPtr; sl@0: SocketEvent *eventPtr = (SocketEvent *) evPtr; sl@0: int mask = 0; sl@0: int events; sl@0: ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); sl@0: sl@0: if (!(flags & TCL_FILE_EVENTS)) { sl@0: return 0; sl@0: } sl@0: sl@0: /* sl@0: * Find the specified socket on the socket list. sl@0: */ sl@0: sl@0: WaitForSingleObject(tsdPtr->socketListLock, INFINITE); sl@0: for (infoPtr = tsdPtr->socketList; infoPtr != NULL; sl@0: infoPtr = infoPtr->nextPtr) { sl@0: if (infoPtr->socket == eventPtr->socket) { sl@0: break; sl@0: } sl@0: } sl@0: SetEvent(tsdPtr->socketListLock); sl@0: sl@0: /* sl@0: * Discard events that have gone stale. sl@0: */ sl@0: sl@0: if (!infoPtr) { sl@0: return 1; sl@0: } sl@0: sl@0: infoPtr->flags &= ~SOCKET_PENDING; sl@0: sl@0: /* sl@0: * Handle connection requests directly. sl@0: */ sl@0: sl@0: if (infoPtr->readyEvents & FD_ACCEPT) { sl@0: TcpAccept(infoPtr); sl@0: return 1; sl@0: } sl@0: sl@0: /* sl@0: * Mask off unwanted events and compute the read/write mask so sl@0: * we can notify the channel. sl@0: */ sl@0: sl@0: events = infoPtr->readyEvents & infoPtr->watchEvents; sl@0: sl@0: if (events & FD_CLOSE) { sl@0: /* sl@0: * If the socket was closed and the channel is still interested sl@0: * in read events, then we need to ensure that we keep polling sl@0: * for this event until someone does something with the channel. sl@0: * Note that we do this before calling Tcl_NotifyChannel so we don't sl@0: * have to watch out for the channel being deleted out from under sl@0: * us. This may cause a redundant trip through the event loop, but sl@0: * it's simpler than trying to do unwind protection. sl@0: */ sl@0: sl@0: Tcl_Time blockTime = { 0, 0 }; sl@0: Tcl_SetMaxBlockTime(&blockTime); sl@0: mask |= TCL_READABLE|TCL_WRITABLE; sl@0: } else if (events & FD_READ) { sl@0: fd_set readFds; sl@0: struct timeval timeout; sl@0: sl@0: /* sl@0: * We must check to see if data is really available, since someone sl@0: * could have consumed the data in the meantime. Turn off async sl@0: * notification so select will work correctly. If the socket is sl@0: * still readable, notify the channel driver, otherwise reset the sl@0: * async select handler and keep waiting. sl@0: */ sl@0: sl@0: SendMessage(tsdPtr->hwnd, SOCKET_SELECT, sl@0: (WPARAM) UNSELECT, (LPARAM) infoPtr); sl@0: sl@0: FD_ZERO(&readFds); sl@0: FD_SET(infoPtr->socket, &readFds); sl@0: timeout.tv_usec = 0; sl@0: timeout.tv_sec = 0; sl@0: sl@0: if (winSock.select(0, &readFds, NULL, NULL, &timeout) != 0) { sl@0: mask |= TCL_READABLE; sl@0: } else { sl@0: infoPtr->readyEvents &= ~(FD_READ); sl@0: SendMessage(tsdPtr->hwnd, SOCKET_SELECT, sl@0: (WPARAM) SELECT, (LPARAM) infoPtr); sl@0: } sl@0: } sl@0: if (events & (FD_WRITE | FD_CONNECT)) { sl@0: mask |= TCL_WRITABLE; sl@0: if (events & FD_CONNECT && infoPtr->lastError != NO_ERROR) { sl@0: /* connect errors should also fire the readable handler. */ sl@0: mask |= TCL_READABLE; sl@0: } sl@0: } sl@0: sl@0: if (mask) { sl@0: Tcl_NotifyChannel(infoPtr->channel, mask); sl@0: } sl@0: return 1; sl@0: } sl@0: sl@0: /* sl@0: *---------------------------------------------------------------------- sl@0: * sl@0: * TcpBlockProc -- sl@0: * sl@0: * Sets a socket into blocking or non-blocking mode. sl@0: * sl@0: * Results: sl@0: * 0 if successful, errno if there was an error. sl@0: * sl@0: * Side effects: sl@0: * None. sl@0: * sl@0: *---------------------------------------------------------------------- sl@0: */ sl@0: sl@0: static int sl@0: TcpBlockProc(instanceData, mode) sl@0: ClientData instanceData; /* The socket to block/un-block. */ sl@0: int mode; /* TCL_MODE_BLOCKING or sl@0: * TCL_MODE_NONBLOCKING. */ sl@0: { sl@0: SocketInfo *infoPtr = (SocketInfo *) instanceData; sl@0: sl@0: if (mode == TCL_MODE_NONBLOCKING) { sl@0: infoPtr->flags |= SOCKET_ASYNC; sl@0: } else { sl@0: infoPtr->flags &= ~(SOCKET_ASYNC); sl@0: } sl@0: return 0; sl@0: } sl@0: sl@0: /* sl@0: *---------------------------------------------------------------------- sl@0: * sl@0: * TcpCloseProc -- sl@0: * sl@0: * This procedure is called by the generic IO level to perform sl@0: * channel type specific cleanup on a socket based channel sl@0: * when the channel is closed. sl@0: * sl@0: * Results: sl@0: * 0 if successful, the value of errno if failed. sl@0: * sl@0: * Side effects: sl@0: * Closes the socket. sl@0: * sl@0: *---------------------------------------------------------------------- sl@0: */ sl@0: sl@0: /* ARGSUSED */ sl@0: static int sl@0: TcpCloseProc(instanceData, interp) sl@0: ClientData instanceData; /* The socket to close. */ sl@0: Tcl_Interp *interp; /* Unused. */ sl@0: { sl@0: SocketInfo *infoPtr = (SocketInfo *) instanceData; sl@0: /* TIP #218 */ sl@0: int errorCode = 0; sl@0: ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); sl@0: sl@0: /* sl@0: * Check that WinSock is initialized; do not call it if not, to sl@0: * prevent system crashes. This can happen at exit time if the exit sl@0: * handler for WinSock ran before other exit handlers that want to sl@0: * use sockets. sl@0: */ sl@0: sl@0: if (SocketsEnabled()) { sl@0: sl@0: /* sl@0: * Clean up the OS socket handle. The default Windows setting sl@0: * for a socket is SO_DONTLINGER, which does a graceful shutdown sl@0: * in the background. sl@0: */ sl@0: sl@0: if (winSock.closesocket(infoPtr->socket) == SOCKET_ERROR) { sl@0: TclWinConvertWSAError((DWORD) winSock.WSAGetLastError()); sl@0: errorCode = Tcl_GetErrno(); sl@0: } sl@0: } sl@0: sl@0: /* TIP #218. Removed the code removing the structure sl@0: * from the global socket list. This is now done by sl@0: * the thread action callbacks, and only there. This sl@0: * happens before this code is called. We can free sl@0: * without fear of damanging the list. sl@0: */ sl@0: ckfree((char *) infoPtr); sl@0: return errorCode; sl@0: } sl@0: sl@0: /* sl@0: *---------------------------------------------------------------------- sl@0: * sl@0: * NewSocketInfo -- sl@0: * sl@0: * This function allocates and initializes a new SocketInfo sl@0: * structure. sl@0: * sl@0: * Results: sl@0: * Returns a newly allocated SocketInfo. sl@0: * sl@0: * Side effects: sl@0: * None, except for allocation of memory. sl@0: * sl@0: *---------------------------------------------------------------------- sl@0: */ sl@0: sl@0: static SocketInfo * sl@0: NewSocketInfo(socket) sl@0: SOCKET socket; sl@0: { sl@0: SocketInfo *infoPtr; sl@0: sl@0: infoPtr = (SocketInfo *) ckalloc((unsigned) sizeof(SocketInfo)); sl@0: infoPtr->socket = socket; sl@0: infoPtr->flags = 0; sl@0: infoPtr->watchEvents = 0; sl@0: infoPtr->readyEvents = 0; sl@0: infoPtr->selectEvents = 0; sl@0: infoPtr->acceptEventCount = 0; sl@0: infoPtr->acceptProc = NULL; sl@0: infoPtr->lastError = 0; sl@0: sl@0: /* TIP #218. Removed the code inserting the new structure sl@0: * into the global list. This is now handled in the thread sl@0: * action callbacks, and only there. sl@0: */ sl@0: infoPtr->nextPtr = NULL; sl@0: sl@0: return infoPtr; sl@0: } sl@0: sl@0: /* sl@0: *---------------------------------------------------------------------- sl@0: * sl@0: * CreateSocket -- sl@0: * sl@0: * This function opens a new socket and initializes the sl@0: * SocketInfo structure. sl@0: * sl@0: * Results: sl@0: * Returns a new SocketInfo, or NULL with an error in interp. sl@0: * sl@0: * Side effects: sl@0: * None, except for allocation of memory. sl@0: * sl@0: *---------------------------------------------------------------------- sl@0: */ sl@0: sl@0: static SocketInfo * sl@0: CreateSocket(interp, port, host, server, myaddr, myport, async) sl@0: Tcl_Interp *interp; /* For error reporting; can be NULL. */ sl@0: int port; /* Port number to open. */ sl@0: CONST char *host; /* Name of host on which to open port. */ sl@0: int server; /* 1 if socket should be a server socket, sl@0: * else 0 for a client socket. */ sl@0: CONST char *myaddr; /* Optional client-side address */ sl@0: int myport; /* Optional client-side port */ sl@0: int async; /* If nonzero, connect client socket sl@0: * asynchronously. */ sl@0: { sl@0: u_long flag = 1; /* Indicates nonblocking mode. */ sl@0: int asyncConnect = 0; /* Will be 1 if async connect is sl@0: * in progress. */ sl@0: SOCKADDR_IN sockaddr; /* Socket address */ sl@0: SOCKADDR_IN mysockaddr; /* Socket address for client */ sl@0: SOCKET sock = INVALID_SOCKET; sl@0: SocketInfo *infoPtr; /* The returned value. */ sl@0: ThreadSpecificData *tsdPtr = sl@0: (ThreadSpecificData *)TclThreadDataKeyGet(&dataKey); sl@0: sl@0: /* sl@0: * Check that WinSock is initialized; do not call it if not, to sl@0: * prevent system crashes. This can happen at exit time if the exit sl@0: * handler for WinSock ran before other exit handlers that want to sl@0: * use sockets. sl@0: */ sl@0: sl@0: if (!SocketsEnabled()) { sl@0: return NULL; sl@0: } sl@0: sl@0: if (! CreateSocketAddress(&sockaddr, host, port)) { sl@0: goto error; sl@0: } sl@0: if ((myaddr != NULL || myport != 0) && sl@0: ! CreateSocketAddress(&mysockaddr, myaddr, myport)) { sl@0: goto error; sl@0: } sl@0: sl@0: sock = winSock.socket(AF_INET, SOCK_STREAM, 0); sl@0: if (sock == INVALID_SOCKET) { sl@0: goto error; sl@0: } sl@0: sl@0: /* sl@0: * Win-NT has a misfeature that sockets are inherited in child sl@0: * processes by default. Turn off the inherit bit. sl@0: */ sl@0: sl@0: SetHandleInformation( (HANDLE) sock, HANDLE_FLAG_INHERIT, 0 ); sl@0: sl@0: /* sl@0: * Set kernel space buffering sl@0: */ sl@0: sl@0: TclSockMinimumBuffers((int) sock, TCP_BUFFER_SIZE); sl@0: sl@0: if (server) { sl@0: /* sl@0: * Bind to the specified port. Note that we must not call setsockopt sl@0: * with SO_REUSEADDR because Microsoft allows addresses to be reused sl@0: * even if they are still in use. sl@0: * sl@0: * Bind should not be affected by the socket having already been sl@0: * set into nonblocking mode. If there is trouble, this is one place sl@0: * to look for bugs. sl@0: */ sl@0: sl@0: if (winSock.bind(sock, (SOCKADDR *) &sockaddr, sl@0: sizeof(SOCKADDR_IN)) == SOCKET_ERROR) { sl@0: goto error; sl@0: } sl@0: sl@0: /* sl@0: * Set the maximum number of pending connect requests to the sl@0: * max value allowed on each platform (Win32 and Win32s may be sl@0: * different, and there may be differences between TCP/IP stacks). sl@0: */ sl@0: sl@0: if (winSock.listen(sock, SOMAXCONN) == SOCKET_ERROR) { sl@0: goto error; sl@0: } sl@0: sl@0: /* sl@0: * Add this socket to the global list of sockets. sl@0: */ sl@0: sl@0: infoPtr = NewSocketInfo(sock); sl@0: sl@0: /* sl@0: * Set up the select mask for connection request events. sl@0: */ sl@0: sl@0: infoPtr->selectEvents = FD_ACCEPT; sl@0: infoPtr->watchEvents |= FD_ACCEPT; sl@0: sl@0: } else { sl@0: sl@0: /* sl@0: * Try to bind to a local port, if specified. sl@0: */ sl@0: sl@0: if (myaddr != NULL || myport != 0) { sl@0: if (winSock.bind(sock, (SOCKADDR *) &mysockaddr, sl@0: sizeof(SOCKADDR_IN)) == SOCKET_ERROR) { sl@0: goto error; sl@0: } sl@0: } sl@0: sl@0: /* sl@0: * Set the socket into nonblocking mode if the connect should be sl@0: * done in the background. sl@0: */ sl@0: sl@0: if (async) { sl@0: if (winSock.ioctlsocket(sock, (long) FIONBIO, &flag) == SOCKET_ERROR) { sl@0: goto error; sl@0: } sl@0: } sl@0: sl@0: /* sl@0: * Attempt to connect to the remote socket. sl@0: */ sl@0: sl@0: if (winSock.connect(sock, (SOCKADDR *) &sockaddr, sl@0: sizeof(SOCKADDR_IN)) == SOCKET_ERROR) { sl@0: TclWinConvertWSAError((DWORD) winSock.WSAGetLastError()); sl@0: if (Tcl_GetErrno() != EWOULDBLOCK) { sl@0: goto error; sl@0: } sl@0: sl@0: /* sl@0: * The connection is progressing in the background. sl@0: */ sl@0: sl@0: asyncConnect = 1; sl@0: } sl@0: sl@0: /* sl@0: * Add this socket to the global list of sockets. sl@0: */ sl@0: sl@0: infoPtr = NewSocketInfo(sock); sl@0: sl@0: /* sl@0: * Set up the select mask for read/write events. If the connect sl@0: * attempt has not completed, include connect events. sl@0: */ sl@0: sl@0: infoPtr->selectEvents = FD_READ | FD_WRITE | FD_CLOSE; sl@0: if (asyncConnect) { sl@0: infoPtr->flags |= SOCKET_ASYNC_CONNECT; sl@0: infoPtr->selectEvents |= FD_CONNECT; sl@0: } sl@0: } sl@0: sl@0: /* sl@0: * Register for interest in events in the select mask. Note that this sl@0: * automatically places the socket into non-blocking mode. sl@0: */ sl@0: sl@0: winSock.ioctlsocket(sock, (long) FIONBIO, &flag); sl@0: SendMessage(tsdPtr->hwnd, SOCKET_SELECT, sl@0: (WPARAM) SELECT, (LPARAM) infoPtr); sl@0: sl@0: return infoPtr; sl@0: sl@0: error: sl@0: TclWinConvertWSAError((DWORD) winSock.WSAGetLastError()); sl@0: if (interp != NULL) { sl@0: Tcl_AppendResult(interp, "couldn't open socket: ", sl@0: Tcl_PosixError(interp), (char *) NULL); sl@0: } sl@0: if (sock != INVALID_SOCKET) { sl@0: winSock.closesocket(sock); sl@0: } sl@0: return NULL; sl@0: } sl@0: sl@0: /* sl@0: *---------------------------------------------------------------------- sl@0: * sl@0: * CreateSocketAddress -- sl@0: * sl@0: * This function initializes a sockaddr structure for a host and port. sl@0: * sl@0: * Results: sl@0: * 1 if the host was valid, 0 if the host could not be converted to sl@0: * an IP address. sl@0: * sl@0: * Side effects: sl@0: * Fills in the *sockaddrPtr structure. sl@0: * sl@0: *---------------------------------------------------------------------- sl@0: */ sl@0: sl@0: static int sl@0: CreateSocketAddress(sockaddrPtr, host, port) sl@0: LPSOCKADDR_IN sockaddrPtr; /* Socket address */ sl@0: CONST char *host; /* Host. NULL implies INADDR_ANY */ sl@0: int port; /* Port number */ sl@0: { sl@0: struct hostent *hostent; /* Host database entry */ sl@0: struct in_addr addr; /* For 64/32 bit madness */ sl@0: sl@0: /* sl@0: * Check that WinSock is initialized; do not call it if not, to sl@0: * prevent system crashes. This can happen at exit time if the exit sl@0: * handler for WinSock ran before other exit handlers that want to sl@0: * use sockets. sl@0: */ sl@0: sl@0: if (!SocketsEnabled()) { sl@0: Tcl_SetErrno(EFAULT); sl@0: return 0; sl@0: } sl@0: sl@0: ZeroMemory(sockaddrPtr, sizeof(SOCKADDR_IN)); sl@0: sockaddrPtr->sin_family = AF_INET; sl@0: sockaddrPtr->sin_port = winSock.htons((u_short) (port & 0xFFFF)); sl@0: if (host == NULL) { sl@0: addr.s_addr = INADDR_ANY; sl@0: } else { sl@0: addr.s_addr = winSock.inet_addr(host); sl@0: if (addr.s_addr == INADDR_NONE) { sl@0: hostent = winSock.gethostbyname(host); sl@0: if (hostent != NULL) { sl@0: memcpy(&addr, hostent->h_addr, (size_t) hostent->h_length); sl@0: } else { sl@0: #ifdef EHOSTUNREACH sl@0: Tcl_SetErrno(EHOSTUNREACH); sl@0: #else sl@0: #ifdef ENXIO sl@0: Tcl_SetErrno(ENXIO); sl@0: #endif sl@0: #endif sl@0: return 0; /* Error. */ sl@0: } sl@0: } sl@0: } sl@0: sl@0: /* sl@0: * NOTE: On 64 bit machines the assignment below is rumored to not sl@0: * do the right thing. Please report errors related to this if you sl@0: * observe incorrect behavior on 64 bit machines such as DEC Alphas. sl@0: * Should we modify this code to do an explicit memcpy? sl@0: */ sl@0: sl@0: sockaddrPtr->sin_addr.s_addr = addr.s_addr; sl@0: return 1; /* Success. */ sl@0: } sl@0: sl@0: /* sl@0: *---------------------------------------------------------------------- sl@0: * sl@0: * WaitForSocketEvent -- sl@0: * sl@0: * Waits until one of the specified events occurs on a socket. sl@0: * sl@0: * Results: sl@0: * Returns 1 on success or 0 on failure, with an error code in sl@0: * errorCodePtr. sl@0: * sl@0: * Side effects: sl@0: * Processes socket events off the system queue. sl@0: * sl@0: *---------------------------------------------------------------------- sl@0: */ sl@0: sl@0: static int sl@0: WaitForSocketEvent(infoPtr, events, errorCodePtr) sl@0: SocketInfo *infoPtr; /* Information about this socket. */ sl@0: int events; /* Events to look for. */ sl@0: int *errorCodePtr; /* Where to store errors? */ sl@0: { sl@0: int result = 1; sl@0: int oldMode; sl@0: ThreadSpecificData *tsdPtr = sl@0: (ThreadSpecificData *)TclThreadDataKeyGet(&dataKey); sl@0: sl@0: /* sl@0: * Be sure to disable event servicing so we are truly modal. sl@0: */ sl@0: sl@0: oldMode = Tcl_SetServiceMode(TCL_SERVICE_NONE); sl@0: sl@0: /* sl@0: * Reset WSAAsyncSelect so we have a fresh set of events pending. sl@0: */ sl@0: sl@0: SendMessage(tsdPtr->hwnd, SOCKET_SELECT, sl@0: (WPARAM) UNSELECT, (LPARAM) infoPtr); sl@0: sl@0: SendMessage(tsdPtr->hwnd, SOCKET_SELECT, sl@0: (WPARAM) SELECT, (LPARAM) infoPtr); sl@0: sl@0: while (1) { sl@0: sl@0: if (infoPtr->lastError) { sl@0: *errorCodePtr = infoPtr->lastError; sl@0: result = 0; sl@0: break; sl@0: } else if (infoPtr->readyEvents & events) { sl@0: break; sl@0: } else if (infoPtr->flags & SOCKET_ASYNC) { sl@0: *errorCodePtr = EWOULDBLOCK; sl@0: result = 0; sl@0: break; sl@0: } sl@0: sl@0: /* sl@0: * Wait until something happens. sl@0: */ sl@0: WaitForSingleObject(tsdPtr->readyEvent, INFINITE); sl@0: } sl@0: sl@0: (void) Tcl_SetServiceMode(oldMode); sl@0: return result; sl@0: } sl@0: sl@0: /* sl@0: *---------------------------------------------------------------------- sl@0: * sl@0: * Tcl_OpenTcpClient -- sl@0: * sl@0: * Opens a TCP client socket and creates a channel around it. sl@0: * sl@0: * Results: sl@0: * The channel or NULL if failed. An error message is returned sl@0: * in the interpreter on failure. sl@0: * sl@0: * Side effects: sl@0: * Opens a client socket and creates a new channel. sl@0: * sl@0: *---------------------------------------------------------------------- sl@0: */ sl@0: sl@0: Tcl_Channel sl@0: Tcl_OpenTcpClient(interp, port, host, myaddr, myport, async) sl@0: Tcl_Interp *interp; /* For error reporting; can be NULL. */ sl@0: int port; /* Port number to open. */ sl@0: CONST char *host; /* Host on which to open port. */ sl@0: CONST char *myaddr; /* Client-side address */ sl@0: int myport; /* Client-side port */ sl@0: int async; /* If nonzero, should connect sl@0: * client socket asynchronously. */ sl@0: { sl@0: SocketInfo *infoPtr; sl@0: char channelName[16 + TCL_INTEGER_SPACE]; sl@0: sl@0: if (TclpHasSockets(interp) != TCL_OK) { sl@0: return NULL; sl@0: } sl@0: sl@0: /* sl@0: * Create a new client socket and wrap it in a channel. sl@0: */ sl@0: sl@0: infoPtr = CreateSocket(interp, port, host, 0, myaddr, myport, async); sl@0: if (infoPtr == NULL) { sl@0: return NULL; sl@0: } sl@0: sl@0: wsprintfA(channelName, "sock%d", infoPtr->socket); sl@0: sl@0: infoPtr->channel = Tcl_CreateChannel(&tcpChannelType, channelName, sl@0: (ClientData) infoPtr, (TCL_READABLE | TCL_WRITABLE)); sl@0: if (Tcl_SetChannelOption(interp, infoPtr->channel, "-translation", sl@0: "auto crlf") == TCL_ERROR) { sl@0: Tcl_Close((Tcl_Interp *) NULL, infoPtr->channel); sl@0: return (Tcl_Channel) NULL; sl@0: } sl@0: if (Tcl_SetChannelOption(NULL, infoPtr->channel, "-eofchar", "") sl@0: == TCL_ERROR) { sl@0: Tcl_Close((Tcl_Interp *) NULL, infoPtr->channel); sl@0: return (Tcl_Channel) NULL; sl@0: } sl@0: return infoPtr->channel; sl@0: } sl@0: sl@0: /* sl@0: *---------------------------------------------------------------------- sl@0: * sl@0: * Tcl_MakeTcpClientChannel -- sl@0: * sl@0: * Creates a Tcl_Channel from an existing client TCP socket. sl@0: * sl@0: * Results: sl@0: * The Tcl_Channel wrapped around the preexisting TCP socket. sl@0: * sl@0: * Side effects: sl@0: * None. sl@0: * sl@0: * NOTE: Code contributed by Mark Diekhans (markd@grizzly.com) sl@0: * sl@0: *---------------------------------------------------------------------- sl@0: */ sl@0: sl@0: Tcl_Channel sl@0: Tcl_MakeTcpClientChannel(sock) sl@0: ClientData sock; /* The socket to wrap up into a channel. */ sl@0: { sl@0: SocketInfo *infoPtr; sl@0: char channelName[16 + TCL_INTEGER_SPACE]; sl@0: ThreadSpecificData *tsdPtr; sl@0: sl@0: if (TclpHasSockets(NULL) != TCL_OK) { sl@0: return NULL; sl@0: } sl@0: sl@0: tsdPtr = (ThreadSpecificData *)TclThreadDataKeyGet(&dataKey); sl@0: sl@0: /* sl@0: * Set kernel space buffering and non-blocking. sl@0: */ sl@0: sl@0: TclSockMinimumBuffers((int) sock, TCP_BUFFER_SIZE); sl@0: sl@0: infoPtr = NewSocketInfo((SOCKET) sock); sl@0: sl@0: /* sl@0: * Start watching for read/write events on the socket. sl@0: */ sl@0: sl@0: infoPtr->selectEvents = FD_READ | FD_CLOSE | FD_WRITE; sl@0: SendMessage(tsdPtr->hwnd, SOCKET_SELECT, sl@0: (WPARAM) SELECT, (LPARAM) infoPtr); sl@0: sl@0: wsprintfA(channelName, "sock%d", infoPtr->socket); sl@0: infoPtr->channel = Tcl_CreateChannel(&tcpChannelType, channelName, sl@0: (ClientData) infoPtr, (TCL_READABLE | TCL_WRITABLE)); sl@0: Tcl_SetChannelOption(NULL, infoPtr->channel, "-translation", "auto crlf"); sl@0: return infoPtr->channel; sl@0: } sl@0: sl@0: /* sl@0: *---------------------------------------------------------------------- sl@0: * sl@0: * Tcl_OpenTcpServer -- sl@0: * sl@0: * Opens a TCP server socket and creates a channel around it. sl@0: * sl@0: * Results: sl@0: * The channel or NULL if failed. An error message is returned sl@0: * in the interpreter on failure. sl@0: * sl@0: * Side effects: sl@0: * Opens a server socket and creates a new channel. sl@0: * sl@0: *---------------------------------------------------------------------- sl@0: */ sl@0: sl@0: Tcl_Channel sl@0: Tcl_OpenTcpServer(interp, port, host, acceptProc, acceptProcData) sl@0: Tcl_Interp *interp; /* For error reporting - may be sl@0: * NULL. */ sl@0: int port; /* Port number to open. */ sl@0: CONST char *host; /* Name of local host. */ sl@0: Tcl_TcpAcceptProc *acceptProc; /* Callback for accepting connections sl@0: * from new clients. */ sl@0: ClientData acceptProcData; /* Data for the callback. */ sl@0: { sl@0: SocketInfo *infoPtr; sl@0: char channelName[16 + TCL_INTEGER_SPACE]; sl@0: sl@0: if (TclpHasSockets(interp) != TCL_OK) { sl@0: return NULL; sl@0: } sl@0: sl@0: /* sl@0: * Create a new client socket and wrap it in a channel. sl@0: */ sl@0: sl@0: infoPtr = CreateSocket(interp, port, host, 1, NULL, 0, 0); sl@0: if (infoPtr == NULL) { sl@0: return NULL; sl@0: } sl@0: sl@0: infoPtr->acceptProc = acceptProc; sl@0: infoPtr->acceptProcData = acceptProcData; sl@0: sl@0: wsprintfA(channelName, "sock%d", infoPtr->socket); sl@0: sl@0: infoPtr->channel = Tcl_CreateChannel(&tcpChannelType, channelName, sl@0: (ClientData) infoPtr, 0); sl@0: if (Tcl_SetChannelOption(interp, infoPtr->channel, "-eofchar", "") sl@0: == TCL_ERROR) { sl@0: Tcl_Close((Tcl_Interp *) NULL, infoPtr->channel); sl@0: return (Tcl_Channel) NULL; sl@0: } sl@0: sl@0: return infoPtr->channel; sl@0: } sl@0: sl@0: /* sl@0: *---------------------------------------------------------------------- sl@0: * sl@0: * TcpAccept -- sl@0: * Accept a TCP socket connection. This is called by sl@0: * SocketEventProc and it in turns calls the registered accept sl@0: * procedure. sl@0: * sl@0: * Results: sl@0: * None. sl@0: * sl@0: * Side effects: sl@0: * Invokes the accept proc which may invoke arbitrary Tcl code. sl@0: * sl@0: *---------------------------------------------------------------------- sl@0: */ sl@0: sl@0: static void sl@0: TcpAccept(infoPtr) sl@0: SocketInfo *infoPtr; /* Socket to accept. */ sl@0: { sl@0: SOCKET newSocket; sl@0: SocketInfo *newInfoPtr; sl@0: SOCKADDR_IN addr; sl@0: int len; sl@0: char channelName[16 + TCL_INTEGER_SPACE]; sl@0: ThreadSpecificData *tsdPtr = sl@0: (ThreadSpecificData *)TclThreadDataKeyGet(&dataKey); sl@0: sl@0: /* sl@0: * Accept the incoming connection request. sl@0: */ sl@0: sl@0: len = sizeof(SOCKADDR_IN); sl@0: sl@0: newSocket = winSock.accept(infoPtr->socket, (SOCKADDR *)&addr, sl@0: &len); sl@0: sl@0: /* sl@0: * Clear the ready mask so we can detect the next connection request. sl@0: * Note that connection requests are level triggered, so if there is sl@0: * a request already pending, a new event will be generated. sl@0: */ sl@0: sl@0: if (newSocket == INVALID_SOCKET) { sl@0: infoPtr->acceptEventCount = 0; sl@0: infoPtr->readyEvents &= ~(FD_ACCEPT); sl@0: return; sl@0: } sl@0: sl@0: /* sl@0: * It is possible that more than one FD_ACCEPT has been sent, so an extra sl@0: * count must be kept. Decrement the count, and reset the readyEvent bit sl@0: * if the count is no longer > 0. sl@0: */ sl@0: sl@0: infoPtr->acceptEventCount--; sl@0: sl@0: if (infoPtr->acceptEventCount <= 0) { sl@0: infoPtr->readyEvents &= ~(FD_ACCEPT); sl@0: } sl@0: sl@0: /* sl@0: * Win-NT has a misfeature that sockets are inherited in child sl@0: * processes by default. Turn off the inherit bit. sl@0: */ sl@0: sl@0: SetHandleInformation( (HANDLE) newSocket, HANDLE_FLAG_INHERIT, 0 ); sl@0: sl@0: /* sl@0: * Add this socket to the global list of sockets. sl@0: */ sl@0: sl@0: newInfoPtr = NewSocketInfo(newSocket); sl@0: sl@0: /* sl@0: * Select on read/write events and create the channel. sl@0: */ sl@0: sl@0: newInfoPtr->selectEvents = (FD_READ | FD_WRITE | FD_CLOSE); sl@0: SendMessage(tsdPtr->hwnd, SOCKET_SELECT, sl@0: (WPARAM) SELECT, (LPARAM) newInfoPtr); sl@0: sl@0: wsprintfA(channelName, "sock%d", newInfoPtr->socket); sl@0: newInfoPtr->channel = Tcl_CreateChannel(&tcpChannelType, channelName, sl@0: (ClientData) newInfoPtr, (TCL_READABLE | TCL_WRITABLE)); sl@0: if (Tcl_SetChannelOption(NULL, newInfoPtr->channel, "-translation", sl@0: "auto crlf") == TCL_ERROR) { sl@0: Tcl_Close((Tcl_Interp *) NULL, newInfoPtr->channel); sl@0: return; sl@0: } sl@0: if (Tcl_SetChannelOption(NULL, newInfoPtr->channel, "-eofchar", "") sl@0: == TCL_ERROR) { sl@0: Tcl_Close((Tcl_Interp *) NULL, newInfoPtr->channel); sl@0: return; sl@0: } sl@0: sl@0: /* sl@0: * Invoke the accept callback procedure. sl@0: */ sl@0: sl@0: if (infoPtr->acceptProc != NULL) { sl@0: (infoPtr->acceptProc) (infoPtr->acceptProcData, sl@0: newInfoPtr->channel, sl@0: winSock.inet_ntoa(addr.sin_addr), sl@0: winSock.ntohs(addr.sin_port)); sl@0: } sl@0: } sl@0: sl@0: /* sl@0: *---------------------------------------------------------------------- sl@0: * sl@0: * TcpInputProc -- sl@0: * sl@0: * This procedure is called by the generic IO level to read data from sl@0: * a socket based channel. sl@0: * sl@0: * Results: sl@0: * The number of bytes read or -1 on error. sl@0: * sl@0: * Side effects: sl@0: * Consumes input from the socket. sl@0: * sl@0: *---------------------------------------------------------------------- sl@0: */ sl@0: sl@0: static int sl@0: TcpInputProc(instanceData, buf, toRead, errorCodePtr) sl@0: ClientData instanceData; /* The socket state. */ sl@0: char *buf; /* Where to store data. */ sl@0: int toRead; /* Maximum number of bytes to read. */ sl@0: int *errorCodePtr; /* Where to store error codes. */ sl@0: { sl@0: SocketInfo *infoPtr = (SocketInfo *) instanceData; sl@0: int bytesRead; sl@0: DWORD error; sl@0: ThreadSpecificData *tsdPtr = sl@0: (ThreadSpecificData *)TclThreadDataKeyGet(&dataKey); sl@0: sl@0: *errorCodePtr = 0; sl@0: sl@0: /* sl@0: * Check that WinSock is initialized; do not call it if not, to sl@0: * prevent system crashes. This can happen at exit time if the exit sl@0: * handler for WinSock ran before other exit handlers that want to sl@0: * use sockets. sl@0: */ sl@0: sl@0: if (!SocketsEnabled()) { sl@0: *errorCodePtr = EFAULT; sl@0: return -1; sl@0: } sl@0: sl@0: /* sl@0: * First check to see if EOF was already detected, to prevent sl@0: * calling the socket stack after the first time EOF is detected. sl@0: */ sl@0: sl@0: if (infoPtr->flags & SOCKET_EOF) { sl@0: return 0; sl@0: } sl@0: sl@0: /* sl@0: * Check to see if the socket is connected before trying to read. sl@0: */ sl@0: sl@0: if ((infoPtr->flags & SOCKET_ASYNC_CONNECT) sl@0: && ! WaitForSocketEvent(infoPtr, FD_CONNECT, errorCodePtr)) { sl@0: return -1; sl@0: } sl@0: sl@0: /* sl@0: * No EOF, and it is connected, so try to read more from the socket. sl@0: * Note that we clear the FD_READ bit because read events are level sl@0: * triggered so a new event will be generated if there is still data sl@0: * available to be read. We have to simulate blocking behavior here sl@0: * since we are always using non-blocking sockets. sl@0: */ sl@0: sl@0: while (1) { sl@0: SendMessage(tsdPtr->hwnd, SOCKET_SELECT, sl@0: (WPARAM) UNSELECT, (LPARAM) infoPtr); sl@0: bytesRead = winSock.recv(infoPtr->socket, buf, toRead, 0); sl@0: infoPtr->readyEvents &= ~(FD_READ); sl@0: sl@0: /* sl@0: * Check for end-of-file condition or successful read. sl@0: */ sl@0: sl@0: if (bytesRead == 0) { sl@0: infoPtr->flags |= SOCKET_EOF; sl@0: } sl@0: if (bytesRead != SOCKET_ERROR) { sl@0: break; sl@0: } sl@0: sl@0: /* sl@0: * If an error occurs after the FD_CLOSE has arrived, sl@0: * then ignore the error and report an EOF. sl@0: */ sl@0: sl@0: if (infoPtr->readyEvents & FD_CLOSE) { sl@0: infoPtr->flags |= SOCKET_EOF; sl@0: bytesRead = 0; sl@0: break; sl@0: } sl@0: sl@0: /* sl@0: * Check for error condition or underflow in non-blocking case. sl@0: */ sl@0: sl@0: error = winSock.WSAGetLastError(); sl@0: if ((infoPtr->flags & SOCKET_ASYNC) || (error != WSAEWOULDBLOCK)) { sl@0: TclWinConvertWSAError(error); sl@0: *errorCodePtr = Tcl_GetErrno(); sl@0: bytesRead = -1; sl@0: break; sl@0: } sl@0: sl@0: /* sl@0: * In the blocking case, wait until the file becomes readable sl@0: * or closed and try again. sl@0: */ sl@0: sl@0: if (!WaitForSocketEvent(infoPtr, FD_READ|FD_CLOSE, errorCodePtr)) { sl@0: bytesRead = -1; sl@0: break; sl@0: } sl@0: } sl@0: sl@0: SendMessage(tsdPtr->hwnd, SOCKET_SELECT, sl@0: (WPARAM) SELECT, (LPARAM) infoPtr); sl@0: sl@0: return bytesRead; sl@0: } sl@0: sl@0: /* sl@0: *---------------------------------------------------------------------- sl@0: * sl@0: * TcpOutputProc -- sl@0: * sl@0: * This procedure is called by the generic IO level to write data sl@0: * to a socket based channel. sl@0: * sl@0: * Results: sl@0: * The number of bytes written or -1 on failure. sl@0: * sl@0: * Side effects: sl@0: * Produces output on the socket. sl@0: * sl@0: *---------------------------------------------------------------------- sl@0: */ sl@0: sl@0: static int sl@0: TcpOutputProc(instanceData, buf, toWrite, errorCodePtr) sl@0: ClientData instanceData; /* The socket state. */ sl@0: CONST char *buf; /* Where to get data. */ sl@0: int toWrite; /* Maximum number of bytes to write. */ sl@0: int *errorCodePtr; /* Where to store error codes. */ sl@0: { sl@0: SocketInfo *infoPtr = (SocketInfo *) instanceData; sl@0: int bytesWritten; sl@0: DWORD error; sl@0: ThreadSpecificData *tsdPtr = sl@0: (ThreadSpecificData *)TclThreadDataKeyGet(&dataKey); sl@0: sl@0: *errorCodePtr = 0; sl@0: sl@0: /* sl@0: * Check that WinSock is initialized; do not call it if not, to sl@0: * prevent system crashes. This can happen at exit time if the exit sl@0: * handler for WinSock ran before other exit handlers that want to sl@0: * use sockets. sl@0: */ sl@0: sl@0: if (!SocketsEnabled()) { sl@0: *errorCodePtr = EFAULT; sl@0: return -1; sl@0: } sl@0: sl@0: /* sl@0: * Check to see if the socket is connected before trying to write. sl@0: */ sl@0: sl@0: if ((infoPtr->flags & SOCKET_ASYNC_CONNECT) sl@0: && ! WaitForSocketEvent(infoPtr, FD_CONNECT, errorCodePtr)) { sl@0: return -1; sl@0: } sl@0: sl@0: while (1) { sl@0: SendMessage(tsdPtr->hwnd, SOCKET_SELECT, sl@0: (WPARAM) UNSELECT, (LPARAM) infoPtr); sl@0: sl@0: bytesWritten = winSock.send(infoPtr->socket, buf, toWrite, 0); sl@0: if (bytesWritten != SOCKET_ERROR) { sl@0: /* sl@0: * Since Windows won't generate a new write event until we hit sl@0: * an overflow condition, we need to force the event loop to sl@0: * poll until the condition changes. sl@0: */ sl@0: sl@0: if (infoPtr->watchEvents & FD_WRITE) { sl@0: Tcl_Time blockTime = { 0, 0 }; sl@0: Tcl_SetMaxBlockTime(&blockTime); sl@0: } sl@0: break; sl@0: } sl@0: sl@0: /* sl@0: * Check for error condition or overflow. In the event of overflow, we sl@0: * need to clear the FD_WRITE flag so we can detect the next writable sl@0: * event. Note that Windows only sends a new writable event after a sl@0: * send fails with WSAEWOULDBLOCK. sl@0: */ sl@0: sl@0: error = winSock.WSAGetLastError(); sl@0: if (error == WSAEWOULDBLOCK) { sl@0: infoPtr->readyEvents &= ~(FD_WRITE); sl@0: if (infoPtr->flags & SOCKET_ASYNC) { sl@0: *errorCodePtr = EWOULDBLOCK; sl@0: bytesWritten = -1; sl@0: break; sl@0: } sl@0: } else { sl@0: TclWinConvertWSAError(error); sl@0: *errorCodePtr = Tcl_GetErrno(); sl@0: bytesWritten = -1; sl@0: break; sl@0: } sl@0: sl@0: /* sl@0: * In the blocking case, wait until the file becomes writable sl@0: * or closed and try again. sl@0: */ sl@0: sl@0: if (!WaitForSocketEvent(infoPtr, FD_WRITE|FD_CLOSE, errorCodePtr)) { sl@0: bytesWritten = -1; sl@0: break; sl@0: } sl@0: } sl@0: sl@0: SendMessage(tsdPtr->hwnd, SOCKET_SELECT, sl@0: (WPARAM) SELECT, (LPARAM) infoPtr); sl@0: sl@0: return bytesWritten; sl@0: } sl@0: sl@0: /* sl@0: *---------------------------------------------------------------------- sl@0: * sl@0: * TcpSetOptionProc -- sl@0: * sl@0: * Sets Tcp channel specific options. sl@0: * sl@0: * Results: sl@0: * None, unless an error happens. sl@0: * sl@0: * Side effects: sl@0: * Changes attributes of the socket at the system level. sl@0: * sl@0: *---------------------------------------------------------------------- sl@0: */ sl@0: sl@0: static int sl@0: TcpSetOptionProc ( sl@0: ClientData instanceData, /* Socket state. */ sl@0: Tcl_Interp *interp, /* For error reporting - can be NULL. */ sl@0: CONST char *optionName, /* Name of the option to set. */ sl@0: CONST char *value) /* New value for option. */ sl@0: { sl@0: SocketInfo *infoPtr; sl@0: SOCKET sock; sl@0: /* sl@0: BOOL val = FALSE; sl@0: int boolVar, rtn; sl@0: */ sl@0: /* sl@0: * Check that WinSock is initialized; do not call it if not, to sl@0: * prevent system crashes. This can happen at exit time if the exit sl@0: * handler for WinSock ran before other exit handlers that want to sl@0: * use sockets. sl@0: */ sl@0: sl@0: if (!SocketsEnabled()) { sl@0: if (interp) { sl@0: Tcl_AppendResult(interp, "winsock is not initialized", NULL); sl@0: } sl@0: return TCL_ERROR; sl@0: } sl@0: sl@0: infoPtr = (SocketInfo *) instanceData; sl@0: sock = infoPtr->socket; sl@0: sl@0: /* sl@0: if (!stricmp(optionName, "-keepalive")) { sl@0: if (Tcl_GetBoolean(interp, value, &boolVar) != TCL_OK) { sl@0: return TCL_ERROR; sl@0: } sl@0: if (boolVar) val = TRUE; sl@0: rtn = winSock.setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, sl@0: (const char *) &val, sizeof(BOOL)); sl@0: if (rtn != 0) { sl@0: TclWinConvertWSAError(winSock.WSAGetLastError()); sl@0: if (interp) { sl@0: Tcl_AppendResult(interp, "couldn't set socket option: ", sl@0: Tcl_PosixError(interp), NULL); sl@0: } sl@0: return TCL_ERROR; sl@0: } sl@0: return TCL_OK; sl@0: sl@0: } else if (!stricmp(optionName, "-nagle")) { sl@0: if (Tcl_GetBoolean(interp, value, &boolVar) != TCL_OK) { sl@0: return TCL_ERROR; sl@0: } sl@0: if (!boolVar) val = TRUE; sl@0: rtn = winSock.setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, sl@0: (const char *) &val, sizeof(BOOL)); sl@0: if (rtn != 0) { sl@0: TclWinConvertWSAError(winSock.WSAGetLastError()); sl@0: if (interp) { sl@0: Tcl_AppendResult(interp, "couldn't set socket option: ", sl@0: Tcl_PosixError(interp), NULL); sl@0: } sl@0: return TCL_ERROR; sl@0: } sl@0: return TCL_OK; sl@0: } sl@0: sl@0: return Tcl_BadChannelOption(interp, optionName, "keepalive nagle"); sl@0: */ sl@0: return Tcl_BadChannelOption(interp, optionName, ""); sl@0: } sl@0: sl@0: /* sl@0: *---------------------------------------------------------------------- sl@0: * sl@0: * TcpGetOptionProc -- sl@0: * sl@0: * Computes an option value for a TCP socket based channel, or a sl@0: * list of all options and their values. sl@0: * sl@0: * Note: This code is based on code contributed by John Haxby. sl@0: * sl@0: * Results: sl@0: * A standard Tcl result. The value of the specified option or a sl@0: * list of all options and their values is returned in the sl@0: * supplied DString. sl@0: * sl@0: * Side effects: sl@0: * None. sl@0: * sl@0: *---------------------------------------------------------------------- sl@0: */ sl@0: sl@0: static int sl@0: TcpGetOptionProc(instanceData, interp, optionName, dsPtr) sl@0: ClientData instanceData; /* Socket state. */ sl@0: Tcl_Interp *interp; /* For error reporting - can be NULL */ sl@0: CONST char *optionName; /* Name of the option to sl@0: * retrieve the value for, or sl@0: * NULL to get all options and sl@0: * their values. */ sl@0: Tcl_DString *dsPtr; /* Where to store the computed sl@0: * value; initialized by caller. */ sl@0: { sl@0: SocketInfo *infoPtr; sl@0: SOCKADDR_IN sockname; sl@0: SOCKADDR_IN peername; sl@0: struct hostent *hostEntPtr; sl@0: SOCKET sock; sl@0: int size = sizeof(SOCKADDR_IN); sl@0: size_t len = 0; sl@0: char buf[TCL_INTEGER_SPACE]; sl@0: sl@0: /* sl@0: * Check that WinSock is initialized; do not call it if not, to sl@0: * prevent system crashes. This can happen at exit time if the exit sl@0: * handler for WinSock ran before other exit handlers that want to sl@0: * use sockets. sl@0: */ sl@0: sl@0: if (!SocketsEnabled()) { sl@0: if (interp) { sl@0: Tcl_AppendResult(interp, "winsock is not initialized", NULL); sl@0: } sl@0: return TCL_ERROR; sl@0: } sl@0: sl@0: infoPtr = (SocketInfo *) instanceData; sl@0: sock = (int) infoPtr->socket; sl@0: if (optionName != (char *) NULL) { sl@0: len = strlen(optionName); sl@0: } sl@0: sl@0: if ((len > 1) && (optionName[1] == 'e') && sl@0: (strncmp(optionName, "-error", len) == 0)) { sl@0: int optlen; sl@0: DWORD err; sl@0: int ret; sl@0: sl@0: optlen = sizeof(int); sl@0: ret = TclWinGetSockOpt(sock, SOL_SOCKET, SO_ERROR, sl@0: (char *)&err, &optlen); sl@0: if (ret == SOCKET_ERROR) { sl@0: err = winSock.WSAGetLastError(); sl@0: } sl@0: if (err) { sl@0: TclWinConvertWSAError(err); sl@0: Tcl_DStringAppend(dsPtr, Tcl_ErrnoMsg(Tcl_GetErrno()), -1); sl@0: } sl@0: return TCL_OK; sl@0: } sl@0: sl@0: if ((len == 0) || sl@0: ((len > 1) && (optionName[1] == 'p') && sl@0: (strncmp(optionName, "-peername", len) == 0))) { sl@0: if (winSock.getpeername(sock, (LPSOCKADDR) &peername, &size) sl@0: == 0) { sl@0: if (len == 0) { sl@0: Tcl_DStringAppendElement(dsPtr, "-peername"); sl@0: Tcl_DStringStartSublist(dsPtr); sl@0: } sl@0: Tcl_DStringAppendElement(dsPtr, sl@0: winSock.inet_ntoa(peername.sin_addr)); sl@0: sl@0: if (peername.sin_addr.s_addr == 0) { sl@0: hostEntPtr = (struct hostent *) NULL; sl@0: } else { sl@0: hostEntPtr = winSock.gethostbyaddr( sl@0: (char *) &(peername.sin_addr), sizeof(peername.sin_addr), sl@0: AF_INET); sl@0: } sl@0: if (hostEntPtr != (struct hostent *) NULL) { sl@0: Tcl_DStringAppendElement(dsPtr, hostEntPtr->h_name); sl@0: } else { sl@0: Tcl_DStringAppendElement(dsPtr, sl@0: winSock.inet_ntoa(peername.sin_addr)); sl@0: } sl@0: TclFormatInt(buf, winSock.ntohs(peername.sin_port)); sl@0: Tcl_DStringAppendElement(dsPtr, buf); sl@0: if (len == 0) { sl@0: Tcl_DStringEndSublist(dsPtr); sl@0: } else { sl@0: return TCL_OK; sl@0: } sl@0: } else { sl@0: /* sl@0: * getpeername failed - but if we were asked for all the options sl@0: * (len==0), don't flag an error at that point because it could sl@0: * be an fconfigure request on a server socket. (which have sl@0: * no peer). {copied from unix/tclUnixChan.c} sl@0: */ sl@0: if (len) { sl@0: TclWinConvertWSAError((DWORD) winSock.WSAGetLastError()); sl@0: if (interp) { sl@0: Tcl_AppendResult(interp, "can't get peername: ", sl@0: Tcl_PosixError(interp), sl@0: (char *) NULL); sl@0: } sl@0: return TCL_ERROR; sl@0: } sl@0: } sl@0: } sl@0: sl@0: if ((len == 0) || sl@0: ((len > 1) && (optionName[1] == 's') && sl@0: (strncmp(optionName, "-sockname", len) == 0))) { sl@0: if (winSock.getsockname(sock, (LPSOCKADDR) &sockname, &size) sl@0: == 0) { sl@0: if (len == 0) { sl@0: Tcl_DStringAppendElement(dsPtr, "-sockname"); sl@0: Tcl_DStringStartSublist(dsPtr); sl@0: } sl@0: Tcl_DStringAppendElement(dsPtr, sl@0: winSock.inet_ntoa(sockname.sin_addr)); sl@0: if (sockname.sin_addr.s_addr == 0) { sl@0: hostEntPtr = (struct hostent *) NULL; sl@0: } else { sl@0: hostEntPtr = winSock.gethostbyaddr( sl@0: (char *) &(sockname.sin_addr), sizeof(peername.sin_addr), sl@0: AF_INET); sl@0: } sl@0: if (hostEntPtr != (struct hostent *) NULL) { sl@0: Tcl_DStringAppendElement(dsPtr, hostEntPtr->h_name); sl@0: } else { sl@0: Tcl_DStringAppendElement(dsPtr, sl@0: winSock.inet_ntoa(sockname.sin_addr)); sl@0: } sl@0: TclFormatInt(buf, winSock.ntohs(sockname.sin_port)); sl@0: Tcl_DStringAppendElement(dsPtr, buf); sl@0: if (len == 0) { sl@0: Tcl_DStringEndSublist(dsPtr); sl@0: } else { sl@0: return TCL_OK; sl@0: } sl@0: } else { sl@0: if (interp) { sl@0: TclWinConvertWSAError((DWORD) winSock.WSAGetLastError()); sl@0: Tcl_AppendResult(interp, "can't get sockname: ", sl@0: Tcl_PosixError(interp), sl@0: (char *) NULL); sl@0: } sl@0: return TCL_ERROR; sl@0: } sl@0: } sl@0: sl@0: /* sl@0: if (len == 0 || !strncmp(optionName, "-keepalive", len)) { sl@0: int optlen; sl@0: BOOL opt = FALSE; sl@0: sl@0: if (len == 0) { sl@0: Tcl_DStringAppendElement(dsPtr, "-keepalive"); sl@0: } sl@0: optlen = sizeof(BOOL); sl@0: winSock.getsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, (char *)&opt, sl@0: &optlen); sl@0: if (opt) { sl@0: Tcl_DStringAppendElement(dsPtr, "1"); sl@0: } else { sl@0: Tcl_DStringAppendElement(dsPtr, "0"); sl@0: } sl@0: if (len > 0) return TCL_OK; sl@0: } sl@0: sl@0: if (len == 0 || !strncmp(optionName, "-nagle", len)) { sl@0: int optlen; sl@0: BOOL opt = FALSE; sl@0: sl@0: if (len == 0) { sl@0: Tcl_DStringAppendElement(dsPtr, "-nagle"); sl@0: } sl@0: optlen = sizeof(BOOL); sl@0: winSock.getsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (char *)&opt, sl@0: &optlen); sl@0: if (opt) { sl@0: Tcl_DStringAppendElement(dsPtr, "0"); sl@0: } else { sl@0: Tcl_DStringAppendElement(dsPtr, "1"); sl@0: } sl@0: if (len > 0) return TCL_OK; sl@0: } sl@0: */ sl@0: sl@0: if (len > 0) { sl@0: /*return Tcl_BadChannelOption(interp, optionName, "peername sockname keepalive nagle");*/ sl@0: return Tcl_BadChannelOption(interp, optionName, "peername sockname"); sl@0: } sl@0: sl@0: return TCL_OK; sl@0: } sl@0: sl@0: /* sl@0: *---------------------------------------------------------------------- sl@0: * sl@0: * TcpWatchProc -- sl@0: * sl@0: * Informs the channel driver of the events that the generic sl@0: * channel code wishes to receive on this socket. sl@0: * sl@0: * Results: sl@0: * None. sl@0: * sl@0: * Side effects: sl@0: * May cause the notifier to poll if any of the specified sl@0: * conditions are already true. sl@0: * sl@0: *---------------------------------------------------------------------- sl@0: */ sl@0: sl@0: static void sl@0: TcpWatchProc(instanceData, mask) sl@0: ClientData instanceData; /* The socket state. */ sl@0: int mask; /* Events of interest; an OR-ed sl@0: * combination of TCL_READABLE, sl@0: * TCL_WRITABLE and TCL_EXCEPTION. */ sl@0: { sl@0: SocketInfo *infoPtr = (SocketInfo *) instanceData; sl@0: sl@0: /* sl@0: * Update the watch events mask. Only if the socket is not a sl@0: * server socket. Fix for SF Tcl Bug #557878. sl@0: */ sl@0: sl@0: if (!infoPtr->acceptProc) { sl@0: infoPtr->watchEvents = 0; sl@0: if (mask & TCL_READABLE) { sl@0: infoPtr->watchEvents |= (FD_READ|FD_CLOSE|FD_ACCEPT); sl@0: } sl@0: if (mask & TCL_WRITABLE) { sl@0: infoPtr->watchEvents |= (FD_WRITE|FD_CLOSE|FD_CONNECT); sl@0: } sl@0: sl@0: /* sl@0: * If there are any conditions already set, then tell the notifier to poll sl@0: * rather than block. sl@0: */ sl@0: sl@0: if (infoPtr->readyEvents & infoPtr->watchEvents) { sl@0: Tcl_Time blockTime = { 0, 0 }; sl@0: Tcl_SetMaxBlockTime(&blockTime); sl@0: } sl@0: } sl@0: } sl@0: sl@0: /* sl@0: *---------------------------------------------------------------------- sl@0: * sl@0: * TcpGetProc -- sl@0: * sl@0: * Called from Tcl_GetChannelHandle to retrieve an OS handle from inside sl@0: * a TCP socket based channel. sl@0: * sl@0: * Results: sl@0: * Returns TCL_OK with the socket in handlePtr. sl@0: * sl@0: * Side effects: sl@0: * None. sl@0: * sl@0: *---------------------------------------------------------------------- sl@0: */ sl@0: sl@0: static int sl@0: TcpGetHandleProc(instanceData, direction, handlePtr) sl@0: ClientData instanceData; /* The socket state. */ sl@0: int direction; /* Not used. */ sl@0: ClientData *handlePtr; /* Where to store the handle. */ sl@0: { sl@0: SocketInfo *statePtr = (SocketInfo *) instanceData; sl@0: sl@0: *handlePtr = (ClientData) statePtr->socket; sl@0: return TCL_OK; sl@0: } sl@0: sl@0: /* sl@0: *---------------------------------------------------------------------- sl@0: * sl@0: * SocketThread -- sl@0: * sl@0: * Helper thread used to manage the socket event handling window. sl@0: * sl@0: * Results: sl@0: * 1 if unable to create socket event window, 0 otherwise. sl@0: * sl@0: * Side effects: sl@0: * None. sl@0: * sl@0: *---------------------------------------------------------------------- sl@0: */ sl@0: sl@0: static DWORD WINAPI sl@0: SocketThread(LPVOID arg) sl@0: { sl@0: MSG msg; sl@0: ThreadSpecificData *tsdPtr = (ThreadSpecificData *)(arg); sl@0: sl@0: /* sl@0: * Create a dummy window receiving socket events. sl@0: */ sl@0: sl@0: tsdPtr->hwnd = CreateWindow("TclSocket", "TclSocket", sl@0: WS_TILED, 0, 0, 0, 0, NULL, NULL, windowClass.hInstance, arg); sl@0: sl@0: /* sl@0: * Signalize thread creator that we are done creating the window. sl@0: */ sl@0: sl@0: SetEvent(tsdPtr->readyEvent); sl@0: sl@0: /* sl@0: * If unable to create the window, exit this thread immediately. sl@0: */ sl@0: sl@0: if (tsdPtr->hwnd == NULL) { sl@0: return 1; sl@0: } sl@0: sl@0: /* sl@0: * Process all messages on the socket window until WM_QUIT. sl@0: * This threads exits only when instructed to do so by the sl@0: * call to PostMessage(SOCKET_TERMINATE) in TclpFinalizeSockets(). sl@0: */ sl@0: sl@0: while (GetMessage(&msg, NULL, 0, 0) > 0) { sl@0: DispatchMessage(&msg); sl@0: } sl@0: sl@0: /* sl@0: * This releases waiters on thread exit in TclpFinalizeSockets() sl@0: */ sl@0: sl@0: SetEvent(tsdPtr->readyEvent); sl@0: sl@0: return (DWORD)msg.wParam; sl@0: } sl@0: sl@0: sl@0: /* sl@0: *---------------------------------------------------------------------- sl@0: * sl@0: * SocketProc -- sl@0: * sl@0: * This function is called when WSAAsyncSelect has been used sl@0: * to register interest in a socket event, and the event has sl@0: * occurred. sl@0: * sl@0: * Results: sl@0: * 0 on success. sl@0: * sl@0: * Side effects: sl@0: * The flags for the given socket are updated to reflect the sl@0: * event that occured. sl@0: * sl@0: *---------------------------------------------------------------------- sl@0: */ sl@0: sl@0: static LRESULT CALLBACK sl@0: SocketProc(hwnd, message, wParam, lParam) sl@0: HWND hwnd; sl@0: UINT message; sl@0: WPARAM wParam; sl@0: LPARAM lParam; sl@0: { sl@0: int event, error; sl@0: SOCKET socket; sl@0: SocketInfo *infoPtr; sl@0: ThreadSpecificData *tsdPtr = sl@0: #ifdef _WIN64 sl@0: (ThreadSpecificData *) GetWindowLongPtr(hwnd, GWLP_USERDATA); sl@0: #else sl@0: (ThreadSpecificData *) GetWindowLong(hwnd, GWL_USERDATA); sl@0: #endif sl@0: sl@0: switch (message) { sl@0: sl@0: default: sl@0: return DefWindowProc(hwnd, message, wParam, lParam); sl@0: break; sl@0: sl@0: case WM_CREATE: sl@0: /* sl@0: * store the initial tsdPtr, it's from a different thread, so it's sl@0: * not directly accessible, but needed. sl@0: */ sl@0: sl@0: #ifdef _WIN64 sl@0: SetWindowLongPtr(hwnd, GWLP_USERDATA, sl@0: (LONG_PTR) ((LPCREATESTRUCT)lParam)->lpCreateParams); sl@0: #else sl@0: SetWindowLong(hwnd, GWL_USERDATA, sl@0: (LONG) ((LPCREATESTRUCT)lParam)->lpCreateParams); sl@0: #endif sl@0: break; sl@0: sl@0: case WM_DESTROY: sl@0: PostQuitMessage(0); sl@0: break; sl@0: sl@0: case SOCKET_MESSAGE: sl@0: event = WSAGETSELECTEVENT(lParam); sl@0: error = WSAGETSELECTERROR(lParam); sl@0: socket = (SOCKET) wParam; sl@0: sl@0: /* sl@0: * Find the specified socket on the socket list and update its sl@0: * eventState flag. sl@0: */ sl@0: sl@0: WaitForSingleObject(tsdPtr->socketListLock, INFINITE); sl@0: for (infoPtr = tsdPtr->socketList; infoPtr != NULL; sl@0: infoPtr = infoPtr->nextPtr) { sl@0: if (infoPtr->socket == socket) { sl@0: /* sl@0: * Update the socket state. sl@0: */ sl@0: sl@0: /* sl@0: * A count of FD_ACCEPTS is stored, so if an FD_CLOSE sl@0: * event happens, then clear the FD_ACCEPT count. sl@0: * Otherwise, increment the count if the current sl@0: * event is an FD_ACCEPT. sl@0: */ sl@0: sl@0: if (event & FD_CLOSE) { sl@0: infoPtr->acceptEventCount = 0; sl@0: infoPtr->readyEvents &= ~(FD_WRITE|FD_ACCEPT); sl@0: } else if (event & FD_ACCEPT) { sl@0: infoPtr->acceptEventCount++; sl@0: } sl@0: sl@0: if (event & FD_CONNECT) { sl@0: /* sl@0: * The socket is now connected, sl@0: * clear the async connect flag. sl@0: */ sl@0: sl@0: infoPtr->flags &= ~(SOCKET_ASYNC_CONNECT); sl@0: sl@0: /* sl@0: * Remember any error that occurred so we can report sl@0: * connection failures. sl@0: */ sl@0: sl@0: if (error != ERROR_SUCCESS) { sl@0: TclWinConvertWSAError((DWORD) error); sl@0: infoPtr->lastError = Tcl_GetErrno(); sl@0: } sl@0: sl@0: } sl@0: if(infoPtr->flags & SOCKET_ASYNC_CONNECT) { sl@0: infoPtr->flags &= ~(SOCKET_ASYNC_CONNECT); sl@0: if (error != ERROR_SUCCESS) { sl@0: TclWinConvertWSAError((DWORD) error); sl@0: infoPtr->lastError = Tcl_GetErrno(); sl@0: } sl@0: infoPtr->readyEvents |= FD_WRITE; sl@0: } sl@0: infoPtr->readyEvents |= event; sl@0: sl@0: /* sl@0: * Wake up the Main Thread. sl@0: */ sl@0: SetEvent(tsdPtr->readyEvent); sl@0: Tcl_ThreadAlert(tsdPtr->threadId); sl@0: break; sl@0: } sl@0: } sl@0: SetEvent(tsdPtr->socketListLock); sl@0: break; sl@0: sl@0: case SOCKET_SELECT: sl@0: infoPtr = (SocketInfo *) lParam; sl@0: if (wParam == SELECT) { sl@0: sl@0: winSock.WSAAsyncSelect(infoPtr->socket, hwnd, sl@0: SOCKET_MESSAGE, infoPtr->selectEvents); sl@0: } else { sl@0: /* sl@0: * Clear the selection mask sl@0: */ sl@0: sl@0: winSock.WSAAsyncSelect(infoPtr->socket, hwnd, 0, 0); sl@0: } sl@0: break; sl@0: sl@0: case SOCKET_TERMINATE: sl@0: DestroyWindow(hwnd); sl@0: break; sl@0: } sl@0: sl@0: return 0; sl@0: } sl@0: sl@0: /* sl@0: *---------------------------------------------------------------------- sl@0: * sl@0: * Tcl_GetHostName -- sl@0: * sl@0: * Returns the name of the local host. sl@0: * sl@0: * Results: sl@0: * A string containing the network name for this machine, or sl@0: * an empty string if we can't figure out the name. The caller sl@0: * must not modify or free this string. sl@0: * sl@0: * Side effects: sl@0: * None. sl@0: * sl@0: *---------------------------------------------------------------------- sl@0: */ sl@0: sl@0: CONST char * sl@0: Tcl_GetHostName() sl@0: { sl@0: DWORD length; sl@0: WCHAR wbuf[MAX_COMPUTERNAME_LENGTH + 1]; sl@0: sl@0: Tcl_MutexLock(&socketMutex); sl@0: InitSockets(); sl@0: sl@0: if (hostnameInitialized) { sl@0: Tcl_MutexUnlock(&socketMutex); sl@0: return hostname; sl@0: } sl@0: Tcl_MutexUnlock(&socketMutex); sl@0: sl@0: if (TclpHasSockets(NULL) == TCL_OK) { sl@0: /* sl@0: * INTL: bug sl@0: */ sl@0: sl@0: if (winSock.gethostname(hostname, sizeof(hostname)) == 0) { sl@0: Tcl_MutexLock(&socketMutex); sl@0: hostnameInitialized = 1; sl@0: Tcl_MutexUnlock(&socketMutex); sl@0: return hostname; sl@0: } sl@0: } sl@0: Tcl_MutexLock(&socketMutex); sl@0: length = sizeof(hostname); sl@0: if ((*tclWinProcs->getComputerNameProc)(wbuf, &length) != 0) { sl@0: /* sl@0: * Convert string from native to UTF then change to lowercase. sl@0: */ sl@0: sl@0: Tcl_DString ds; sl@0: sl@0: lstrcpynA(hostname, Tcl_WinTCharToUtf((TCHAR *) wbuf, -1, &ds), sl@0: sizeof(hostname)); sl@0: Tcl_DStringFree(&ds); sl@0: Tcl_UtfToLower(hostname); sl@0: } else { sl@0: hostname[0] = '\0'; sl@0: } sl@0: hostnameInitialized = 1; sl@0: Tcl_MutexUnlock(&socketMutex); sl@0: return hostname; sl@0: } sl@0: sl@0: /* sl@0: *---------------------------------------------------------------------- sl@0: * sl@0: * TclWinGetSockOpt, et al. -- sl@0: * sl@0: * These functions are wrappers that let us bind the WinSock sl@0: * API dynamically so we can run on systems that don't have sl@0: * the wsock32.dll. We need wrappers for these interfaces sl@0: * because they are called from the generic Tcl code. sl@0: * sl@0: * Results: sl@0: * As defined for each function. sl@0: * sl@0: * Side effects: sl@0: * As defined for each function. sl@0: * sl@0: *---------------------------------------------------------------------- sl@0: */ sl@0: sl@0: int sl@0: TclWinGetSockOpt(SOCKET s, int level, int optname, char * optval, sl@0: int FAR *optlen) sl@0: { sl@0: /* sl@0: * Check that WinSock is initialized; do not call it if not, to sl@0: * prevent system crashes. This can happen at exit time if the exit sl@0: * handler for WinSock ran before other exit handlers that want to sl@0: * use sockets. sl@0: */ sl@0: sl@0: if (!SocketsEnabled()) { sl@0: return SOCKET_ERROR; sl@0: } sl@0: sl@0: return winSock.getsockopt(s, level, optname, optval, optlen); sl@0: } sl@0: sl@0: int sl@0: TclWinSetSockOpt(SOCKET s, int level, int optname, const char * optval, sl@0: int optlen) sl@0: { sl@0: /* sl@0: * Check that WinSock is initialized; do not call it if not, to sl@0: * prevent system crashes. This can happen at exit time if the exit sl@0: * handler for WinSock ran before other exit handlers that want to sl@0: * use sockets. sl@0: */ sl@0: if (!SocketsEnabled()) { sl@0: return SOCKET_ERROR; sl@0: } sl@0: sl@0: return winSock.setsockopt(s, level, optname, optval, optlen); sl@0: } sl@0: sl@0: u_short sl@0: TclWinNToHS(u_short netshort) sl@0: { sl@0: /* sl@0: * Check that WinSock is initialized; do not call it if not, to sl@0: * prevent system crashes. This can happen at exit time if the exit sl@0: * handler for WinSock ran before other exit handlers that want to sl@0: * use sockets. sl@0: */ sl@0: sl@0: if (!SocketsEnabled()) { sl@0: return (u_short) -1; sl@0: } sl@0: sl@0: return winSock.ntohs(netshort); sl@0: } sl@0: sl@0: struct servent * sl@0: TclWinGetServByName(const char * name, const char * proto) sl@0: { sl@0: /* sl@0: * Check that WinSock is initialized; do not call it if not, to sl@0: * prevent system crashes. This can happen at exit time if the exit sl@0: * handler for WinSock ran before other exit handlers that want to sl@0: * use sockets. sl@0: */ sl@0: if (!SocketsEnabled()) { sl@0: return (struct servent *) NULL; sl@0: } sl@0: sl@0: return winSock.getservbyname(name, proto); sl@0: } sl@0: sl@0: /* sl@0: *---------------------------------------------------------------------- sl@0: * sl@0: * TcpThreadActionProc -- sl@0: * sl@0: * Insert or remove any thread local refs to this channel. sl@0: * sl@0: * Results: sl@0: * None. sl@0: * sl@0: * Side effects: sl@0: * Changes thread local list of valid channels. sl@0: * sl@0: *---------------------------------------------------------------------- sl@0: */ sl@0: sl@0: static void sl@0: TcpThreadActionProc (instanceData, action) sl@0: ClientData instanceData; sl@0: int action; sl@0: { sl@0: ThreadSpecificData *tsdPtr; sl@0: SocketInfo *infoPtr = (SocketInfo *) instanceData; sl@0: int notifyCmd; sl@0: sl@0: if (action == TCL_CHANNEL_THREAD_INSERT) { sl@0: /* sl@0: * Ensure that socket subsystem is initialized in this thread, or sl@0: * else sockets will not work. sl@0: */ sl@0: sl@0: Tcl_MutexLock(&socketMutex); sl@0: InitSockets(); sl@0: Tcl_MutexUnlock(&socketMutex); sl@0: sl@0: tsdPtr = TCL_TSD_INIT(&dataKey); sl@0: sl@0: WaitForSingleObject(tsdPtr->socketListLock, INFINITE); sl@0: infoPtr->nextPtr = tsdPtr->socketList; sl@0: tsdPtr->socketList = infoPtr; sl@0: SetEvent(tsdPtr->socketListLock); sl@0: sl@0: notifyCmd = SELECT; sl@0: } else { sl@0: SocketInfo **nextPtrPtr; sl@0: int removed = 0; sl@0: sl@0: tsdPtr = TCL_TSD_INIT(&dataKey); sl@0: sl@0: /* TIP #218, Bugfix: All access to socketList has to be protected by the lock */ sl@0: WaitForSingleObject(tsdPtr->socketListLock, INFINITE); sl@0: for (nextPtrPtr = &(tsdPtr->socketList); (*nextPtrPtr) != NULL; sl@0: nextPtrPtr = &((*nextPtrPtr)->nextPtr)) { sl@0: if ((*nextPtrPtr) == infoPtr) { sl@0: (*nextPtrPtr) = infoPtr->nextPtr; sl@0: removed = 1; sl@0: break; sl@0: } sl@0: } sl@0: SetEvent(tsdPtr->socketListLock); sl@0: sl@0: /* sl@0: * This could happen if the channel was created in one thread sl@0: * and then moved to another without updating the thread sl@0: * local data in each thread. sl@0: */ sl@0: sl@0: if (!removed) { sl@0: Tcl_Panic("file info ptr not on thread channel list"); sl@0: } sl@0: sl@0: notifyCmd = UNSELECT; sl@0: } sl@0: sl@0: /* sl@0: * Ensure that, or stop, notifications for the socket occur in this thread. sl@0: */ sl@0: sl@0: SendMessage(tsdPtr->hwnd, SOCKET_SELECT, sl@0: (WPARAM) notifyCmd, (LPARAM) infoPtr); sl@0: }