os/persistentdata/persistentstorage/sqlite3api/TEST/TCL/tcldistribution/win/tclWinSock.c
1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/os/persistentdata/persistentstorage/sqlite3api/TEST/TCL/tcldistribution/win/tclWinSock.c Fri Jun 15 03:10:57 2012 +0200
1.3 @@ -0,0 +1,2752 @@
1.4 +/*
1.5 + * tclWinSock.c --
1.6 + *
1.7 + * This file contains Windows-specific socket related code.
1.8 + *
1.9 + * Copyright (c) 1995-1997 Sun Microsystems, Inc.
1.10 + *
1.11 + * See the file "license.terms" for information on usage and redistribution
1.12 + * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
1.13 + *
1.14 + * RCS: @(#) $Id: tclWinSock.c,v 1.36.2.6 2006/09/26 21:40:37 patthoyts Exp $
1.15 + */
1.16 +
1.17 +#include "tclWinInt.h"
1.18 +
1.19 +/*
1.20 + * Make sure to remove the redirection defines set in tclWinPort.h
1.21 + * that is in use in other sections of the core, except for us.
1.22 + */
1.23 +#undef getservbyname
1.24 +#undef getsockopt
1.25 +#undef ntohs
1.26 +#undef setsockopt
1.27 +
1.28 +/*
1.29 + * The following variable is used to tell whether this module has been
1.30 + * initialized.
1.31 + */
1.32 +
1.33 +static int initialized = 0;
1.34 +
1.35 +static int hostnameInitialized = 0;
1.36 +static char hostname[255]; /* This buffer should be big enough for
1.37 + * hostname plus domain name. */
1.38 +
1.39 +TCL_DECLARE_MUTEX(socketMutex)
1.40 +
1.41 +
1.42 +/*
1.43 + * Mingw and Cygwin may not have LPFN_* typedefs.
1.44 + */
1.45 +
1.46 +#ifdef HAVE_NO_LPFN_DECLS
1.47 + typedef SOCKET (PASCAL FAR *LPFN_ACCEPT)(SOCKET s,
1.48 + struct sockaddr FAR * addr, int FAR * addrlen);
1.49 + typedef int (PASCAL FAR *LPFN_BIND)(SOCKET s,
1.50 + const struct sockaddr FAR *addr, int namelen);
1.51 + typedef int (PASCAL FAR *LPFN_CLOSESOCKET)(SOCKET s);
1.52 + typedef int (PASCAL FAR *LPFN_CONNECT)(SOCKET s,
1.53 + const struct sockaddr FAR *name, int namelen);
1.54 + typedef struct hostent FAR * (PASCAL FAR *LPFN_GETHOSTBYADDR)
1.55 + (const char FAR *addr, int addrlen, int addrtype);
1.56 + typedef struct hostent FAR * (PASCAL FAR *LPFN_GETHOSTBYNAME)
1.57 + (const char FAR * name);
1.58 + typedef int (PASCAL FAR *LPFN_GETHOSTNAME)(char FAR * name,
1.59 + int namelen);
1.60 + typedef int (PASCAL FAR *LPFN_GETPEERNAME)(SOCKET sock,
1.61 + struct sockaddr FAR *name, int FAR *namelen);
1.62 + typedef struct servent FAR * (PASCAL FAR *LPFN_GETSERVBYNAME)
1.63 + (const char FAR * name, const char FAR * proto);
1.64 + typedef int (PASCAL FAR *LPFN_GETSOCKNAME)(SOCKET sock,
1.65 + struct sockaddr FAR *name, int FAR *namelen);
1.66 + typedef int (PASCAL FAR *LPFN_GETSOCKOPT)(SOCKET s, int level,
1.67 + int optname, char FAR * optval, int FAR *optlen);
1.68 + typedef u_short (PASCAL FAR *LPFN_HTONS)(u_short hostshort);
1.69 + typedef unsigned long (PASCAL FAR *LPFN_INET_ADDR)
1.70 + (const char FAR * cp);
1.71 + typedef char FAR * (PASCAL FAR *LPFN_INET_NTOA)
1.72 + (struct in_addr in);
1.73 + typedef int (PASCAL FAR *LPFN_IOCTLSOCKET)(SOCKET s,
1.74 + long cmd, u_long FAR *argp);
1.75 + typedef int (PASCAL FAR *LPFN_LISTEN)(SOCKET s, int backlog);
1.76 + typedef u_short (PASCAL FAR *LPFN_NTOHS)(u_short netshort);
1.77 + typedef int (PASCAL FAR *LPFN_RECV)(SOCKET s, char FAR * buf,
1.78 + int len, int flags);
1.79 + typedef int (PASCAL FAR *LPFN_SELECT)(int nfds,
1.80 + fd_set FAR * readfds, fd_set FAR * writefds,
1.81 + fd_set FAR * exceptfds,
1.82 + const struct timeval FAR * timeout);
1.83 + typedef int (PASCAL FAR *LPFN_SEND)(SOCKET s,
1.84 + const char FAR * buf, int len, int flags);
1.85 + typedef int (PASCAL FAR *LPFN_SETSOCKOPT)(SOCKET s,
1.86 + int level, int optname, const char FAR * optval,
1.87 + int optlen);
1.88 + typedef SOCKET (PASCAL FAR *LPFN_SOCKET)(int af,
1.89 + int type, int protocol);
1.90 + typedef int (PASCAL FAR *LPFN_WSAASYNCSELECT)(SOCKET s,
1.91 + HWND hWnd, u_int wMsg, long lEvent);
1.92 + typedef int (PASCAL FAR *LPFN_WSACLEANUP)(void);
1.93 + typedef int (PASCAL FAR *LPFN_WSAGETLASTERROR)(void);
1.94 + typedef int (PASCAL FAR *LPFN_WSASTARTUP)(WORD wVersionRequired,
1.95 + LPWSADATA lpWSAData);
1.96 +#endif
1.97 +
1.98 +
1.99 +/*
1.100 + * The following structure contains pointers to all of the WinSock API
1.101 + * entry points used by Tcl. It is initialized by InitSockets. Since
1.102 + * we dynamically load the Winsock DLL on demand, we must use this
1.103 + * function table to refer to functions in the winsock API.
1.104 + */
1.105 +
1.106 +static struct {
1.107 + HMODULE hModule; /* Handle to WinSock library. */
1.108 +
1.109 + /* Winsock 1.1 functions */
1.110 + LPFN_ACCEPT accept;
1.111 + LPFN_BIND bind;
1.112 + LPFN_CLOSESOCKET closesocket;
1.113 + LPFN_CONNECT connect;
1.114 + LPFN_GETHOSTBYADDR gethostbyaddr;
1.115 + LPFN_GETHOSTBYNAME gethostbyname;
1.116 + LPFN_GETHOSTNAME gethostname;
1.117 + LPFN_GETPEERNAME getpeername;
1.118 + LPFN_GETSERVBYNAME getservbyname;
1.119 + LPFN_GETSOCKNAME getsockname;
1.120 + LPFN_GETSOCKOPT getsockopt;
1.121 + LPFN_HTONS htons;
1.122 + LPFN_INET_ADDR inet_addr;
1.123 + LPFN_INET_NTOA inet_ntoa;
1.124 + LPFN_IOCTLSOCKET ioctlsocket;
1.125 + LPFN_LISTEN listen;
1.126 + LPFN_NTOHS ntohs;
1.127 + LPFN_RECV recv;
1.128 + LPFN_SELECT select;
1.129 + LPFN_SEND send;
1.130 + LPFN_SETSOCKOPT setsockopt;
1.131 + LPFN_SOCKET socket;
1.132 + LPFN_WSAASYNCSELECT WSAAsyncSelect;
1.133 + LPFN_WSACLEANUP WSACleanup;
1.134 + LPFN_WSAGETLASTERROR WSAGetLastError;
1.135 + LPFN_WSASTARTUP WSAStartup;
1.136 +
1.137 +} winSock;
1.138 +
1.139 +/*
1.140 + * The following defines declare the messages used on socket windows.
1.141 + */
1.142 +
1.143 +#define SOCKET_MESSAGE WM_USER+1
1.144 +#define SOCKET_SELECT WM_USER+2
1.145 +#define SOCKET_TERMINATE WM_USER+3
1.146 +#define SELECT TRUE
1.147 +#define UNSELECT FALSE
1.148 +
1.149 +/*
1.150 + * The following structure is used to store the data associated with
1.151 + * each socket.
1.152 + */
1.153 +
1.154 +typedef struct SocketInfo {
1.155 + Tcl_Channel channel; /* Channel associated with this
1.156 + * socket. */
1.157 + SOCKET socket; /* Windows SOCKET handle. */
1.158 + int flags; /* Bit field comprised of the flags
1.159 + * described below. */
1.160 + int watchEvents; /* OR'ed combination of FD_READ,
1.161 + * FD_WRITE, FD_CLOSE, FD_ACCEPT and
1.162 + * FD_CONNECT that indicate which
1.163 + * events are interesting. */
1.164 + int readyEvents; /* OR'ed combination of FD_READ,
1.165 + * FD_WRITE, FD_CLOSE, FD_ACCEPT and
1.166 + * FD_CONNECT that indicate which
1.167 + * events have occurred. */
1.168 + int selectEvents; /* OR'ed combination of FD_READ,
1.169 + * FD_WRITE, FD_CLOSE, FD_ACCEPT and
1.170 + * FD_CONNECT that indicate which
1.171 + * events are currently being
1.172 + * selected. */
1.173 + int acceptEventCount; /* Count of the current number of
1.174 + * FD_ACCEPTs that have arrived and
1.175 + * not yet processed. */
1.176 + Tcl_TcpAcceptProc *acceptProc; /* Proc to call on accept. */
1.177 + ClientData acceptProcData; /* The data for the accept proc. */
1.178 + int lastError; /* Error code from last message. */
1.179 + struct SocketInfo *nextPtr; /* The next socket on the per-thread
1.180 + * socket list. */
1.181 +} SocketInfo;
1.182 +
1.183 +/*
1.184 + * The following structure is what is added to the Tcl event queue when
1.185 + * a socket event occurs.
1.186 + */
1.187 +
1.188 +typedef struct SocketEvent {
1.189 + Tcl_Event header; /* Information that is standard for
1.190 + * all events. */
1.191 + SOCKET socket; /* Socket descriptor that is ready. Used
1.192 + * to find the SocketInfo structure for
1.193 + * the file (can't point directly to the
1.194 + * SocketInfo structure because it could
1.195 + * go away while the event is queued). */
1.196 +} SocketEvent;
1.197 +
1.198 +/*
1.199 + * This defines the minimum buffersize maintained by the kernel.
1.200 + */
1.201 +
1.202 +#define TCP_BUFFER_SIZE 4096
1.203 +
1.204 +/*
1.205 + * The following macros may be used to set the flags field of
1.206 + * a SocketInfo structure.
1.207 + */
1.208 +
1.209 +#define SOCKET_ASYNC (1<<0) /* The socket is in blocking
1.210 + * mode. */
1.211 +#define SOCKET_EOF (1<<1) /* A zero read happened on
1.212 + * the socket. */
1.213 +#define SOCKET_ASYNC_CONNECT (1<<2) /* This socket uses async
1.214 + * connect. */
1.215 +#define SOCKET_PENDING (1<<3) /* A message has been sent
1.216 + * for this socket */
1.217 +
1.218 +typedef struct ThreadSpecificData {
1.219 + HWND hwnd; /* Handle to window for socket messages. */
1.220 + HANDLE socketThread; /* Thread handling the window */
1.221 + Tcl_ThreadId threadId; /* Parent thread. */
1.222 + HANDLE readyEvent; /* Event indicating that a socket event is
1.223 + * ready. Also used to indicate that the
1.224 + * socketThread has been initialized and has
1.225 + * started. */
1.226 + HANDLE socketListLock; /* Win32 Event to lock the socketList */
1.227 + SocketInfo *socketList; /* Every open socket in this thread has an
1.228 + * entry on this list. */
1.229 +} ThreadSpecificData;
1.230 +
1.231 +static Tcl_ThreadDataKey dataKey;
1.232 +static WNDCLASS windowClass;
1.233 +
1.234 +/*
1.235 + * Static functions defined in this file.
1.236 + */
1.237 +
1.238 +static SocketInfo * CreateSocket _ANSI_ARGS_((Tcl_Interp *interp,
1.239 + int port, CONST char *host,
1.240 + int server, CONST char *myaddr,
1.241 + int myport, int async));
1.242 +static int CreateSocketAddress _ANSI_ARGS_(
1.243 + (LPSOCKADDR_IN sockaddrPtr,
1.244 + CONST char *host, int port));
1.245 +static void InitSockets _ANSI_ARGS_((void));
1.246 +static SocketInfo * NewSocketInfo _ANSI_ARGS_((SOCKET socket));
1.247 +static Tcl_EventCheckProc SocketCheckProc;
1.248 +static Tcl_EventProc SocketEventProc;
1.249 +static void SocketExitHandler _ANSI_ARGS_((
1.250 + ClientData clientData));
1.251 +static LRESULT CALLBACK SocketProc _ANSI_ARGS_((HWND hwnd,
1.252 + UINT message, WPARAM wParam,
1.253 + LPARAM lParam));
1.254 +static Tcl_EventSetupProc SocketSetupProc;
1.255 +static int SocketsEnabled _ANSI_ARGS_((void));
1.256 +static void TcpAccept _ANSI_ARGS_((SocketInfo *infoPtr));
1.257 +static Tcl_DriverBlockModeProc TcpBlockProc;
1.258 +static Tcl_DriverCloseProc TcpCloseProc;
1.259 +static Tcl_DriverSetOptionProc TcpSetOptionProc;
1.260 +static Tcl_DriverGetOptionProc TcpGetOptionProc;
1.261 +static Tcl_DriverInputProc TcpInputProc;
1.262 +static Tcl_DriverOutputProc TcpOutputProc;
1.263 +static Tcl_DriverWatchProc TcpWatchProc;
1.264 +static Tcl_DriverGetHandleProc TcpGetHandleProc;
1.265 +static int WaitForSocketEvent _ANSI_ARGS_((
1.266 + SocketInfo *infoPtr, int events,
1.267 + int *errorCodePtr));
1.268 +static DWORD WINAPI SocketThread _ANSI_ARGS_((LPVOID arg));
1.269 +
1.270 +static void TcpThreadActionProc _ANSI_ARGS_ ((
1.271 + ClientData instanceData, int action));
1.272 +
1.273 +
1.274 +/*
1.275 + * This structure describes the channel type structure for TCP socket
1.276 + * based IO.
1.277 + */
1.278 +
1.279 +static Tcl_ChannelType tcpChannelType = {
1.280 + "tcp", /* Type name. */
1.281 + TCL_CHANNEL_VERSION_4, /* v4 channel */
1.282 + TcpCloseProc, /* Close proc. */
1.283 + TcpInputProc, /* Input proc. */
1.284 + TcpOutputProc, /* Output proc. */
1.285 + NULL, /* Seek proc. */
1.286 + TcpSetOptionProc, /* Set option proc. */
1.287 + TcpGetOptionProc, /* Get option proc. */
1.288 + TcpWatchProc, /* Set up notifier to watch this channel. */
1.289 + TcpGetHandleProc, /* Get an OS handle from channel. */
1.290 + NULL, /* close2proc. */
1.291 + TcpBlockProc, /* Set socket into (non-)blocking mode. */
1.292 + NULL, /* flush proc. */
1.293 + NULL, /* handler proc. */
1.294 + NULL, /* wide seek proc */
1.295 + TcpThreadActionProc, /* thread action proc */
1.296 +};
1.297 +
1.298 +
1.299 +/*
1.300 + *----------------------------------------------------------------------
1.301 + *
1.302 + * InitSockets --
1.303 + *
1.304 + * Initialize the socket module. Attempts to load the wsock32.dll
1.305 + * library and set up the winSock function table. If successful,
1.306 + * registers the event window for the socket notifier code.
1.307 + *
1.308 + * Assumes socketMutex is held.
1.309 + *
1.310 + * Results:
1.311 + * None.
1.312 + *
1.313 + * Side effects:
1.314 + * Dynamically loads wsock32.dll, and registers a new window
1.315 + * class and creates a window for use in asynchronous socket
1.316 + * notification.
1.317 + *
1.318 + *----------------------------------------------------------------------
1.319 + */
1.320 +
1.321 +static void
1.322 +InitSockets()
1.323 +{
1.324 + DWORD id;
1.325 + WSADATA wsaData;
1.326 + DWORD err;
1.327 + ThreadSpecificData *tsdPtr =
1.328 + (ThreadSpecificData *)TclThreadDataKeyGet(&dataKey);
1.329 +
1.330 + if (!initialized) {
1.331 + initialized = 1;
1.332 + Tcl_CreateExitHandler(SocketExitHandler, (ClientData) NULL);
1.333 +
1.334 + winSock.hModule = LoadLibraryA("wsock32.dll");
1.335 +
1.336 + if (winSock.hModule == NULL) {
1.337 + return;
1.338 + }
1.339 +
1.340 + /*
1.341 + * Initialize the function table.
1.342 + */
1.343 +
1.344 + winSock.accept = (LPFN_ACCEPT)
1.345 + GetProcAddress(winSock.hModule, "accept");
1.346 + winSock.bind = (LPFN_BIND)
1.347 + GetProcAddress(winSock.hModule, "bind");
1.348 + winSock.closesocket = (LPFN_CLOSESOCKET)
1.349 + GetProcAddress(winSock.hModule, "closesocket");
1.350 + winSock.connect = (LPFN_CONNECT)
1.351 + GetProcAddress(winSock.hModule, "connect");
1.352 + winSock.gethostbyaddr = (LPFN_GETHOSTBYADDR)
1.353 + GetProcAddress(winSock.hModule, "gethostbyaddr");
1.354 + winSock.gethostbyname = (LPFN_GETHOSTBYNAME)
1.355 + GetProcAddress(winSock.hModule, "gethostbyname");
1.356 + winSock.gethostname = (LPFN_GETHOSTNAME)
1.357 + GetProcAddress(winSock.hModule, "gethostname");
1.358 + winSock.getpeername = (LPFN_GETPEERNAME)
1.359 + GetProcAddress(winSock.hModule, "getpeername");
1.360 + winSock.getservbyname = (LPFN_GETSERVBYNAME)
1.361 + GetProcAddress(winSock.hModule, "getservbyname");
1.362 + winSock.getsockname = (LPFN_GETSOCKNAME)
1.363 + GetProcAddress(winSock.hModule, "getsockname");
1.364 + winSock.getsockopt = (LPFN_GETSOCKOPT)
1.365 + GetProcAddress(winSock.hModule, "getsockopt");
1.366 + winSock.htons = (LPFN_HTONS)
1.367 + GetProcAddress(winSock.hModule, "htons");
1.368 + winSock.inet_addr = (LPFN_INET_ADDR)
1.369 + GetProcAddress(winSock.hModule, "inet_addr");
1.370 + winSock.inet_ntoa = (LPFN_INET_NTOA)
1.371 + GetProcAddress(winSock.hModule, "inet_ntoa");
1.372 + winSock.ioctlsocket = (LPFN_IOCTLSOCKET)
1.373 + GetProcAddress(winSock.hModule, "ioctlsocket");
1.374 + winSock.listen = (LPFN_LISTEN)
1.375 + GetProcAddress(winSock.hModule, "listen");
1.376 + winSock.ntohs = (LPFN_NTOHS)
1.377 + GetProcAddress(winSock.hModule, "ntohs");
1.378 + winSock.recv = (LPFN_RECV)
1.379 + GetProcAddress(winSock.hModule, "recv");
1.380 + winSock.select = (LPFN_SELECT)
1.381 + GetProcAddress(winSock.hModule, "select");
1.382 + winSock.send = (LPFN_SEND)
1.383 + GetProcAddress(winSock.hModule, "send");
1.384 + winSock.setsockopt = (LPFN_SETSOCKOPT)
1.385 + GetProcAddress(winSock.hModule, "setsockopt");
1.386 + winSock.socket = (LPFN_SOCKET)
1.387 + GetProcAddress(winSock.hModule, "socket");
1.388 + winSock.WSAAsyncSelect = (LPFN_WSAASYNCSELECT)
1.389 + GetProcAddress(winSock.hModule, "WSAAsyncSelect");
1.390 + winSock.WSACleanup = (LPFN_WSACLEANUP)
1.391 + GetProcAddress(winSock.hModule, "WSACleanup");
1.392 + winSock.WSAGetLastError = (LPFN_WSAGETLASTERROR)
1.393 + GetProcAddress(winSock.hModule, "WSAGetLastError");
1.394 + winSock.WSAStartup = (LPFN_WSASTARTUP)
1.395 + GetProcAddress(winSock.hModule, "WSAStartup");
1.396 +
1.397 + /*
1.398 + * Now check that all fields are properly initialized. If not,
1.399 + * return zero to indicate that we failed to initialize
1.400 + * properly.
1.401 + */
1.402 +
1.403 + if ((winSock.accept == NULL) ||
1.404 + (winSock.bind == NULL) ||
1.405 + (winSock.closesocket == NULL) ||
1.406 + (winSock.connect == NULL) ||
1.407 + (winSock.gethostbyname == NULL) ||
1.408 + (winSock.gethostbyaddr == NULL) ||
1.409 + (winSock.gethostname == NULL) ||
1.410 + (winSock.getpeername == NULL) ||
1.411 + (winSock.getservbyname == NULL) ||
1.412 + (winSock.getsockname == NULL) ||
1.413 + (winSock.getsockopt == NULL) ||
1.414 + (winSock.htons == NULL) ||
1.415 + (winSock.inet_addr == NULL) ||
1.416 + (winSock.inet_ntoa == NULL) ||
1.417 + (winSock.ioctlsocket == NULL) ||
1.418 + (winSock.listen == NULL) ||
1.419 + (winSock.ntohs == NULL) ||
1.420 + (winSock.recv == NULL) ||
1.421 + (winSock.select == NULL) ||
1.422 + (winSock.send == NULL) ||
1.423 + (winSock.setsockopt == NULL) ||
1.424 + (winSock.socket == NULL) ||
1.425 + (winSock.WSAAsyncSelect == NULL) ||
1.426 + (winSock.WSACleanup == NULL) ||
1.427 + (winSock.WSAGetLastError == NULL) ||
1.428 + (winSock.WSAStartup == NULL))
1.429 + {
1.430 + goto unloadLibrary;
1.431 + }
1.432 +
1.433 + /*
1.434 + * Create the async notification window with a new class. We
1.435 + * must create a new class to avoid a Windows 95 bug that causes
1.436 + * us to get the wrong message number for socket events if the
1.437 + * message window is a subclass of a static control.
1.438 + */
1.439 +
1.440 + windowClass.style = 0;
1.441 + windowClass.cbClsExtra = 0;
1.442 + windowClass.cbWndExtra = 0;
1.443 + windowClass.hInstance = TclWinGetTclInstance();
1.444 + windowClass.hbrBackground = NULL;
1.445 + windowClass.lpszMenuName = NULL;
1.446 + windowClass.lpszClassName = "TclSocket";
1.447 + windowClass.lpfnWndProc = SocketProc;
1.448 + windowClass.hIcon = NULL;
1.449 + windowClass.hCursor = NULL;
1.450 +
1.451 + if (!RegisterClassA(&windowClass)) {
1.452 + TclWinConvertError(GetLastError());
1.453 + goto unloadLibrary;
1.454 + }
1.455 +
1.456 + /*
1.457 + * Initialize the winsock library and check the interface
1.458 + * version actually loaded. We only ask for the 1.1 interface
1.459 + * and do require that it not be less than 1.1.
1.460 + */
1.461 +
1.462 +#define WSA_VERSION_MAJOR 1
1.463 +#define WSA_VERSION_MINOR 1
1.464 +#define WSA_VERSION_REQD MAKEWORD(WSA_VERSION_MAJOR, WSA_VERSION_MINOR)
1.465 +
1.466 + if ((err = winSock.WSAStartup(WSA_VERSION_REQD, &wsaData)) != 0) {
1.467 + TclWinConvertWSAError(err);
1.468 + goto unloadLibrary;
1.469 + }
1.470 +
1.471 + /*
1.472 + * Note the byte positions are swapped for the comparison, so
1.473 + * that 0x0002 (2.0, MAKEWORD(2,0)) doesn't look less than 0x0101
1.474 + * (1.1). We want the comparison to be 0x0200 < 0x0101.
1.475 + */
1.476 +
1.477 + if (MAKEWORD(HIBYTE(wsaData.wVersion), LOBYTE(wsaData.wVersion))
1.478 + < MAKEWORD(WSA_VERSION_MINOR, WSA_VERSION_MAJOR)) {
1.479 + TclWinConvertWSAError(WSAVERNOTSUPPORTED);
1.480 + winSock.WSACleanup();
1.481 + goto unloadLibrary;
1.482 + }
1.483 +
1.484 +#undef WSA_VERSION_REQD
1.485 +#undef WSA_VERSION_MAJOR
1.486 +#undef WSA_VERSION_MINOR
1.487 + }
1.488 +
1.489 + /*
1.490 + * Check for per-thread initialization.
1.491 + */
1.492 +
1.493 + if (tsdPtr == NULL) {
1.494 + tsdPtr = TCL_TSD_INIT(&dataKey);
1.495 + tsdPtr->socketList = NULL;
1.496 + tsdPtr->hwnd = NULL;
1.497 + tsdPtr->threadId = Tcl_GetCurrentThread();
1.498 + tsdPtr->readyEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
1.499 + if (tsdPtr->readyEvent == NULL) {
1.500 + goto unloadLibrary;
1.501 + }
1.502 + tsdPtr->socketListLock = CreateEvent(NULL, FALSE, TRUE, NULL);
1.503 + if (tsdPtr->socketListLock == NULL) {
1.504 + goto unloadLibrary;
1.505 + }
1.506 + tsdPtr->socketThread = CreateThread(NULL, 256, SocketThread,
1.507 + tsdPtr, 0, &id);
1.508 + if (tsdPtr->socketThread == NULL) {
1.509 + goto unloadLibrary;
1.510 + }
1.511 +
1.512 + SetThreadPriority(tsdPtr->socketThread, THREAD_PRIORITY_HIGHEST);
1.513 +
1.514 + /*
1.515 + * Wait for the thread to signal when the window has
1.516 + * been created and if it is ready to go.
1.517 + */
1.518 +
1.519 + WaitForSingleObject(tsdPtr->readyEvent, INFINITE);
1.520 +
1.521 + if (tsdPtr->hwnd == NULL) {
1.522 + goto unloadLibrary; /* Trouble creating the window */
1.523 + }
1.524 +
1.525 + Tcl_CreateEventSource(SocketSetupProc, SocketCheckProc, NULL);
1.526 + }
1.527 + return;
1.528 +
1.529 +unloadLibrary:
1.530 + TclpFinalizeSockets();
1.531 + FreeLibrary(winSock.hModule);
1.532 + winSock.hModule = NULL;
1.533 + return;
1.534 +}
1.535 +
1.536 +/*
1.537 + *----------------------------------------------------------------------
1.538 + *
1.539 + * SocketsEnabled --
1.540 + *
1.541 + * Check that the WinSock DLL is loaded and ready.
1.542 + *
1.543 + * Results:
1.544 + * 1 if it is.
1.545 + *
1.546 + * Side effects:
1.547 + * None.
1.548 + *
1.549 + *----------------------------------------------------------------------
1.550 + */
1.551 +
1.552 + /* ARGSUSED */
1.553 +static int
1.554 +SocketsEnabled()
1.555 +{
1.556 + int enabled;
1.557 + Tcl_MutexLock(&socketMutex);
1.558 + enabled = (winSock.hModule != NULL);
1.559 + Tcl_MutexUnlock(&socketMutex);
1.560 + return enabled;
1.561 +}
1.562 +
1.563 +
1.564 +/*
1.565 + *----------------------------------------------------------------------
1.566 + *
1.567 + * SocketExitHandler --
1.568 + *
1.569 + * Callback invoked during app exit clean up to delete the socket
1.570 + * communication window and to release the WinSock DLL.
1.571 + *
1.572 + * Results:
1.573 + * None.
1.574 + *
1.575 + * Side effects:
1.576 + * None.
1.577 + *
1.578 + *----------------------------------------------------------------------
1.579 + */
1.580 +
1.581 + /* ARGSUSED */
1.582 +static void
1.583 +SocketExitHandler(clientData)
1.584 + ClientData clientData; /* Not used. */
1.585 +{
1.586 + Tcl_MutexLock(&socketMutex);
1.587 + if (winSock.hModule) {
1.588 + /*
1.589 + * Make sure the socket event handling window is cleaned-up
1.590 + * for, at most, this thread.
1.591 + */
1.592 + TclpFinalizeSockets();
1.593 + UnregisterClass("TclSocket", TclWinGetTclInstance());
1.594 + winSock.WSACleanup();
1.595 + FreeLibrary(winSock.hModule);
1.596 + winSock.hModule = NULL;
1.597 + }
1.598 + initialized = 0;
1.599 + hostnameInitialized = 0;
1.600 + Tcl_MutexUnlock(&socketMutex);
1.601 +}
1.602 +
1.603 +/*
1.604 + *----------------------------------------------------------------------
1.605 + *
1.606 + * TclpFinalizeSockets --
1.607 + *
1.608 + * This function is called from Tcl_FinalizeThread to finalize
1.609 + * the platform specific socket subsystem.
1.610 + * Also, it may be called from within this module to cleanup
1.611 + * the state if unable to initialize the sockets subsystem.
1.612 + *
1.613 + * Results:
1.614 + * None.
1.615 + *
1.616 + * Side effects:
1.617 + * Deletes the event source and destroys the socket thread.
1.618 + *
1.619 + *----------------------------------------------------------------------
1.620 + */
1.621 +
1.622 +void
1.623 +TclpFinalizeSockets()
1.624 +{
1.625 + ThreadSpecificData *tsdPtr;
1.626 +
1.627 + tsdPtr = (ThreadSpecificData *)TclThreadDataKeyGet(&dataKey);
1.628 + if (tsdPtr != NULL) {
1.629 + if (tsdPtr->socketThread != NULL) {
1.630 + if (tsdPtr->hwnd != NULL) {
1.631 + PostMessage(tsdPtr->hwnd, SOCKET_TERMINATE, 0, 0);
1.632 + /*
1.633 + * Wait for the thread to exit. This ensures that we are
1.634 + * completely cleaned up before we leave this function.
1.635 + */
1.636 + WaitForSingleObject(tsdPtr->readyEvent, INFINITE);
1.637 + tsdPtr->hwnd = NULL;
1.638 + }
1.639 + CloseHandle(tsdPtr->socketThread);
1.640 + tsdPtr->socketThread = NULL;
1.641 + }
1.642 + if (tsdPtr->readyEvent != NULL) {
1.643 + CloseHandle(tsdPtr->readyEvent);
1.644 + tsdPtr->readyEvent = NULL;
1.645 + }
1.646 + if (tsdPtr->socketListLock != NULL) {
1.647 + CloseHandle(tsdPtr->socketListLock);
1.648 + tsdPtr->socketListLock = NULL;
1.649 + }
1.650 + Tcl_DeleteEventSource(SocketSetupProc, SocketCheckProc, NULL);
1.651 + }
1.652 +}
1.653 +
1.654 +/*
1.655 + *----------------------------------------------------------------------
1.656 + *
1.657 + * TclpHasSockets --
1.658 + *
1.659 + * This function determines whether sockets are available on the
1.660 + * current system and returns an error in interp if they are not.
1.661 + * Note that interp may be NULL.
1.662 + *
1.663 + * Results:
1.664 + * Returns TCL_OK if the system supports sockets, or TCL_ERROR with
1.665 + * an error in interp.
1.666 + *
1.667 + * Side effects:
1.668 + * If not already prepared, initializes the TSD structure and
1.669 + * socket message handling thread associated to the calling thread
1.670 + * for the subsystem of the driver.
1.671 + *
1.672 + *----------------------------------------------------------------------
1.673 + */
1.674 +
1.675 +int
1.676 +TclpHasSockets(interp)
1.677 + Tcl_Interp *interp;
1.678 +{
1.679 + Tcl_MutexLock(&socketMutex);
1.680 + InitSockets();
1.681 + Tcl_MutexUnlock(&socketMutex);
1.682 +
1.683 + if (SocketsEnabled()) {
1.684 + return TCL_OK;
1.685 + }
1.686 + if (interp != NULL) {
1.687 + Tcl_AppendResult(interp, "sockets are not available on this system",
1.688 + NULL);
1.689 + }
1.690 + return TCL_ERROR;
1.691 +}
1.692 +
1.693 +/*
1.694 + *----------------------------------------------------------------------
1.695 + *
1.696 + * SocketSetupProc --
1.697 + *
1.698 + * This procedure is invoked before Tcl_DoOneEvent blocks waiting
1.699 + * for an event.
1.700 + *
1.701 + * Results:
1.702 + * None.
1.703 + *
1.704 + * Side effects:
1.705 + * Adjusts the block time if needed.
1.706 + *
1.707 + *----------------------------------------------------------------------
1.708 + */
1.709 +
1.710 +void
1.711 +SocketSetupProc(data, flags)
1.712 + ClientData data; /* Not used. */
1.713 + int flags; /* Event flags as passed to Tcl_DoOneEvent. */
1.714 +{
1.715 + SocketInfo *infoPtr;
1.716 + Tcl_Time blockTime = { 0, 0 };
1.717 + ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
1.718 +
1.719 + if (!(flags & TCL_FILE_EVENTS)) {
1.720 + return;
1.721 + }
1.722 +
1.723 + /*
1.724 + * Check to see if there is a ready socket. If so, poll.
1.725 + */
1.726 +
1.727 + WaitForSingleObject(tsdPtr->socketListLock, INFINITE);
1.728 + for (infoPtr = tsdPtr->socketList; infoPtr != NULL;
1.729 + infoPtr = infoPtr->nextPtr) {
1.730 + if (infoPtr->readyEvents & infoPtr->watchEvents) {
1.731 + Tcl_SetMaxBlockTime(&blockTime);
1.732 + break;
1.733 + }
1.734 + }
1.735 + SetEvent(tsdPtr->socketListLock);
1.736 +}
1.737 +
1.738 +/*
1.739 + *----------------------------------------------------------------------
1.740 + *
1.741 + * SocketCheckProc --
1.742 + *
1.743 + * This procedure is called by Tcl_DoOneEvent to check the socket
1.744 + * event source for events.
1.745 + *
1.746 + * Results:
1.747 + * None.
1.748 + *
1.749 + * Side effects:
1.750 + * May queue an event.
1.751 + *
1.752 + *----------------------------------------------------------------------
1.753 + */
1.754 +
1.755 +static void
1.756 +SocketCheckProc(data, flags)
1.757 + ClientData data; /* Not used. */
1.758 + int flags; /* Event flags as passed to Tcl_DoOneEvent. */
1.759 +{
1.760 + SocketInfo *infoPtr;
1.761 + SocketEvent *evPtr;
1.762 + ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
1.763 +
1.764 + if (!(flags & TCL_FILE_EVENTS)) {
1.765 + return;
1.766 + }
1.767 +
1.768 + /*
1.769 + * Queue events for any ready sockets that don't already have events
1.770 + * queued (caused by persistent states that won't generate WinSock
1.771 + * events).
1.772 + */
1.773 +
1.774 + WaitForSingleObject(tsdPtr->socketListLock, INFINITE);
1.775 + for (infoPtr = tsdPtr->socketList; infoPtr != NULL;
1.776 + infoPtr = infoPtr->nextPtr) {
1.777 + if ((infoPtr->readyEvents & infoPtr->watchEvents)
1.778 + && !(infoPtr->flags & SOCKET_PENDING)) {
1.779 + infoPtr->flags |= SOCKET_PENDING;
1.780 + evPtr = (SocketEvent *) ckalloc(sizeof(SocketEvent));
1.781 + evPtr->header.proc = SocketEventProc;
1.782 + evPtr->socket = infoPtr->socket;
1.783 + Tcl_QueueEvent((Tcl_Event *) evPtr, TCL_QUEUE_TAIL);
1.784 + }
1.785 + }
1.786 + SetEvent(tsdPtr->socketListLock);
1.787 +}
1.788 +
1.789 +/*
1.790 + *----------------------------------------------------------------------
1.791 + *
1.792 + * SocketEventProc --
1.793 + *
1.794 + * This procedure is called by Tcl_ServiceEvent when a socket event
1.795 + * reaches the front of the event queue. This procedure is
1.796 + * responsible for notifying the generic channel code.
1.797 + *
1.798 + * Results:
1.799 + * Returns 1 if the event was handled, meaning it should be removed
1.800 + * from the queue. Returns 0 if the event was not handled, meaning
1.801 + * it should stay on the queue. The only time the event isn't
1.802 + * handled is if the TCL_FILE_EVENTS flag bit isn't set.
1.803 + *
1.804 + * Side effects:
1.805 + * Whatever the channel callback procedures do.
1.806 + *
1.807 + *----------------------------------------------------------------------
1.808 + */
1.809 +
1.810 +static int
1.811 +SocketEventProc(evPtr, flags)
1.812 + Tcl_Event *evPtr; /* Event to service. */
1.813 + int flags; /* Flags that indicate what events to
1.814 + * handle, such as TCL_FILE_EVENTS. */
1.815 +{
1.816 + SocketInfo *infoPtr;
1.817 + SocketEvent *eventPtr = (SocketEvent *) evPtr;
1.818 + int mask = 0;
1.819 + int events;
1.820 + ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
1.821 +
1.822 + if (!(flags & TCL_FILE_EVENTS)) {
1.823 + return 0;
1.824 + }
1.825 +
1.826 + /*
1.827 + * Find the specified socket on the socket list.
1.828 + */
1.829 +
1.830 + WaitForSingleObject(tsdPtr->socketListLock, INFINITE);
1.831 + for (infoPtr = tsdPtr->socketList; infoPtr != NULL;
1.832 + infoPtr = infoPtr->nextPtr) {
1.833 + if (infoPtr->socket == eventPtr->socket) {
1.834 + break;
1.835 + }
1.836 + }
1.837 + SetEvent(tsdPtr->socketListLock);
1.838 +
1.839 + /*
1.840 + * Discard events that have gone stale.
1.841 + */
1.842 +
1.843 + if (!infoPtr) {
1.844 + return 1;
1.845 + }
1.846 +
1.847 + infoPtr->flags &= ~SOCKET_PENDING;
1.848 +
1.849 + /*
1.850 + * Handle connection requests directly.
1.851 + */
1.852 +
1.853 + if (infoPtr->readyEvents & FD_ACCEPT) {
1.854 + TcpAccept(infoPtr);
1.855 + return 1;
1.856 + }
1.857 +
1.858 + /*
1.859 + * Mask off unwanted events and compute the read/write mask so
1.860 + * we can notify the channel.
1.861 + */
1.862 +
1.863 + events = infoPtr->readyEvents & infoPtr->watchEvents;
1.864 +
1.865 + if (events & FD_CLOSE) {
1.866 + /*
1.867 + * If the socket was closed and the channel is still interested
1.868 + * in read events, then we need to ensure that we keep polling
1.869 + * for this event until someone does something with the channel.
1.870 + * Note that we do this before calling Tcl_NotifyChannel so we don't
1.871 + * have to watch out for the channel being deleted out from under
1.872 + * us. This may cause a redundant trip through the event loop, but
1.873 + * it's simpler than trying to do unwind protection.
1.874 + */
1.875 +
1.876 + Tcl_Time blockTime = { 0, 0 };
1.877 + Tcl_SetMaxBlockTime(&blockTime);
1.878 + mask |= TCL_READABLE|TCL_WRITABLE;
1.879 + } else if (events & FD_READ) {
1.880 + fd_set readFds;
1.881 + struct timeval timeout;
1.882 +
1.883 + /*
1.884 + * We must check to see if data is really available, since someone
1.885 + * could have consumed the data in the meantime. Turn off async
1.886 + * notification so select will work correctly. If the socket is
1.887 + * still readable, notify the channel driver, otherwise reset the
1.888 + * async select handler and keep waiting.
1.889 + */
1.890 +
1.891 + SendMessage(tsdPtr->hwnd, SOCKET_SELECT,
1.892 + (WPARAM) UNSELECT, (LPARAM) infoPtr);
1.893 +
1.894 + FD_ZERO(&readFds);
1.895 + FD_SET(infoPtr->socket, &readFds);
1.896 + timeout.tv_usec = 0;
1.897 + timeout.tv_sec = 0;
1.898 +
1.899 + if (winSock.select(0, &readFds, NULL, NULL, &timeout) != 0) {
1.900 + mask |= TCL_READABLE;
1.901 + } else {
1.902 + infoPtr->readyEvents &= ~(FD_READ);
1.903 + SendMessage(tsdPtr->hwnd, SOCKET_SELECT,
1.904 + (WPARAM) SELECT, (LPARAM) infoPtr);
1.905 + }
1.906 + }
1.907 + if (events & (FD_WRITE | FD_CONNECT)) {
1.908 + mask |= TCL_WRITABLE;
1.909 + if (events & FD_CONNECT && infoPtr->lastError != NO_ERROR) {
1.910 + /* connect errors should also fire the readable handler. */
1.911 + mask |= TCL_READABLE;
1.912 + }
1.913 + }
1.914 +
1.915 + if (mask) {
1.916 + Tcl_NotifyChannel(infoPtr->channel, mask);
1.917 + }
1.918 + return 1;
1.919 +}
1.920 +
1.921 +/*
1.922 + *----------------------------------------------------------------------
1.923 + *
1.924 + * TcpBlockProc --
1.925 + *
1.926 + * Sets a socket into blocking or non-blocking mode.
1.927 + *
1.928 + * Results:
1.929 + * 0 if successful, errno if there was an error.
1.930 + *
1.931 + * Side effects:
1.932 + * None.
1.933 + *
1.934 + *----------------------------------------------------------------------
1.935 + */
1.936 +
1.937 +static int
1.938 +TcpBlockProc(instanceData, mode)
1.939 + ClientData instanceData; /* The socket to block/un-block. */
1.940 + int mode; /* TCL_MODE_BLOCKING or
1.941 + * TCL_MODE_NONBLOCKING. */
1.942 +{
1.943 + SocketInfo *infoPtr = (SocketInfo *) instanceData;
1.944 +
1.945 + if (mode == TCL_MODE_NONBLOCKING) {
1.946 + infoPtr->flags |= SOCKET_ASYNC;
1.947 + } else {
1.948 + infoPtr->flags &= ~(SOCKET_ASYNC);
1.949 + }
1.950 + return 0;
1.951 +}
1.952 +
1.953 +/*
1.954 + *----------------------------------------------------------------------
1.955 + *
1.956 + * TcpCloseProc --
1.957 + *
1.958 + * This procedure is called by the generic IO level to perform
1.959 + * channel type specific cleanup on a socket based channel
1.960 + * when the channel is closed.
1.961 + *
1.962 + * Results:
1.963 + * 0 if successful, the value of errno if failed.
1.964 + *
1.965 + * Side effects:
1.966 + * Closes the socket.
1.967 + *
1.968 + *----------------------------------------------------------------------
1.969 + */
1.970 +
1.971 + /* ARGSUSED */
1.972 +static int
1.973 +TcpCloseProc(instanceData, interp)
1.974 + ClientData instanceData; /* The socket to close. */
1.975 + Tcl_Interp *interp; /* Unused. */
1.976 +{
1.977 + SocketInfo *infoPtr = (SocketInfo *) instanceData;
1.978 + /* TIP #218 */
1.979 + int errorCode = 0;
1.980 + ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
1.981 +
1.982 + /*
1.983 + * Check that WinSock is initialized; do not call it if not, to
1.984 + * prevent system crashes. This can happen at exit time if the exit
1.985 + * handler for WinSock ran before other exit handlers that want to
1.986 + * use sockets.
1.987 + */
1.988 +
1.989 + if (SocketsEnabled()) {
1.990 +
1.991 + /*
1.992 + * Clean up the OS socket handle. The default Windows setting
1.993 + * for a socket is SO_DONTLINGER, which does a graceful shutdown
1.994 + * in the background.
1.995 + */
1.996 +
1.997 + if (winSock.closesocket(infoPtr->socket) == SOCKET_ERROR) {
1.998 + TclWinConvertWSAError((DWORD) winSock.WSAGetLastError());
1.999 + errorCode = Tcl_GetErrno();
1.1000 + }
1.1001 + }
1.1002 +
1.1003 + /* TIP #218. Removed the code removing the structure
1.1004 + * from the global socket list. This is now done by
1.1005 + * the thread action callbacks, and only there. This
1.1006 + * happens before this code is called. We can free
1.1007 + * without fear of damanging the list.
1.1008 + */
1.1009 + ckfree((char *) infoPtr);
1.1010 + return errorCode;
1.1011 +}
1.1012 +
1.1013 +/*
1.1014 + *----------------------------------------------------------------------
1.1015 + *
1.1016 + * NewSocketInfo --
1.1017 + *
1.1018 + * This function allocates and initializes a new SocketInfo
1.1019 + * structure.
1.1020 + *
1.1021 + * Results:
1.1022 + * Returns a newly allocated SocketInfo.
1.1023 + *
1.1024 + * Side effects:
1.1025 + * None, except for allocation of memory.
1.1026 + *
1.1027 + *----------------------------------------------------------------------
1.1028 + */
1.1029 +
1.1030 +static SocketInfo *
1.1031 +NewSocketInfo(socket)
1.1032 + SOCKET socket;
1.1033 +{
1.1034 + SocketInfo *infoPtr;
1.1035 +
1.1036 + infoPtr = (SocketInfo *) ckalloc((unsigned) sizeof(SocketInfo));
1.1037 + infoPtr->socket = socket;
1.1038 + infoPtr->flags = 0;
1.1039 + infoPtr->watchEvents = 0;
1.1040 + infoPtr->readyEvents = 0;
1.1041 + infoPtr->selectEvents = 0;
1.1042 + infoPtr->acceptEventCount = 0;
1.1043 + infoPtr->acceptProc = NULL;
1.1044 + infoPtr->lastError = 0;
1.1045 +
1.1046 + /* TIP #218. Removed the code inserting the new structure
1.1047 + * into the global list. This is now handled in the thread
1.1048 + * action callbacks, and only there.
1.1049 + */
1.1050 + infoPtr->nextPtr = NULL;
1.1051 +
1.1052 + return infoPtr;
1.1053 +}
1.1054 +
1.1055 +/*
1.1056 + *----------------------------------------------------------------------
1.1057 + *
1.1058 + * CreateSocket --
1.1059 + *
1.1060 + * This function opens a new socket and initializes the
1.1061 + * SocketInfo structure.
1.1062 + *
1.1063 + * Results:
1.1064 + * Returns a new SocketInfo, or NULL with an error in interp.
1.1065 + *
1.1066 + * Side effects:
1.1067 + * None, except for allocation of memory.
1.1068 + *
1.1069 + *----------------------------------------------------------------------
1.1070 + */
1.1071 +
1.1072 +static SocketInfo *
1.1073 +CreateSocket(interp, port, host, server, myaddr, myport, async)
1.1074 + Tcl_Interp *interp; /* For error reporting; can be NULL. */
1.1075 + int port; /* Port number to open. */
1.1076 + CONST char *host; /* Name of host on which to open port. */
1.1077 + int server; /* 1 if socket should be a server socket,
1.1078 + * else 0 for a client socket. */
1.1079 + CONST char *myaddr; /* Optional client-side address */
1.1080 + int myport; /* Optional client-side port */
1.1081 + int async; /* If nonzero, connect client socket
1.1082 + * asynchronously. */
1.1083 +{
1.1084 + u_long flag = 1; /* Indicates nonblocking mode. */
1.1085 + int asyncConnect = 0; /* Will be 1 if async connect is
1.1086 + * in progress. */
1.1087 + SOCKADDR_IN sockaddr; /* Socket address */
1.1088 + SOCKADDR_IN mysockaddr; /* Socket address for client */
1.1089 + SOCKET sock = INVALID_SOCKET;
1.1090 + SocketInfo *infoPtr; /* The returned value. */
1.1091 + ThreadSpecificData *tsdPtr =
1.1092 + (ThreadSpecificData *)TclThreadDataKeyGet(&dataKey);
1.1093 +
1.1094 + /*
1.1095 + * Check that WinSock is initialized; do not call it if not, to
1.1096 + * prevent system crashes. This can happen at exit time if the exit
1.1097 + * handler for WinSock ran before other exit handlers that want to
1.1098 + * use sockets.
1.1099 + */
1.1100 +
1.1101 + if (!SocketsEnabled()) {
1.1102 + return NULL;
1.1103 + }
1.1104 +
1.1105 + if (! CreateSocketAddress(&sockaddr, host, port)) {
1.1106 + goto error;
1.1107 + }
1.1108 + if ((myaddr != NULL || myport != 0) &&
1.1109 + ! CreateSocketAddress(&mysockaddr, myaddr, myport)) {
1.1110 + goto error;
1.1111 + }
1.1112 +
1.1113 + sock = winSock.socket(AF_INET, SOCK_STREAM, 0);
1.1114 + if (sock == INVALID_SOCKET) {
1.1115 + goto error;
1.1116 + }
1.1117 +
1.1118 + /*
1.1119 + * Win-NT has a misfeature that sockets are inherited in child
1.1120 + * processes by default. Turn off the inherit bit.
1.1121 + */
1.1122 +
1.1123 + SetHandleInformation( (HANDLE) sock, HANDLE_FLAG_INHERIT, 0 );
1.1124 +
1.1125 + /*
1.1126 + * Set kernel space buffering
1.1127 + */
1.1128 +
1.1129 + TclSockMinimumBuffers((int) sock, TCP_BUFFER_SIZE);
1.1130 +
1.1131 + if (server) {
1.1132 + /*
1.1133 + * Bind to the specified port. Note that we must not call setsockopt
1.1134 + * with SO_REUSEADDR because Microsoft allows addresses to be reused
1.1135 + * even if they are still in use.
1.1136 + *
1.1137 + * Bind should not be affected by the socket having already been
1.1138 + * set into nonblocking mode. If there is trouble, this is one place
1.1139 + * to look for bugs.
1.1140 + */
1.1141 +
1.1142 + if (winSock.bind(sock, (SOCKADDR *) &sockaddr,
1.1143 + sizeof(SOCKADDR_IN)) == SOCKET_ERROR) {
1.1144 + goto error;
1.1145 + }
1.1146 +
1.1147 + /*
1.1148 + * Set the maximum number of pending connect requests to the
1.1149 + * max value allowed on each platform (Win32 and Win32s may be
1.1150 + * different, and there may be differences between TCP/IP stacks).
1.1151 + */
1.1152 +
1.1153 + if (winSock.listen(sock, SOMAXCONN) == SOCKET_ERROR) {
1.1154 + goto error;
1.1155 + }
1.1156 +
1.1157 + /*
1.1158 + * Add this socket to the global list of sockets.
1.1159 + */
1.1160 +
1.1161 + infoPtr = NewSocketInfo(sock);
1.1162 +
1.1163 + /*
1.1164 + * Set up the select mask for connection request events.
1.1165 + */
1.1166 +
1.1167 + infoPtr->selectEvents = FD_ACCEPT;
1.1168 + infoPtr->watchEvents |= FD_ACCEPT;
1.1169 +
1.1170 + } else {
1.1171 +
1.1172 + /*
1.1173 + * Try to bind to a local port, if specified.
1.1174 + */
1.1175 +
1.1176 + if (myaddr != NULL || myport != 0) {
1.1177 + if (winSock.bind(sock, (SOCKADDR *) &mysockaddr,
1.1178 + sizeof(SOCKADDR_IN)) == SOCKET_ERROR) {
1.1179 + goto error;
1.1180 + }
1.1181 + }
1.1182 +
1.1183 + /*
1.1184 + * Set the socket into nonblocking mode if the connect should be
1.1185 + * done in the background.
1.1186 + */
1.1187 +
1.1188 + if (async) {
1.1189 + if (winSock.ioctlsocket(sock, (long) FIONBIO, &flag) == SOCKET_ERROR) {
1.1190 + goto error;
1.1191 + }
1.1192 + }
1.1193 +
1.1194 + /*
1.1195 + * Attempt to connect to the remote socket.
1.1196 + */
1.1197 +
1.1198 + if (winSock.connect(sock, (SOCKADDR *) &sockaddr,
1.1199 + sizeof(SOCKADDR_IN)) == SOCKET_ERROR) {
1.1200 + TclWinConvertWSAError((DWORD) winSock.WSAGetLastError());
1.1201 + if (Tcl_GetErrno() != EWOULDBLOCK) {
1.1202 + goto error;
1.1203 + }
1.1204 +
1.1205 + /*
1.1206 + * The connection is progressing in the background.
1.1207 + */
1.1208 +
1.1209 + asyncConnect = 1;
1.1210 + }
1.1211 +
1.1212 + /*
1.1213 + * Add this socket to the global list of sockets.
1.1214 + */
1.1215 +
1.1216 + infoPtr = NewSocketInfo(sock);
1.1217 +
1.1218 + /*
1.1219 + * Set up the select mask for read/write events. If the connect
1.1220 + * attempt has not completed, include connect events.
1.1221 + */
1.1222 +
1.1223 + infoPtr->selectEvents = FD_READ | FD_WRITE | FD_CLOSE;
1.1224 + if (asyncConnect) {
1.1225 + infoPtr->flags |= SOCKET_ASYNC_CONNECT;
1.1226 + infoPtr->selectEvents |= FD_CONNECT;
1.1227 + }
1.1228 + }
1.1229 +
1.1230 + /*
1.1231 + * Register for interest in events in the select mask. Note that this
1.1232 + * automatically places the socket into non-blocking mode.
1.1233 + */
1.1234 +
1.1235 + winSock.ioctlsocket(sock, (long) FIONBIO, &flag);
1.1236 + SendMessage(tsdPtr->hwnd, SOCKET_SELECT,
1.1237 + (WPARAM) SELECT, (LPARAM) infoPtr);
1.1238 +
1.1239 + return infoPtr;
1.1240 +
1.1241 +error:
1.1242 + TclWinConvertWSAError((DWORD) winSock.WSAGetLastError());
1.1243 + if (interp != NULL) {
1.1244 + Tcl_AppendResult(interp, "couldn't open socket: ",
1.1245 + Tcl_PosixError(interp), (char *) NULL);
1.1246 + }
1.1247 + if (sock != INVALID_SOCKET) {
1.1248 + winSock.closesocket(sock);
1.1249 + }
1.1250 + return NULL;
1.1251 +}
1.1252 +
1.1253 +/*
1.1254 + *----------------------------------------------------------------------
1.1255 + *
1.1256 + * CreateSocketAddress --
1.1257 + *
1.1258 + * This function initializes a sockaddr structure for a host and port.
1.1259 + *
1.1260 + * Results:
1.1261 + * 1 if the host was valid, 0 if the host could not be converted to
1.1262 + * an IP address.
1.1263 + *
1.1264 + * Side effects:
1.1265 + * Fills in the *sockaddrPtr structure.
1.1266 + *
1.1267 + *----------------------------------------------------------------------
1.1268 + */
1.1269 +
1.1270 +static int
1.1271 +CreateSocketAddress(sockaddrPtr, host, port)
1.1272 + LPSOCKADDR_IN sockaddrPtr; /* Socket address */
1.1273 + CONST char *host; /* Host. NULL implies INADDR_ANY */
1.1274 + int port; /* Port number */
1.1275 +{
1.1276 + struct hostent *hostent; /* Host database entry */
1.1277 + struct in_addr addr; /* For 64/32 bit madness */
1.1278 +
1.1279 + /*
1.1280 + * Check that WinSock is initialized; do not call it if not, to
1.1281 + * prevent system crashes. This can happen at exit time if the exit
1.1282 + * handler for WinSock ran before other exit handlers that want to
1.1283 + * use sockets.
1.1284 + */
1.1285 +
1.1286 + if (!SocketsEnabled()) {
1.1287 + Tcl_SetErrno(EFAULT);
1.1288 + return 0;
1.1289 + }
1.1290 +
1.1291 + ZeroMemory(sockaddrPtr, sizeof(SOCKADDR_IN));
1.1292 + sockaddrPtr->sin_family = AF_INET;
1.1293 + sockaddrPtr->sin_port = winSock.htons((u_short) (port & 0xFFFF));
1.1294 + if (host == NULL) {
1.1295 + addr.s_addr = INADDR_ANY;
1.1296 + } else {
1.1297 + addr.s_addr = winSock.inet_addr(host);
1.1298 + if (addr.s_addr == INADDR_NONE) {
1.1299 + hostent = winSock.gethostbyname(host);
1.1300 + if (hostent != NULL) {
1.1301 + memcpy(&addr, hostent->h_addr, (size_t) hostent->h_length);
1.1302 + } else {
1.1303 +#ifdef EHOSTUNREACH
1.1304 + Tcl_SetErrno(EHOSTUNREACH);
1.1305 +#else
1.1306 +#ifdef ENXIO
1.1307 + Tcl_SetErrno(ENXIO);
1.1308 +#endif
1.1309 +#endif
1.1310 + return 0; /* Error. */
1.1311 + }
1.1312 + }
1.1313 + }
1.1314 +
1.1315 + /*
1.1316 + * NOTE: On 64 bit machines the assignment below is rumored to not
1.1317 + * do the right thing. Please report errors related to this if you
1.1318 + * observe incorrect behavior on 64 bit machines such as DEC Alphas.
1.1319 + * Should we modify this code to do an explicit memcpy?
1.1320 + */
1.1321 +
1.1322 + sockaddrPtr->sin_addr.s_addr = addr.s_addr;
1.1323 + return 1; /* Success. */
1.1324 +}
1.1325 +
1.1326 +/*
1.1327 + *----------------------------------------------------------------------
1.1328 + *
1.1329 + * WaitForSocketEvent --
1.1330 + *
1.1331 + * Waits until one of the specified events occurs on a socket.
1.1332 + *
1.1333 + * Results:
1.1334 + * Returns 1 on success or 0 on failure, with an error code in
1.1335 + * errorCodePtr.
1.1336 + *
1.1337 + * Side effects:
1.1338 + * Processes socket events off the system queue.
1.1339 + *
1.1340 + *----------------------------------------------------------------------
1.1341 + */
1.1342 +
1.1343 +static int
1.1344 +WaitForSocketEvent(infoPtr, events, errorCodePtr)
1.1345 + SocketInfo *infoPtr; /* Information about this socket. */
1.1346 + int events; /* Events to look for. */
1.1347 + int *errorCodePtr; /* Where to store errors? */
1.1348 +{
1.1349 + int result = 1;
1.1350 + int oldMode;
1.1351 + ThreadSpecificData *tsdPtr =
1.1352 + (ThreadSpecificData *)TclThreadDataKeyGet(&dataKey);
1.1353 +
1.1354 + /*
1.1355 + * Be sure to disable event servicing so we are truly modal.
1.1356 + */
1.1357 +
1.1358 + oldMode = Tcl_SetServiceMode(TCL_SERVICE_NONE);
1.1359 +
1.1360 + /*
1.1361 + * Reset WSAAsyncSelect so we have a fresh set of events pending.
1.1362 + */
1.1363 +
1.1364 + SendMessage(tsdPtr->hwnd, SOCKET_SELECT,
1.1365 + (WPARAM) UNSELECT, (LPARAM) infoPtr);
1.1366 +
1.1367 + SendMessage(tsdPtr->hwnd, SOCKET_SELECT,
1.1368 + (WPARAM) SELECT, (LPARAM) infoPtr);
1.1369 +
1.1370 + while (1) {
1.1371 +
1.1372 + if (infoPtr->lastError) {
1.1373 + *errorCodePtr = infoPtr->lastError;
1.1374 + result = 0;
1.1375 + break;
1.1376 + } else if (infoPtr->readyEvents & events) {
1.1377 + break;
1.1378 + } else if (infoPtr->flags & SOCKET_ASYNC) {
1.1379 + *errorCodePtr = EWOULDBLOCK;
1.1380 + result = 0;
1.1381 + break;
1.1382 + }
1.1383 +
1.1384 + /*
1.1385 + * Wait until something happens.
1.1386 + */
1.1387 + WaitForSingleObject(tsdPtr->readyEvent, INFINITE);
1.1388 + }
1.1389 +
1.1390 + (void) Tcl_SetServiceMode(oldMode);
1.1391 + return result;
1.1392 +}
1.1393 +
1.1394 +/*
1.1395 + *----------------------------------------------------------------------
1.1396 + *
1.1397 + * Tcl_OpenTcpClient --
1.1398 + *
1.1399 + * Opens a TCP client socket and creates a channel around it.
1.1400 + *
1.1401 + * Results:
1.1402 + * The channel or NULL if failed. An error message is returned
1.1403 + * in the interpreter on failure.
1.1404 + *
1.1405 + * Side effects:
1.1406 + * Opens a client socket and creates a new channel.
1.1407 + *
1.1408 + *----------------------------------------------------------------------
1.1409 + */
1.1410 +
1.1411 +Tcl_Channel
1.1412 +Tcl_OpenTcpClient(interp, port, host, myaddr, myport, async)
1.1413 + Tcl_Interp *interp; /* For error reporting; can be NULL. */
1.1414 + int port; /* Port number to open. */
1.1415 + CONST char *host; /* Host on which to open port. */
1.1416 + CONST char *myaddr; /* Client-side address */
1.1417 + int myport; /* Client-side port */
1.1418 + int async; /* If nonzero, should connect
1.1419 + * client socket asynchronously. */
1.1420 +{
1.1421 + SocketInfo *infoPtr;
1.1422 + char channelName[16 + TCL_INTEGER_SPACE];
1.1423 +
1.1424 + if (TclpHasSockets(interp) != TCL_OK) {
1.1425 + return NULL;
1.1426 + }
1.1427 +
1.1428 + /*
1.1429 + * Create a new client socket and wrap it in a channel.
1.1430 + */
1.1431 +
1.1432 + infoPtr = CreateSocket(interp, port, host, 0, myaddr, myport, async);
1.1433 + if (infoPtr == NULL) {
1.1434 + return NULL;
1.1435 + }
1.1436 +
1.1437 + wsprintfA(channelName, "sock%d", infoPtr->socket);
1.1438 +
1.1439 + infoPtr->channel = Tcl_CreateChannel(&tcpChannelType, channelName,
1.1440 + (ClientData) infoPtr, (TCL_READABLE | TCL_WRITABLE));
1.1441 + if (Tcl_SetChannelOption(interp, infoPtr->channel, "-translation",
1.1442 + "auto crlf") == TCL_ERROR) {
1.1443 + Tcl_Close((Tcl_Interp *) NULL, infoPtr->channel);
1.1444 + return (Tcl_Channel) NULL;
1.1445 + }
1.1446 + if (Tcl_SetChannelOption(NULL, infoPtr->channel, "-eofchar", "")
1.1447 + == TCL_ERROR) {
1.1448 + Tcl_Close((Tcl_Interp *) NULL, infoPtr->channel);
1.1449 + return (Tcl_Channel) NULL;
1.1450 + }
1.1451 + return infoPtr->channel;
1.1452 +}
1.1453 +
1.1454 +/*
1.1455 + *----------------------------------------------------------------------
1.1456 + *
1.1457 + * Tcl_MakeTcpClientChannel --
1.1458 + *
1.1459 + * Creates a Tcl_Channel from an existing client TCP socket.
1.1460 + *
1.1461 + * Results:
1.1462 + * The Tcl_Channel wrapped around the preexisting TCP socket.
1.1463 + *
1.1464 + * Side effects:
1.1465 + * None.
1.1466 + *
1.1467 + * NOTE: Code contributed by Mark Diekhans (markd@grizzly.com)
1.1468 + *
1.1469 + *----------------------------------------------------------------------
1.1470 + */
1.1471 +
1.1472 +Tcl_Channel
1.1473 +Tcl_MakeTcpClientChannel(sock)
1.1474 + ClientData sock; /* The socket to wrap up into a channel. */
1.1475 +{
1.1476 + SocketInfo *infoPtr;
1.1477 + char channelName[16 + TCL_INTEGER_SPACE];
1.1478 + ThreadSpecificData *tsdPtr;
1.1479 +
1.1480 + if (TclpHasSockets(NULL) != TCL_OK) {
1.1481 + return NULL;
1.1482 + }
1.1483 +
1.1484 + tsdPtr = (ThreadSpecificData *)TclThreadDataKeyGet(&dataKey);
1.1485 +
1.1486 + /*
1.1487 + * Set kernel space buffering and non-blocking.
1.1488 + */
1.1489 +
1.1490 + TclSockMinimumBuffers((int) sock, TCP_BUFFER_SIZE);
1.1491 +
1.1492 + infoPtr = NewSocketInfo((SOCKET) sock);
1.1493 +
1.1494 + /*
1.1495 + * Start watching for read/write events on the socket.
1.1496 + */
1.1497 +
1.1498 + infoPtr->selectEvents = FD_READ | FD_CLOSE | FD_WRITE;
1.1499 + SendMessage(tsdPtr->hwnd, SOCKET_SELECT,
1.1500 + (WPARAM) SELECT, (LPARAM) infoPtr);
1.1501 +
1.1502 + wsprintfA(channelName, "sock%d", infoPtr->socket);
1.1503 + infoPtr->channel = Tcl_CreateChannel(&tcpChannelType, channelName,
1.1504 + (ClientData) infoPtr, (TCL_READABLE | TCL_WRITABLE));
1.1505 + Tcl_SetChannelOption(NULL, infoPtr->channel, "-translation", "auto crlf");
1.1506 + return infoPtr->channel;
1.1507 +}
1.1508 +
1.1509 +/*
1.1510 + *----------------------------------------------------------------------
1.1511 + *
1.1512 + * Tcl_OpenTcpServer --
1.1513 + *
1.1514 + * Opens a TCP server socket and creates a channel around it.
1.1515 + *
1.1516 + * Results:
1.1517 + * The channel or NULL if failed. An error message is returned
1.1518 + * in the interpreter on failure.
1.1519 + *
1.1520 + * Side effects:
1.1521 + * Opens a server socket and creates a new channel.
1.1522 + *
1.1523 + *----------------------------------------------------------------------
1.1524 + */
1.1525 +
1.1526 +Tcl_Channel
1.1527 +Tcl_OpenTcpServer(interp, port, host, acceptProc, acceptProcData)
1.1528 + Tcl_Interp *interp; /* For error reporting - may be
1.1529 + * NULL. */
1.1530 + int port; /* Port number to open. */
1.1531 + CONST char *host; /* Name of local host. */
1.1532 + Tcl_TcpAcceptProc *acceptProc; /* Callback for accepting connections
1.1533 + * from new clients. */
1.1534 + ClientData acceptProcData; /* Data for the callback. */
1.1535 +{
1.1536 + SocketInfo *infoPtr;
1.1537 + char channelName[16 + TCL_INTEGER_SPACE];
1.1538 +
1.1539 + if (TclpHasSockets(interp) != TCL_OK) {
1.1540 + return NULL;
1.1541 + }
1.1542 +
1.1543 + /*
1.1544 + * Create a new client socket and wrap it in a channel.
1.1545 + */
1.1546 +
1.1547 + infoPtr = CreateSocket(interp, port, host, 1, NULL, 0, 0);
1.1548 + if (infoPtr == NULL) {
1.1549 + return NULL;
1.1550 + }
1.1551 +
1.1552 + infoPtr->acceptProc = acceptProc;
1.1553 + infoPtr->acceptProcData = acceptProcData;
1.1554 +
1.1555 + wsprintfA(channelName, "sock%d", infoPtr->socket);
1.1556 +
1.1557 + infoPtr->channel = Tcl_CreateChannel(&tcpChannelType, channelName,
1.1558 + (ClientData) infoPtr, 0);
1.1559 + if (Tcl_SetChannelOption(interp, infoPtr->channel, "-eofchar", "")
1.1560 + == TCL_ERROR) {
1.1561 + Tcl_Close((Tcl_Interp *) NULL, infoPtr->channel);
1.1562 + return (Tcl_Channel) NULL;
1.1563 + }
1.1564 +
1.1565 + return infoPtr->channel;
1.1566 +}
1.1567 +
1.1568 +/*
1.1569 + *----------------------------------------------------------------------
1.1570 + *
1.1571 + * TcpAccept --
1.1572 + * Accept a TCP socket connection. This is called by
1.1573 + * SocketEventProc and it in turns calls the registered accept
1.1574 + * procedure.
1.1575 + *
1.1576 + * Results:
1.1577 + * None.
1.1578 + *
1.1579 + * Side effects:
1.1580 + * Invokes the accept proc which may invoke arbitrary Tcl code.
1.1581 + *
1.1582 + *----------------------------------------------------------------------
1.1583 + */
1.1584 +
1.1585 +static void
1.1586 +TcpAccept(infoPtr)
1.1587 + SocketInfo *infoPtr; /* Socket to accept. */
1.1588 +{
1.1589 + SOCKET newSocket;
1.1590 + SocketInfo *newInfoPtr;
1.1591 + SOCKADDR_IN addr;
1.1592 + int len;
1.1593 + char channelName[16 + TCL_INTEGER_SPACE];
1.1594 + ThreadSpecificData *tsdPtr =
1.1595 + (ThreadSpecificData *)TclThreadDataKeyGet(&dataKey);
1.1596 +
1.1597 + /*
1.1598 + * Accept the incoming connection request.
1.1599 + */
1.1600 +
1.1601 + len = sizeof(SOCKADDR_IN);
1.1602 +
1.1603 + newSocket = winSock.accept(infoPtr->socket, (SOCKADDR *)&addr,
1.1604 + &len);
1.1605 +
1.1606 + /*
1.1607 + * Clear the ready mask so we can detect the next connection request.
1.1608 + * Note that connection requests are level triggered, so if there is
1.1609 + * a request already pending, a new event will be generated.
1.1610 + */
1.1611 +
1.1612 + if (newSocket == INVALID_SOCKET) {
1.1613 + infoPtr->acceptEventCount = 0;
1.1614 + infoPtr->readyEvents &= ~(FD_ACCEPT);
1.1615 + return;
1.1616 + }
1.1617 +
1.1618 + /*
1.1619 + * It is possible that more than one FD_ACCEPT has been sent, so an extra
1.1620 + * count must be kept. Decrement the count, and reset the readyEvent bit
1.1621 + * if the count is no longer > 0.
1.1622 + */
1.1623 +
1.1624 + infoPtr->acceptEventCount--;
1.1625 +
1.1626 + if (infoPtr->acceptEventCount <= 0) {
1.1627 + infoPtr->readyEvents &= ~(FD_ACCEPT);
1.1628 + }
1.1629 +
1.1630 + /*
1.1631 + * Win-NT has a misfeature that sockets are inherited in child
1.1632 + * processes by default. Turn off the inherit bit.
1.1633 + */
1.1634 +
1.1635 + SetHandleInformation( (HANDLE) newSocket, HANDLE_FLAG_INHERIT, 0 );
1.1636 +
1.1637 + /*
1.1638 + * Add this socket to the global list of sockets.
1.1639 + */
1.1640 +
1.1641 + newInfoPtr = NewSocketInfo(newSocket);
1.1642 +
1.1643 + /*
1.1644 + * Select on read/write events and create the channel.
1.1645 + */
1.1646 +
1.1647 + newInfoPtr->selectEvents = (FD_READ | FD_WRITE | FD_CLOSE);
1.1648 + SendMessage(tsdPtr->hwnd, SOCKET_SELECT,
1.1649 + (WPARAM) SELECT, (LPARAM) newInfoPtr);
1.1650 +
1.1651 + wsprintfA(channelName, "sock%d", newInfoPtr->socket);
1.1652 + newInfoPtr->channel = Tcl_CreateChannel(&tcpChannelType, channelName,
1.1653 + (ClientData) newInfoPtr, (TCL_READABLE | TCL_WRITABLE));
1.1654 + if (Tcl_SetChannelOption(NULL, newInfoPtr->channel, "-translation",
1.1655 + "auto crlf") == TCL_ERROR) {
1.1656 + Tcl_Close((Tcl_Interp *) NULL, newInfoPtr->channel);
1.1657 + return;
1.1658 + }
1.1659 + if (Tcl_SetChannelOption(NULL, newInfoPtr->channel, "-eofchar", "")
1.1660 + == TCL_ERROR) {
1.1661 + Tcl_Close((Tcl_Interp *) NULL, newInfoPtr->channel);
1.1662 + return;
1.1663 + }
1.1664 +
1.1665 + /*
1.1666 + * Invoke the accept callback procedure.
1.1667 + */
1.1668 +
1.1669 + if (infoPtr->acceptProc != NULL) {
1.1670 + (infoPtr->acceptProc) (infoPtr->acceptProcData,
1.1671 + newInfoPtr->channel,
1.1672 + winSock.inet_ntoa(addr.sin_addr),
1.1673 + winSock.ntohs(addr.sin_port));
1.1674 + }
1.1675 +}
1.1676 +
1.1677 +/*
1.1678 + *----------------------------------------------------------------------
1.1679 + *
1.1680 + * TcpInputProc --
1.1681 + *
1.1682 + * This procedure is called by the generic IO level to read data from
1.1683 + * a socket based channel.
1.1684 + *
1.1685 + * Results:
1.1686 + * The number of bytes read or -1 on error.
1.1687 + *
1.1688 + * Side effects:
1.1689 + * Consumes input from the socket.
1.1690 + *
1.1691 + *----------------------------------------------------------------------
1.1692 + */
1.1693 +
1.1694 +static int
1.1695 +TcpInputProc(instanceData, buf, toRead, errorCodePtr)
1.1696 + ClientData instanceData; /* The socket state. */
1.1697 + char *buf; /* Where to store data. */
1.1698 + int toRead; /* Maximum number of bytes to read. */
1.1699 + int *errorCodePtr; /* Where to store error codes. */
1.1700 +{
1.1701 + SocketInfo *infoPtr = (SocketInfo *) instanceData;
1.1702 + int bytesRead;
1.1703 + DWORD error;
1.1704 + ThreadSpecificData *tsdPtr =
1.1705 + (ThreadSpecificData *)TclThreadDataKeyGet(&dataKey);
1.1706 +
1.1707 + *errorCodePtr = 0;
1.1708 +
1.1709 + /*
1.1710 + * Check that WinSock is initialized; do not call it if not, to
1.1711 + * prevent system crashes. This can happen at exit time if the exit
1.1712 + * handler for WinSock ran before other exit handlers that want to
1.1713 + * use sockets.
1.1714 + */
1.1715 +
1.1716 + if (!SocketsEnabled()) {
1.1717 + *errorCodePtr = EFAULT;
1.1718 + return -1;
1.1719 + }
1.1720 +
1.1721 + /*
1.1722 + * First check to see if EOF was already detected, to prevent
1.1723 + * calling the socket stack after the first time EOF is detected.
1.1724 + */
1.1725 +
1.1726 + if (infoPtr->flags & SOCKET_EOF) {
1.1727 + return 0;
1.1728 + }
1.1729 +
1.1730 + /*
1.1731 + * Check to see if the socket is connected before trying to read.
1.1732 + */
1.1733 +
1.1734 + if ((infoPtr->flags & SOCKET_ASYNC_CONNECT)
1.1735 + && ! WaitForSocketEvent(infoPtr, FD_CONNECT, errorCodePtr)) {
1.1736 + return -1;
1.1737 + }
1.1738 +
1.1739 + /*
1.1740 + * No EOF, and it is connected, so try to read more from the socket.
1.1741 + * Note that we clear the FD_READ bit because read events are level
1.1742 + * triggered so a new event will be generated if there is still data
1.1743 + * available to be read. We have to simulate blocking behavior here
1.1744 + * since we are always using non-blocking sockets.
1.1745 + */
1.1746 +
1.1747 + while (1) {
1.1748 + SendMessage(tsdPtr->hwnd, SOCKET_SELECT,
1.1749 + (WPARAM) UNSELECT, (LPARAM) infoPtr);
1.1750 + bytesRead = winSock.recv(infoPtr->socket, buf, toRead, 0);
1.1751 + infoPtr->readyEvents &= ~(FD_READ);
1.1752 +
1.1753 + /*
1.1754 + * Check for end-of-file condition or successful read.
1.1755 + */
1.1756 +
1.1757 + if (bytesRead == 0) {
1.1758 + infoPtr->flags |= SOCKET_EOF;
1.1759 + }
1.1760 + if (bytesRead != SOCKET_ERROR) {
1.1761 + break;
1.1762 + }
1.1763 +
1.1764 + /*
1.1765 + * If an error occurs after the FD_CLOSE has arrived,
1.1766 + * then ignore the error and report an EOF.
1.1767 + */
1.1768 +
1.1769 + if (infoPtr->readyEvents & FD_CLOSE) {
1.1770 + infoPtr->flags |= SOCKET_EOF;
1.1771 + bytesRead = 0;
1.1772 + break;
1.1773 + }
1.1774 +
1.1775 + /*
1.1776 + * Check for error condition or underflow in non-blocking case.
1.1777 + */
1.1778 +
1.1779 + error = winSock.WSAGetLastError();
1.1780 + if ((infoPtr->flags & SOCKET_ASYNC) || (error != WSAEWOULDBLOCK)) {
1.1781 + TclWinConvertWSAError(error);
1.1782 + *errorCodePtr = Tcl_GetErrno();
1.1783 + bytesRead = -1;
1.1784 + break;
1.1785 + }
1.1786 +
1.1787 + /*
1.1788 + * In the blocking case, wait until the file becomes readable
1.1789 + * or closed and try again.
1.1790 + */
1.1791 +
1.1792 + if (!WaitForSocketEvent(infoPtr, FD_READ|FD_CLOSE, errorCodePtr)) {
1.1793 + bytesRead = -1;
1.1794 + break;
1.1795 + }
1.1796 + }
1.1797 +
1.1798 + SendMessage(tsdPtr->hwnd, SOCKET_SELECT,
1.1799 + (WPARAM) SELECT, (LPARAM) infoPtr);
1.1800 +
1.1801 + return bytesRead;
1.1802 +}
1.1803 +
1.1804 +/*
1.1805 + *----------------------------------------------------------------------
1.1806 + *
1.1807 + * TcpOutputProc --
1.1808 + *
1.1809 + * This procedure is called by the generic IO level to write data
1.1810 + * to a socket based channel.
1.1811 + *
1.1812 + * Results:
1.1813 + * The number of bytes written or -1 on failure.
1.1814 + *
1.1815 + * Side effects:
1.1816 + * Produces output on the socket.
1.1817 + *
1.1818 + *----------------------------------------------------------------------
1.1819 + */
1.1820 +
1.1821 +static int
1.1822 +TcpOutputProc(instanceData, buf, toWrite, errorCodePtr)
1.1823 + ClientData instanceData; /* The socket state. */
1.1824 + CONST char *buf; /* Where to get data. */
1.1825 + int toWrite; /* Maximum number of bytes to write. */
1.1826 + int *errorCodePtr; /* Where to store error codes. */
1.1827 +{
1.1828 + SocketInfo *infoPtr = (SocketInfo *) instanceData;
1.1829 + int bytesWritten;
1.1830 + DWORD error;
1.1831 + ThreadSpecificData *tsdPtr =
1.1832 + (ThreadSpecificData *)TclThreadDataKeyGet(&dataKey);
1.1833 +
1.1834 + *errorCodePtr = 0;
1.1835 +
1.1836 + /*
1.1837 + * Check that WinSock is initialized; do not call it if not, to
1.1838 + * prevent system crashes. This can happen at exit time if the exit
1.1839 + * handler for WinSock ran before other exit handlers that want to
1.1840 + * use sockets.
1.1841 + */
1.1842 +
1.1843 + if (!SocketsEnabled()) {
1.1844 + *errorCodePtr = EFAULT;
1.1845 + return -1;
1.1846 + }
1.1847 +
1.1848 + /*
1.1849 + * Check to see if the socket is connected before trying to write.
1.1850 + */
1.1851 +
1.1852 + if ((infoPtr->flags & SOCKET_ASYNC_CONNECT)
1.1853 + && ! WaitForSocketEvent(infoPtr, FD_CONNECT, errorCodePtr)) {
1.1854 + return -1;
1.1855 + }
1.1856 +
1.1857 + while (1) {
1.1858 + SendMessage(tsdPtr->hwnd, SOCKET_SELECT,
1.1859 + (WPARAM) UNSELECT, (LPARAM) infoPtr);
1.1860 +
1.1861 + bytesWritten = winSock.send(infoPtr->socket, buf, toWrite, 0);
1.1862 + if (bytesWritten != SOCKET_ERROR) {
1.1863 + /*
1.1864 + * Since Windows won't generate a new write event until we hit
1.1865 + * an overflow condition, we need to force the event loop to
1.1866 + * poll until the condition changes.
1.1867 + */
1.1868 +
1.1869 + if (infoPtr->watchEvents & FD_WRITE) {
1.1870 + Tcl_Time blockTime = { 0, 0 };
1.1871 + Tcl_SetMaxBlockTime(&blockTime);
1.1872 + }
1.1873 + break;
1.1874 + }
1.1875 +
1.1876 + /*
1.1877 + * Check for error condition or overflow. In the event of overflow, we
1.1878 + * need to clear the FD_WRITE flag so we can detect the next writable
1.1879 + * event. Note that Windows only sends a new writable event after a
1.1880 + * send fails with WSAEWOULDBLOCK.
1.1881 + */
1.1882 +
1.1883 + error = winSock.WSAGetLastError();
1.1884 + if (error == WSAEWOULDBLOCK) {
1.1885 + infoPtr->readyEvents &= ~(FD_WRITE);
1.1886 + if (infoPtr->flags & SOCKET_ASYNC) {
1.1887 + *errorCodePtr = EWOULDBLOCK;
1.1888 + bytesWritten = -1;
1.1889 + break;
1.1890 + }
1.1891 + } else {
1.1892 + TclWinConvertWSAError(error);
1.1893 + *errorCodePtr = Tcl_GetErrno();
1.1894 + bytesWritten = -1;
1.1895 + break;
1.1896 + }
1.1897 +
1.1898 + /*
1.1899 + * In the blocking case, wait until the file becomes writable
1.1900 + * or closed and try again.
1.1901 + */
1.1902 +
1.1903 + if (!WaitForSocketEvent(infoPtr, FD_WRITE|FD_CLOSE, errorCodePtr)) {
1.1904 + bytesWritten = -1;
1.1905 + break;
1.1906 + }
1.1907 + }
1.1908 +
1.1909 + SendMessage(tsdPtr->hwnd, SOCKET_SELECT,
1.1910 + (WPARAM) SELECT, (LPARAM) infoPtr);
1.1911 +
1.1912 + return bytesWritten;
1.1913 +}
1.1914 +
1.1915 +/*
1.1916 + *----------------------------------------------------------------------
1.1917 + *
1.1918 + * TcpSetOptionProc --
1.1919 + *
1.1920 + * Sets Tcp channel specific options.
1.1921 + *
1.1922 + * Results:
1.1923 + * None, unless an error happens.
1.1924 + *
1.1925 + * Side effects:
1.1926 + * Changes attributes of the socket at the system level.
1.1927 + *
1.1928 + *----------------------------------------------------------------------
1.1929 + */
1.1930 +
1.1931 +static int
1.1932 +TcpSetOptionProc (
1.1933 + ClientData instanceData, /* Socket state. */
1.1934 + Tcl_Interp *interp, /* For error reporting - can be NULL. */
1.1935 + CONST char *optionName, /* Name of the option to set. */
1.1936 + CONST char *value) /* New value for option. */
1.1937 +{
1.1938 + SocketInfo *infoPtr;
1.1939 + SOCKET sock;
1.1940 +/*
1.1941 + BOOL val = FALSE;
1.1942 + int boolVar, rtn;
1.1943 +*/
1.1944 + /*
1.1945 + * Check that WinSock is initialized; do not call it if not, to
1.1946 + * prevent system crashes. This can happen at exit time if the exit
1.1947 + * handler for WinSock ran before other exit handlers that want to
1.1948 + * use sockets.
1.1949 + */
1.1950 +
1.1951 + if (!SocketsEnabled()) {
1.1952 + if (interp) {
1.1953 + Tcl_AppendResult(interp, "winsock is not initialized", NULL);
1.1954 + }
1.1955 + return TCL_ERROR;
1.1956 + }
1.1957 +
1.1958 + infoPtr = (SocketInfo *) instanceData;
1.1959 + sock = infoPtr->socket;
1.1960 +
1.1961 +/*
1.1962 + if (!stricmp(optionName, "-keepalive")) {
1.1963 + if (Tcl_GetBoolean(interp, value, &boolVar) != TCL_OK) {
1.1964 + return TCL_ERROR;
1.1965 + }
1.1966 + if (boolVar) val = TRUE;
1.1967 + rtn = winSock.setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE,
1.1968 + (const char *) &val, sizeof(BOOL));
1.1969 + if (rtn != 0) {
1.1970 + TclWinConvertWSAError(winSock.WSAGetLastError());
1.1971 + if (interp) {
1.1972 + Tcl_AppendResult(interp, "couldn't set socket option: ",
1.1973 + Tcl_PosixError(interp), NULL);
1.1974 + }
1.1975 + return TCL_ERROR;
1.1976 + }
1.1977 + return TCL_OK;
1.1978 +
1.1979 + } else if (!stricmp(optionName, "-nagle")) {
1.1980 + if (Tcl_GetBoolean(interp, value, &boolVar) != TCL_OK) {
1.1981 + return TCL_ERROR;
1.1982 + }
1.1983 + if (!boolVar) val = TRUE;
1.1984 + rtn = winSock.setsockopt(sock, IPPROTO_TCP, TCP_NODELAY,
1.1985 + (const char *) &val, sizeof(BOOL));
1.1986 + if (rtn != 0) {
1.1987 + TclWinConvertWSAError(winSock.WSAGetLastError());
1.1988 + if (interp) {
1.1989 + Tcl_AppendResult(interp, "couldn't set socket option: ",
1.1990 + Tcl_PosixError(interp), NULL);
1.1991 + }
1.1992 + return TCL_ERROR;
1.1993 + }
1.1994 + return TCL_OK;
1.1995 + }
1.1996 +
1.1997 + return Tcl_BadChannelOption(interp, optionName, "keepalive nagle");
1.1998 +*/
1.1999 + return Tcl_BadChannelOption(interp, optionName, "");
1.2000 +}
1.2001 +
1.2002 +/*
1.2003 + *----------------------------------------------------------------------
1.2004 + *
1.2005 + * TcpGetOptionProc --
1.2006 + *
1.2007 + * Computes an option value for a TCP socket based channel, or a
1.2008 + * list of all options and their values.
1.2009 + *
1.2010 + * Note: This code is based on code contributed by John Haxby.
1.2011 + *
1.2012 + * Results:
1.2013 + * A standard Tcl result. The value of the specified option or a
1.2014 + * list of all options and their values is returned in the
1.2015 + * supplied DString.
1.2016 + *
1.2017 + * Side effects:
1.2018 + * None.
1.2019 + *
1.2020 + *----------------------------------------------------------------------
1.2021 + */
1.2022 +
1.2023 +static int
1.2024 +TcpGetOptionProc(instanceData, interp, optionName, dsPtr)
1.2025 + ClientData instanceData; /* Socket state. */
1.2026 + Tcl_Interp *interp; /* For error reporting - can be NULL */
1.2027 + CONST char *optionName; /* Name of the option to
1.2028 + * retrieve the value for, or
1.2029 + * NULL to get all options and
1.2030 + * their values. */
1.2031 + Tcl_DString *dsPtr; /* Where to store the computed
1.2032 + * value; initialized by caller. */
1.2033 +{
1.2034 + SocketInfo *infoPtr;
1.2035 + SOCKADDR_IN sockname;
1.2036 + SOCKADDR_IN peername;
1.2037 + struct hostent *hostEntPtr;
1.2038 + SOCKET sock;
1.2039 + int size = sizeof(SOCKADDR_IN);
1.2040 + size_t len = 0;
1.2041 + char buf[TCL_INTEGER_SPACE];
1.2042 +
1.2043 + /*
1.2044 + * Check that WinSock is initialized; do not call it if not, to
1.2045 + * prevent system crashes. This can happen at exit time if the exit
1.2046 + * handler for WinSock ran before other exit handlers that want to
1.2047 + * use sockets.
1.2048 + */
1.2049 +
1.2050 + if (!SocketsEnabled()) {
1.2051 + if (interp) {
1.2052 + Tcl_AppendResult(interp, "winsock is not initialized", NULL);
1.2053 + }
1.2054 + return TCL_ERROR;
1.2055 + }
1.2056 +
1.2057 + infoPtr = (SocketInfo *) instanceData;
1.2058 + sock = (int) infoPtr->socket;
1.2059 + if (optionName != (char *) NULL) {
1.2060 + len = strlen(optionName);
1.2061 + }
1.2062 +
1.2063 + if ((len > 1) && (optionName[1] == 'e') &&
1.2064 + (strncmp(optionName, "-error", len) == 0)) {
1.2065 + int optlen;
1.2066 + DWORD err;
1.2067 + int ret;
1.2068 +
1.2069 + optlen = sizeof(int);
1.2070 + ret = TclWinGetSockOpt(sock, SOL_SOCKET, SO_ERROR,
1.2071 + (char *)&err, &optlen);
1.2072 + if (ret == SOCKET_ERROR) {
1.2073 + err = winSock.WSAGetLastError();
1.2074 + }
1.2075 + if (err) {
1.2076 + TclWinConvertWSAError(err);
1.2077 + Tcl_DStringAppend(dsPtr, Tcl_ErrnoMsg(Tcl_GetErrno()), -1);
1.2078 + }
1.2079 + return TCL_OK;
1.2080 + }
1.2081 +
1.2082 + if ((len == 0) ||
1.2083 + ((len > 1) && (optionName[1] == 'p') &&
1.2084 + (strncmp(optionName, "-peername", len) == 0))) {
1.2085 + if (winSock.getpeername(sock, (LPSOCKADDR) &peername, &size)
1.2086 + == 0) {
1.2087 + if (len == 0) {
1.2088 + Tcl_DStringAppendElement(dsPtr, "-peername");
1.2089 + Tcl_DStringStartSublist(dsPtr);
1.2090 + }
1.2091 + Tcl_DStringAppendElement(dsPtr,
1.2092 + winSock.inet_ntoa(peername.sin_addr));
1.2093 +
1.2094 + if (peername.sin_addr.s_addr == 0) {
1.2095 + hostEntPtr = (struct hostent *) NULL;
1.2096 + } else {
1.2097 + hostEntPtr = winSock.gethostbyaddr(
1.2098 + (char *) &(peername.sin_addr), sizeof(peername.sin_addr),
1.2099 + AF_INET);
1.2100 + }
1.2101 + if (hostEntPtr != (struct hostent *) NULL) {
1.2102 + Tcl_DStringAppendElement(dsPtr, hostEntPtr->h_name);
1.2103 + } else {
1.2104 + Tcl_DStringAppendElement(dsPtr,
1.2105 + winSock.inet_ntoa(peername.sin_addr));
1.2106 + }
1.2107 + TclFormatInt(buf, winSock.ntohs(peername.sin_port));
1.2108 + Tcl_DStringAppendElement(dsPtr, buf);
1.2109 + if (len == 0) {
1.2110 + Tcl_DStringEndSublist(dsPtr);
1.2111 + } else {
1.2112 + return TCL_OK;
1.2113 + }
1.2114 + } else {
1.2115 + /*
1.2116 + * getpeername failed - but if we were asked for all the options
1.2117 + * (len==0), don't flag an error at that point because it could
1.2118 + * be an fconfigure request on a server socket. (which have
1.2119 + * no peer). {copied from unix/tclUnixChan.c}
1.2120 + */
1.2121 + if (len) {
1.2122 + TclWinConvertWSAError((DWORD) winSock.WSAGetLastError());
1.2123 + if (interp) {
1.2124 + Tcl_AppendResult(interp, "can't get peername: ",
1.2125 + Tcl_PosixError(interp),
1.2126 + (char *) NULL);
1.2127 + }
1.2128 + return TCL_ERROR;
1.2129 + }
1.2130 + }
1.2131 + }
1.2132 +
1.2133 + if ((len == 0) ||
1.2134 + ((len > 1) && (optionName[1] == 's') &&
1.2135 + (strncmp(optionName, "-sockname", len) == 0))) {
1.2136 + if (winSock.getsockname(sock, (LPSOCKADDR) &sockname, &size)
1.2137 + == 0) {
1.2138 + if (len == 0) {
1.2139 + Tcl_DStringAppendElement(dsPtr, "-sockname");
1.2140 + Tcl_DStringStartSublist(dsPtr);
1.2141 + }
1.2142 + Tcl_DStringAppendElement(dsPtr,
1.2143 + winSock.inet_ntoa(sockname.sin_addr));
1.2144 + if (sockname.sin_addr.s_addr == 0) {
1.2145 + hostEntPtr = (struct hostent *) NULL;
1.2146 + } else {
1.2147 + hostEntPtr = winSock.gethostbyaddr(
1.2148 + (char *) &(sockname.sin_addr), sizeof(peername.sin_addr),
1.2149 + AF_INET);
1.2150 + }
1.2151 + if (hostEntPtr != (struct hostent *) NULL) {
1.2152 + Tcl_DStringAppendElement(dsPtr, hostEntPtr->h_name);
1.2153 + } else {
1.2154 + Tcl_DStringAppendElement(dsPtr,
1.2155 + winSock.inet_ntoa(sockname.sin_addr));
1.2156 + }
1.2157 + TclFormatInt(buf, winSock.ntohs(sockname.sin_port));
1.2158 + Tcl_DStringAppendElement(dsPtr, buf);
1.2159 + if (len == 0) {
1.2160 + Tcl_DStringEndSublist(dsPtr);
1.2161 + } else {
1.2162 + return TCL_OK;
1.2163 + }
1.2164 + } else {
1.2165 + if (interp) {
1.2166 + TclWinConvertWSAError((DWORD) winSock.WSAGetLastError());
1.2167 + Tcl_AppendResult(interp, "can't get sockname: ",
1.2168 + Tcl_PosixError(interp),
1.2169 + (char *) NULL);
1.2170 + }
1.2171 + return TCL_ERROR;
1.2172 + }
1.2173 + }
1.2174 +
1.2175 +/*
1.2176 + if (len == 0 || !strncmp(optionName, "-keepalive", len)) {
1.2177 + int optlen;
1.2178 + BOOL opt = FALSE;
1.2179 +
1.2180 + if (len == 0) {
1.2181 + Tcl_DStringAppendElement(dsPtr, "-keepalive");
1.2182 + }
1.2183 + optlen = sizeof(BOOL);
1.2184 + winSock.getsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, (char *)&opt,
1.2185 + &optlen);
1.2186 + if (opt) {
1.2187 + Tcl_DStringAppendElement(dsPtr, "1");
1.2188 + } else {
1.2189 + Tcl_DStringAppendElement(dsPtr, "0");
1.2190 + }
1.2191 + if (len > 0) return TCL_OK;
1.2192 + }
1.2193 +
1.2194 + if (len == 0 || !strncmp(optionName, "-nagle", len)) {
1.2195 + int optlen;
1.2196 + BOOL opt = FALSE;
1.2197 +
1.2198 + if (len == 0) {
1.2199 + Tcl_DStringAppendElement(dsPtr, "-nagle");
1.2200 + }
1.2201 + optlen = sizeof(BOOL);
1.2202 + winSock.getsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (char *)&opt,
1.2203 + &optlen);
1.2204 + if (opt) {
1.2205 + Tcl_DStringAppendElement(dsPtr, "0");
1.2206 + } else {
1.2207 + Tcl_DStringAppendElement(dsPtr, "1");
1.2208 + }
1.2209 + if (len > 0) return TCL_OK;
1.2210 + }
1.2211 +*/
1.2212 +
1.2213 + if (len > 0) {
1.2214 + /*return Tcl_BadChannelOption(interp, optionName, "peername sockname keepalive nagle");*/
1.2215 + return Tcl_BadChannelOption(interp, optionName, "peername sockname");
1.2216 + }
1.2217 +
1.2218 + return TCL_OK;
1.2219 +}
1.2220 +
1.2221 +/*
1.2222 + *----------------------------------------------------------------------
1.2223 + *
1.2224 + * TcpWatchProc --
1.2225 + *
1.2226 + * Informs the channel driver of the events that the generic
1.2227 + * channel code wishes to receive on this socket.
1.2228 + *
1.2229 + * Results:
1.2230 + * None.
1.2231 + *
1.2232 + * Side effects:
1.2233 + * May cause the notifier to poll if any of the specified
1.2234 + * conditions are already true.
1.2235 + *
1.2236 + *----------------------------------------------------------------------
1.2237 + */
1.2238 +
1.2239 +static void
1.2240 +TcpWatchProc(instanceData, mask)
1.2241 + ClientData instanceData; /* The socket state. */
1.2242 + int mask; /* Events of interest; an OR-ed
1.2243 + * combination of TCL_READABLE,
1.2244 + * TCL_WRITABLE and TCL_EXCEPTION. */
1.2245 +{
1.2246 + SocketInfo *infoPtr = (SocketInfo *) instanceData;
1.2247 +
1.2248 + /*
1.2249 + * Update the watch events mask. Only if the socket is not a
1.2250 + * server socket. Fix for SF Tcl Bug #557878.
1.2251 + */
1.2252 +
1.2253 + if (!infoPtr->acceptProc) {
1.2254 + infoPtr->watchEvents = 0;
1.2255 + if (mask & TCL_READABLE) {
1.2256 + infoPtr->watchEvents |= (FD_READ|FD_CLOSE|FD_ACCEPT);
1.2257 + }
1.2258 + if (mask & TCL_WRITABLE) {
1.2259 + infoPtr->watchEvents |= (FD_WRITE|FD_CLOSE|FD_CONNECT);
1.2260 + }
1.2261 +
1.2262 + /*
1.2263 + * If there are any conditions already set, then tell the notifier to poll
1.2264 + * rather than block.
1.2265 + */
1.2266 +
1.2267 + if (infoPtr->readyEvents & infoPtr->watchEvents) {
1.2268 + Tcl_Time blockTime = { 0, 0 };
1.2269 + Tcl_SetMaxBlockTime(&blockTime);
1.2270 + }
1.2271 + }
1.2272 +}
1.2273 +
1.2274 +/*
1.2275 + *----------------------------------------------------------------------
1.2276 + *
1.2277 + * TcpGetProc --
1.2278 + *
1.2279 + * Called from Tcl_GetChannelHandle to retrieve an OS handle from inside
1.2280 + * a TCP socket based channel.
1.2281 + *
1.2282 + * Results:
1.2283 + * Returns TCL_OK with the socket in handlePtr.
1.2284 + *
1.2285 + * Side effects:
1.2286 + * None.
1.2287 + *
1.2288 + *----------------------------------------------------------------------
1.2289 + */
1.2290 +
1.2291 +static int
1.2292 +TcpGetHandleProc(instanceData, direction, handlePtr)
1.2293 + ClientData instanceData; /* The socket state. */
1.2294 + int direction; /* Not used. */
1.2295 + ClientData *handlePtr; /* Where to store the handle. */
1.2296 +{
1.2297 + SocketInfo *statePtr = (SocketInfo *) instanceData;
1.2298 +
1.2299 + *handlePtr = (ClientData) statePtr->socket;
1.2300 + return TCL_OK;
1.2301 +}
1.2302 +
1.2303 +/*
1.2304 + *----------------------------------------------------------------------
1.2305 + *
1.2306 + * SocketThread --
1.2307 + *
1.2308 + * Helper thread used to manage the socket event handling window.
1.2309 + *
1.2310 + * Results:
1.2311 + * 1 if unable to create socket event window, 0 otherwise.
1.2312 + *
1.2313 + * Side effects:
1.2314 + * None.
1.2315 + *
1.2316 + *----------------------------------------------------------------------
1.2317 + */
1.2318 +
1.2319 +static DWORD WINAPI
1.2320 +SocketThread(LPVOID arg)
1.2321 +{
1.2322 + MSG msg;
1.2323 + ThreadSpecificData *tsdPtr = (ThreadSpecificData *)(arg);
1.2324 +
1.2325 + /*
1.2326 + * Create a dummy window receiving socket events.
1.2327 + */
1.2328 +
1.2329 + tsdPtr->hwnd = CreateWindow("TclSocket", "TclSocket",
1.2330 + WS_TILED, 0, 0, 0, 0, NULL, NULL, windowClass.hInstance, arg);
1.2331 +
1.2332 + /*
1.2333 + * Signalize thread creator that we are done creating the window.
1.2334 + */
1.2335 +
1.2336 + SetEvent(tsdPtr->readyEvent);
1.2337 +
1.2338 + /*
1.2339 + * If unable to create the window, exit this thread immediately.
1.2340 + */
1.2341 +
1.2342 + if (tsdPtr->hwnd == NULL) {
1.2343 + return 1;
1.2344 + }
1.2345 +
1.2346 + /*
1.2347 + * Process all messages on the socket window until WM_QUIT.
1.2348 + * This threads exits only when instructed to do so by the
1.2349 + * call to PostMessage(SOCKET_TERMINATE) in TclpFinalizeSockets().
1.2350 + */
1.2351 +
1.2352 + while (GetMessage(&msg, NULL, 0, 0) > 0) {
1.2353 + DispatchMessage(&msg);
1.2354 + }
1.2355 +
1.2356 + /*
1.2357 + * This releases waiters on thread exit in TclpFinalizeSockets()
1.2358 + */
1.2359 +
1.2360 + SetEvent(tsdPtr->readyEvent);
1.2361 +
1.2362 + return (DWORD)msg.wParam;
1.2363 +}
1.2364 +
1.2365 +
1.2366 +/*
1.2367 + *----------------------------------------------------------------------
1.2368 + *
1.2369 + * SocketProc --
1.2370 + *
1.2371 + * This function is called when WSAAsyncSelect has been used
1.2372 + * to register interest in a socket event, and the event has
1.2373 + * occurred.
1.2374 + *
1.2375 + * Results:
1.2376 + * 0 on success.
1.2377 + *
1.2378 + * Side effects:
1.2379 + * The flags for the given socket are updated to reflect the
1.2380 + * event that occured.
1.2381 + *
1.2382 + *----------------------------------------------------------------------
1.2383 + */
1.2384 +
1.2385 +static LRESULT CALLBACK
1.2386 +SocketProc(hwnd, message, wParam, lParam)
1.2387 + HWND hwnd;
1.2388 + UINT message;
1.2389 + WPARAM wParam;
1.2390 + LPARAM lParam;
1.2391 +{
1.2392 + int event, error;
1.2393 + SOCKET socket;
1.2394 + SocketInfo *infoPtr;
1.2395 + ThreadSpecificData *tsdPtr =
1.2396 +#ifdef _WIN64
1.2397 + (ThreadSpecificData *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
1.2398 +#else
1.2399 + (ThreadSpecificData *) GetWindowLong(hwnd, GWL_USERDATA);
1.2400 +#endif
1.2401 +
1.2402 + switch (message) {
1.2403 +
1.2404 + default:
1.2405 + return DefWindowProc(hwnd, message, wParam, lParam);
1.2406 + break;
1.2407 +
1.2408 + case WM_CREATE:
1.2409 + /*
1.2410 + * store the initial tsdPtr, it's from a different thread, so it's
1.2411 + * not directly accessible, but needed.
1.2412 + */
1.2413 +
1.2414 +#ifdef _WIN64
1.2415 + SetWindowLongPtr(hwnd, GWLP_USERDATA,
1.2416 + (LONG_PTR) ((LPCREATESTRUCT)lParam)->lpCreateParams);
1.2417 +#else
1.2418 + SetWindowLong(hwnd, GWL_USERDATA,
1.2419 + (LONG) ((LPCREATESTRUCT)lParam)->lpCreateParams);
1.2420 +#endif
1.2421 + break;
1.2422 +
1.2423 + case WM_DESTROY:
1.2424 + PostQuitMessage(0);
1.2425 + break;
1.2426 +
1.2427 + case SOCKET_MESSAGE:
1.2428 + event = WSAGETSELECTEVENT(lParam);
1.2429 + error = WSAGETSELECTERROR(lParam);
1.2430 + socket = (SOCKET) wParam;
1.2431 +
1.2432 + /*
1.2433 + * Find the specified socket on the socket list and update its
1.2434 + * eventState flag.
1.2435 + */
1.2436 +
1.2437 + WaitForSingleObject(tsdPtr->socketListLock, INFINITE);
1.2438 + for (infoPtr = tsdPtr->socketList; infoPtr != NULL;
1.2439 + infoPtr = infoPtr->nextPtr) {
1.2440 + if (infoPtr->socket == socket) {
1.2441 + /*
1.2442 + * Update the socket state.
1.2443 + */
1.2444 +
1.2445 + /*
1.2446 + * A count of FD_ACCEPTS is stored, so if an FD_CLOSE
1.2447 + * event happens, then clear the FD_ACCEPT count.
1.2448 + * Otherwise, increment the count if the current
1.2449 + * event is an FD_ACCEPT.
1.2450 + */
1.2451 +
1.2452 + if (event & FD_CLOSE) {
1.2453 + infoPtr->acceptEventCount = 0;
1.2454 + infoPtr->readyEvents &= ~(FD_WRITE|FD_ACCEPT);
1.2455 + } else if (event & FD_ACCEPT) {
1.2456 + infoPtr->acceptEventCount++;
1.2457 + }
1.2458 +
1.2459 + if (event & FD_CONNECT) {
1.2460 + /*
1.2461 + * The socket is now connected,
1.2462 + * clear the async connect flag.
1.2463 + */
1.2464 +
1.2465 + infoPtr->flags &= ~(SOCKET_ASYNC_CONNECT);
1.2466 +
1.2467 + /*
1.2468 + * Remember any error that occurred so we can report
1.2469 + * connection failures.
1.2470 + */
1.2471 +
1.2472 + if (error != ERROR_SUCCESS) {
1.2473 + TclWinConvertWSAError((DWORD) error);
1.2474 + infoPtr->lastError = Tcl_GetErrno();
1.2475 + }
1.2476 +
1.2477 + }
1.2478 + if(infoPtr->flags & SOCKET_ASYNC_CONNECT) {
1.2479 + infoPtr->flags &= ~(SOCKET_ASYNC_CONNECT);
1.2480 + if (error != ERROR_SUCCESS) {
1.2481 + TclWinConvertWSAError((DWORD) error);
1.2482 + infoPtr->lastError = Tcl_GetErrno();
1.2483 + }
1.2484 + infoPtr->readyEvents |= FD_WRITE;
1.2485 + }
1.2486 + infoPtr->readyEvents |= event;
1.2487 +
1.2488 + /*
1.2489 + * Wake up the Main Thread.
1.2490 + */
1.2491 + SetEvent(tsdPtr->readyEvent);
1.2492 + Tcl_ThreadAlert(tsdPtr->threadId);
1.2493 + break;
1.2494 + }
1.2495 + }
1.2496 + SetEvent(tsdPtr->socketListLock);
1.2497 + break;
1.2498 +
1.2499 + case SOCKET_SELECT:
1.2500 + infoPtr = (SocketInfo *) lParam;
1.2501 + if (wParam == SELECT) {
1.2502 +
1.2503 + winSock.WSAAsyncSelect(infoPtr->socket, hwnd,
1.2504 + SOCKET_MESSAGE, infoPtr->selectEvents);
1.2505 + } else {
1.2506 + /*
1.2507 + * Clear the selection mask
1.2508 + */
1.2509 +
1.2510 + winSock.WSAAsyncSelect(infoPtr->socket, hwnd, 0, 0);
1.2511 + }
1.2512 + break;
1.2513 +
1.2514 + case SOCKET_TERMINATE:
1.2515 + DestroyWindow(hwnd);
1.2516 + break;
1.2517 + }
1.2518 +
1.2519 + return 0;
1.2520 +}
1.2521 +
1.2522 +/*
1.2523 + *----------------------------------------------------------------------
1.2524 + *
1.2525 + * Tcl_GetHostName --
1.2526 + *
1.2527 + * Returns the name of the local host.
1.2528 + *
1.2529 + * Results:
1.2530 + * A string containing the network name for this machine, or
1.2531 + * an empty string if we can't figure out the name. The caller
1.2532 + * must not modify or free this string.
1.2533 + *
1.2534 + * Side effects:
1.2535 + * None.
1.2536 + *
1.2537 + *----------------------------------------------------------------------
1.2538 + */
1.2539 +
1.2540 +CONST char *
1.2541 +Tcl_GetHostName()
1.2542 +{
1.2543 + DWORD length;
1.2544 + WCHAR wbuf[MAX_COMPUTERNAME_LENGTH + 1];
1.2545 +
1.2546 + Tcl_MutexLock(&socketMutex);
1.2547 + InitSockets();
1.2548 +
1.2549 + if (hostnameInitialized) {
1.2550 + Tcl_MutexUnlock(&socketMutex);
1.2551 + return hostname;
1.2552 + }
1.2553 + Tcl_MutexUnlock(&socketMutex);
1.2554 +
1.2555 + if (TclpHasSockets(NULL) == TCL_OK) {
1.2556 + /*
1.2557 + * INTL: bug
1.2558 + */
1.2559 +
1.2560 + if (winSock.gethostname(hostname, sizeof(hostname)) == 0) {
1.2561 + Tcl_MutexLock(&socketMutex);
1.2562 + hostnameInitialized = 1;
1.2563 + Tcl_MutexUnlock(&socketMutex);
1.2564 + return hostname;
1.2565 + }
1.2566 + }
1.2567 + Tcl_MutexLock(&socketMutex);
1.2568 + length = sizeof(hostname);
1.2569 + if ((*tclWinProcs->getComputerNameProc)(wbuf, &length) != 0) {
1.2570 + /*
1.2571 + * Convert string from native to UTF then change to lowercase.
1.2572 + */
1.2573 +
1.2574 + Tcl_DString ds;
1.2575 +
1.2576 + lstrcpynA(hostname, Tcl_WinTCharToUtf((TCHAR *) wbuf, -1, &ds),
1.2577 + sizeof(hostname));
1.2578 + Tcl_DStringFree(&ds);
1.2579 + Tcl_UtfToLower(hostname);
1.2580 + } else {
1.2581 + hostname[0] = '\0';
1.2582 + }
1.2583 + hostnameInitialized = 1;
1.2584 + Tcl_MutexUnlock(&socketMutex);
1.2585 + return hostname;
1.2586 +}
1.2587 +
1.2588 +/*
1.2589 + *----------------------------------------------------------------------
1.2590 + *
1.2591 + * TclWinGetSockOpt, et al. --
1.2592 + *
1.2593 + * These functions are wrappers that let us bind the WinSock
1.2594 + * API dynamically so we can run on systems that don't have
1.2595 + * the wsock32.dll. We need wrappers for these interfaces
1.2596 + * because they are called from the generic Tcl code.
1.2597 + *
1.2598 + * Results:
1.2599 + * As defined for each function.
1.2600 + *
1.2601 + * Side effects:
1.2602 + * As defined for each function.
1.2603 + *
1.2604 + *----------------------------------------------------------------------
1.2605 + */
1.2606 +
1.2607 +int
1.2608 +TclWinGetSockOpt(SOCKET s, int level, int optname, char * optval,
1.2609 + int FAR *optlen)
1.2610 +{
1.2611 + /*
1.2612 + * Check that WinSock is initialized; do not call it if not, to
1.2613 + * prevent system crashes. This can happen at exit time if the exit
1.2614 + * handler for WinSock ran before other exit handlers that want to
1.2615 + * use sockets.
1.2616 + */
1.2617 +
1.2618 + if (!SocketsEnabled()) {
1.2619 + return SOCKET_ERROR;
1.2620 + }
1.2621 +
1.2622 + return winSock.getsockopt(s, level, optname, optval, optlen);
1.2623 +}
1.2624 +
1.2625 +int
1.2626 +TclWinSetSockOpt(SOCKET s, int level, int optname, const char * optval,
1.2627 + int optlen)
1.2628 +{
1.2629 + /*
1.2630 + * Check that WinSock is initialized; do not call it if not, to
1.2631 + * prevent system crashes. This can happen at exit time if the exit
1.2632 + * handler for WinSock ran before other exit handlers that want to
1.2633 + * use sockets.
1.2634 + */
1.2635 + if (!SocketsEnabled()) {
1.2636 + return SOCKET_ERROR;
1.2637 + }
1.2638 +
1.2639 + return winSock.setsockopt(s, level, optname, optval, optlen);
1.2640 +}
1.2641 +
1.2642 +u_short
1.2643 +TclWinNToHS(u_short netshort)
1.2644 +{
1.2645 + /*
1.2646 + * Check that WinSock is initialized; do not call it if not, to
1.2647 + * prevent system crashes. This can happen at exit time if the exit
1.2648 + * handler for WinSock ran before other exit handlers that want to
1.2649 + * use sockets.
1.2650 + */
1.2651 +
1.2652 + if (!SocketsEnabled()) {
1.2653 + return (u_short) -1;
1.2654 + }
1.2655 +
1.2656 + return winSock.ntohs(netshort);
1.2657 +}
1.2658 +
1.2659 +struct servent *
1.2660 +TclWinGetServByName(const char * name, const char * proto)
1.2661 +{
1.2662 + /*
1.2663 + * Check that WinSock is initialized; do not call it if not, to
1.2664 + * prevent system crashes. This can happen at exit time if the exit
1.2665 + * handler for WinSock ran before other exit handlers that want to
1.2666 + * use sockets.
1.2667 + */
1.2668 + if (!SocketsEnabled()) {
1.2669 + return (struct servent *) NULL;
1.2670 + }
1.2671 +
1.2672 + return winSock.getservbyname(name, proto);
1.2673 +}
1.2674 +
1.2675 +/*
1.2676 + *----------------------------------------------------------------------
1.2677 + *
1.2678 + * TcpThreadActionProc --
1.2679 + *
1.2680 + * Insert or remove any thread local refs to this channel.
1.2681 + *
1.2682 + * Results:
1.2683 + * None.
1.2684 + *
1.2685 + * Side effects:
1.2686 + * Changes thread local list of valid channels.
1.2687 + *
1.2688 + *----------------------------------------------------------------------
1.2689 + */
1.2690 +
1.2691 +static void
1.2692 +TcpThreadActionProc (instanceData, action)
1.2693 + ClientData instanceData;
1.2694 + int action;
1.2695 +{
1.2696 + ThreadSpecificData *tsdPtr;
1.2697 + SocketInfo *infoPtr = (SocketInfo *) instanceData;
1.2698 + int notifyCmd;
1.2699 +
1.2700 + if (action == TCL_CHANNEL_THREAD_INSERT) {
1.2701 + /*
1.2702 + * Ensure that socket subsystem is initialized in this thread, or
1.2703 + * else sockets will not work.
1.2704 + */
1.2705 +
1.2706 + Tcl_MutexLock(&socketMutex);
1.2707 + InitSockets();
1.2708 + Tcl_MutexUnlock(&socketMutex);
1.2709 +
1.2710 + tsdPtr = TCL_TSD_INIT(&dataKey);
1.2711 +
1.2712 + WaitForSingleObject(tsdPtr->socketListLock, INFINITE);
1.2713 + infoPtr->nextPtr = tsdPtr->socketList;
1.2714 + tsdPtr->socketList = infoPtr;
1.2715 + SetEvent(tsdPtr->socketListLock);
1.2716 +
1.2717 + notifyCmd = SELECT;
1.2718 + } else {
1.2719 + SocketInfo **nextPtrPtr;
1.2720 + int removed = 0;
1.2721 +
1.2722 + tsdPtr = TCL_TSD_INIT(&dataKey);
1.2723 +
1.2724 + /* TIP #218, Bugfix: All access to socketList has to be protected by the lock */
1.2725 + WaitForSingleObject(tsdPtr->socketListLock, INFINITE);
1.2726 + for (nextPtrPtr = &(tsdPtr->socketList); (*nextPtrPtr) != NULL;
1.2727 + nextPtrPtr = &((*nextPtrPtr)->nextPtr)) {
1.2728 + if ((*nextPtrPtr) == infoPtr) {
1.2729 + (*nextPtrPtr) = infoPtr->nextPtr;
1.2730 + removed = 1;
1.2731 + break;
1.2732 + }
1.2733 + }
1.2734 + SetEvent(tsdPtr->socketListLock);
1.2735 +
1.2736 + /*
1.2737 + * This could happen if the channel was created in one thread
1.2738 + * and then moved to another without updating the thread
1.2739 + * local data in each thread.
1.2740 + */
1.2741 +
1.2742 + if (!removed) {
1.2743 + Tcl_Panic("file info ptr not on thread channel list");
1.2744 + }
1.2745 +
1.2746 + notifyCmd = UNSELECT;
1.2747 + }
1.2748 +
1.2749 + /*
1.2750 + * Ensure that, or stop, notifications for the socket occur in this thread.
1.2751 + */
1.2752 +
1.2753 + SendMessage(tsdPtr->hwnd, SOCKET_SELECT,
1.2754 + (WPARAM) notifyCmd, (LPARAM) infoPtr);
1.2755 +}