diff options
Diffstat (limited to 'unix/tclUnixSock.c')
-rw-r--r-- | unix/tclUnixSock.c | 217 |
1 files changed, 151 insertions, 66 deletions
diff --git a/unix/tclUnixSock.c b/unix/tclUnixSock.c index 1a54914..8c9386c 100644 --- a/unix/tclUnixSock.c +++ b/unix/tclUnixSock.c @@ -53,6 +53,8 @@ typedef struct TcpFdList { struct TcpState { Tcl_Channel channel; /* Channel associated with this file. */ + int testFlags; /* bit field for tests. Is set by testsocket + * test procedure */ TcpFdList fds; /* The file descriptors of the sockets. */ int flags; /* ORed combination of the bitfields defined * below. */ @@ -64,7 +66,7 @@ struct TcpState { Tcl_TcpAcceptProc *acceptProc; /* Proc to call on accept. */ - ClientData acceptProcData; /* The data for the accept proc. */ + void *acceptProcData; /* The data for the accept proc. */ /* * Only needed for client sockets @@ -94,6 +96,15 @@ struct TcpState { #define TCP_ASYNC_FAILED (1<<5) /* An async connect finally failed */ /* + * These bits may be ORed together into the "testFlags" field of a TcpState + * structure. + */ + +#define TCP_ASYNC_TEST_MODE (1<<0) /* Async testing activated. Do not + * automatically continue connection + * process. */ + +/* * The following defines the maximum length of the listen queue. This is the * number of outstanding yet-to-be-serviced requests for a connection on a * server socket, more than this number of outstanding requests and the @@ -118,27 +129,27 @@ struct TcpState { * Static routines for this file: */ -static void TcpAsyncCallback(ClientData clientData, int mask); +static void TcpAsyncCallback(void *clientData, int mask); static int TcpConnect(Tcl_Interp *interp, TcpState *state); -static void TcpAccept(ClientData data, int mask); -static int TcpBlockModeProc(ClientData data, int mode); -static int TcpCloseProc(ClientData instanceData, +static void TcpAccept(void *data, int mask); +static int TcpBlockModeProc(void *data, int mode); +static int TcpCloseProc(void *instanceData, Tcl_Interp *interp); -static int TcpClose2Proc(ClientData instanceData, +static int TcpClose2Proc(void *instanceData, Tcl_Interp *interp, int flags); -static int TcpGetHandleProc(ClientData instanceData, - int direction, ClientData *handlePtr); -static int TcpGetOptionProc(ClientData instanceData, +static int TcpGetHandleProc(void *instanceData, + int direction, void **handlePtr); +static int TcpGetOptionProc(void *instanceData, Tcl_Interp *interp, const char *optionName, Tcl_DString *dsPtr); -static int TcpInputProc(ClientData instanceData, char *buf, +static int TcpInputProc(void *instanceData, char *buf, int toRead, int *errorCode); -static int TcpOutputProc(ClientData instanceData, +static int TcpOutputProc(void *instanceData, const char *buf, int toWrite, int *errorCode); -static void TcpThreadActionProc(ClientData instanceData, int action); -static void TcpWatchProc(ClientData instanceData, int mask); +static void TcpThreadActionProc(void *instanceData, int action); +static void TcpWatchProc(void *instanceData, int mask); static int WaitForConnect(TcpState *statePtr, int *errorCodePtr); -static void WrapNotify(ClientData clientData, int mask); +static void WrapNotify(void *clientData, int mask); /* * This structure describes the channel type structure for TCP socket @@ -148,7 +159,11 @@ static void WrapNotify(ClientData clientData, int mask); static const Tcl_ChannelType tcpChannelType = { "tcp", /* Type name. */ TCL_CHANNEL_VERSION_5, /* v5 channel */ +#ifndef TCL_NO_DEPRECATED TcpCloseProc, /* Close proc. */ +#else + TCL_CLOSE2PROC, /* Close proc. */ +#endif TcpInputProc, /* Input proc. */ TcpOutputProc, /* Output proc. */ NULL, /* Seek proc. */ @@ -208,7 +223,7 @@ printaddrinfo( static void InitializeHostName( char **valuePtr, - int *lengthPtr, + unsigned int *lengthPtr, Tcl_Encoding *encodingPtr) { const char *native = NULL; @@ -218,7 +233,7 @@ InitializeHostName( struct hostent *hp; memset(&u, (int) 0, sizeof(struct utsname)); - if (uname(&u) > -1) { /* INTL: Native. */ + if (uname(&u) >= 0) { /* INTL: Native. */ hp = TclpGetHostByName(u.nodename); /* INTL: Native. */ if (hp == NULL) { /* @@ -244,9 +259,6 @@ InitializeHostName( native = u.nodename; } } - if (native == NULL) { - native = tclEmptyStringRep; - } #else /* !NO_UNAME */ /* * Uname doesn't exist; try gethostname instead. @@ -269,15 +281,21 @@ InitializeHostName( char buffer[256]; # endif - if (gethostname(buffer, sizeof(buffer)) > -1) { /* INTL: Native. */ + if (gethostname(buffer, sizeof(buffer)) >= 0) { /* INTL: Native. */ native = buffer; } #endif /* NO_UNAME */ *encodingPtr = Tcl_GetEncoding(NULL, NULL); - *lengthPtr = strlen(native); - *valuePtr = ckalloc(*lengthPtr + 1); - memcpy(*valuePtr, native, *lengthPtr + 1); + if (native) { + *lengthPtr = strlen(native); + *valuePtr = (char *)ckalloc(*lengthPtr + 1); + memcpy(*valuePtr, native, *lengthPtr + 1); + } else { + *lengthPtr = 0; + *valuePtr = (char *)ckalloc(1); + *valuePtr[0] = '\0'; + } } /* @@ -322,10 +340,8 @@ Tcl_GetHostName(void) int TclpHasSockets( - Tcl_Interp *dummy) /* Not used. */ + TCL_UNUSED(Tcl_Interp *)) { - (void)dummy; - return TCL_OK; } @@ -370,7 +386,7 @@ TclpFinalizeSockets(void) static int TcpBlockModeProc( - ClientData instanceData, /* Socket state. */ + void *instanceData, /* Socket state. */ int mode) /* The mode to set. Can be one of * TCL_MODE_BLOCKING or * TCL_MODE_NONBLOCKING. */ @@ -448,6 +464,20 @@ WaitForConnect( return 0; } + /* + * In socket test mode do not continue with the connect. + * Exceptions are: + * - Call by recv/send and blocking socket + * (errorCodePtr != NULL && !GOT_BITS(flags, TCP_NONBLOCKING)) + */ + + if (GOT_BITS(statePtr->testFlags, TCP_ASYNC_TEST_MODE) + && !(errorCodePtr != NULL + && !GOT_BITS(statePtr->flags, TCP_NONBLOCKING))) { + *errorCodePtr = EWOULDBLOCK; + return -1; + } + if (errorCodePtr == NULL || GOT_BITS(statePtr->flags, TCP_NONBLOCKING)) { timeout = 0; } else { @@ -501,7 +531,7 @@ WaitForConnect( static int TcpInputProc( - ClientData instanceData, /* Socket state. */ + void *instanceData, /* Socket state. */ char *buf, /* Where to store data read. */ int bufSize, /* How much space is available in the * buffer? */ @@ -515,7 +545,7 @@ TcpInputProc( return -1; } bytesRead = recv(statePtr->fds.fd, buf, (size_t) bufSize, 0); - if (bytesRead > -1) { + if (bytesRead >= 0) { return bytesRead; } if (errno == ECONNRESET) { @@ -552,7 +582,7 @@ TcpInputProc( static int TcpOutputProc( - ClientData instanceData, /* Socket state. */ + void *instanceData, /* Socket state. */ const char *buf, /* The data buffer. */ int toWrite, /* How many bytes to write? */ int *errorCodePtr) /* Where to store error code. */ @@ -566,7 +596,7 @@ TcpOutputProc( } written = send(statePtr->fds.fd, buf, (size_t) toWrite, 0); - if (written > -1) { + if (written >= 0) { return written; } *errorCodePtr = errno; @@ -593,13 +623,12 @@ TcpOutputProc( static int TcpCloseProc( - ClientData instanceData, /* The socket to close. */ - Tcl_Interp *dummy) /* For error reporting - unused. */ + void *instanceData, /* The socket to close. */ + TCL_UNUSED(Tcl_Interp *)) { TcpState *statePtr = (TcpState *)instanceData; int errorCode = 0; TcpFdList *fds; - (void)dummy; /* * Delete a file handler that may be active for this socket if this is a @@ -655,14 +684,13 @@ TcpCloseProc( static int TcpClose2Proc( - ClientData instanceData, /* The socket to close. */ - Tcl_Interp *dummy, /* For error reporting. */ + void *instanceData, /* The socket to close. */ + TCL_UNUSED(Tcl_Interp *), int flags) /* Flags that indicate which side to close. */ { TcpState *statePtr = (TcpState *)instanceData; int readError = 0; int writeError = 0; - (void)dummy; /* * Shutdown the OS socket handle. @@ -807,7 +835,7 @@ TcpHostPortList( static int TcpGetOptionProc( - ClientData instanceData, /* Socket state. */ + void *instanceData, /* Socket state. */ Tcl_Interp *interp, /* For error reporting - can be NULL. */ const char *optionName, /* Name of the option to retrieve the value * for, or NULL to get all options and their @@ -973,7 +1001,7 @@ TcpGetOptionProc( static void TcpThreadActionProc( - ClientData instanceData, + void *instanceData, int action) { TcpState *statePtr = (TcpState *)instanceData; @@ -1018,7 +1046,7 @@ TcpThreadActionProc( static void WrapNotify( - ClientData clientData, + void *clientData, int mask) { TcpState *statePtr = (TcpState *) clientData; @@ -1047,7 +1075,7 @@ WrapNotify( static void TcpWatchProc( - ClientData instanceData, /* The socket state. */ + void *instanceData, /* The socket state. */ int mask) /* Events of interest; an OR-ed combination of * TCL_READABLE, TCL_WRITABLE and * TCL_EXCEPTION. */ @@ -1120,12 +1148,11 @@ TcpWatchProc( static int TcpGetHandleProc( - ClientData instanceData, /* The socket state. */ - int direction, /* Not used. */ - ClientData *handlePtr) /* Where to store the handle. */ + void *instanceData, /* The socket state. */ + TCL_UNUSED(int) /*direction*/, + void **handlePtr) /* Where to store the handle. */ { TcpState *statePtr = (TcpState *)instanceData; - (void)direction; *handlePtr = INT2PTR(statePtr->fds.fd); return TCL_OK; @@ -1145,13 +1172,9 @@ TcpGetHandleProc( static void TcpAsyncCallback( - ClientData clientData, /* The socket state. */ - int mask) /* Events of interest; an OR-ed combination of - * TCL_READABLE, TCL_WRITABLE and - * TCL_EXCEPTION. */ + void *clientData, /* The socket state. */ + TCL_UNUSED(int) /*mask*/) { - (void)mask; - TcpConnect(NULL, (TcpState *)clientData); } @@ -1194,6 +1217,7 @@ TcpConnect( int async_callback = GOT_BITS(statePtr->flags, TCP_ASYNC_PENDING); int ret = -1, error = EHOSTUNREACH; int async = GOT_BITS(statePtr->flags, TCP_ASYNC_CONNECT); + static const int reuseaddr = 1; if (async_callback) { goto reenter; @@ -1204,7 +1228,6 @@ TcpConnect( for (statePtr->myaddr = statePtr->myaddrlist; statePtr->myaddr != NULL; statePtr->myaddr = statePtr->myaddr->ai_next) { - int reuseaddr = 1; /* * No need to try combinations of local and remote addresses of @@ -1458,7 +1481,7 @@ Tcl_OpenTcpClient( Tcl_Channel Tcl_MakeTcpClientChannel( - ClientData sock) /* The socket to wrap up into a channel. */ + void *sock) /* The socket to wrap up into a channel. */ { return (Tcl_Channel) TclpMakeTcpClientChannelMode(sock, TCL_READABLE | TCL_WRITABLE); @@ -1510,7 +1533,7 @@ TclpMakeTcpClientChannelMode( /* *---------------------------------------------------------------------- * - * Tcl_OpenTcpServer -- + * Tcl_OpenTcpServerEx -- * * Opens a TCP server socket and creates a channel around it. * @@ -1525,16 +1548,17 @@ TclpMakeTcpClientChannelMode( */ Tcl_Channel -Tcl_OpenTcpServer( +Tcl_OpenTcpServerEx( Tcl_Interp *interp, /* For error reporting - may be NULL. */ - int port, /* Port number to open. */ + const char *service, /* Port number to open. */ const char *myHost, /* Name of local host. */ + unsigned int flags, /* Flags. */ Tcl_TcpAcceptProc *acceptProc, /* Callback for accepting connections from new * clients. */ - ClientData acceptProcData) /* Data for the callback. */ + void *acceptProcData) /* Data for the callback. */ { - int status = 0, sock = -1, reuseaddr = 1, chosenport = 0; + int status = 0, sock = -1, optvalue, port, chosenport; struct addrinfo *addrlist = NULL, *addrPtr; /* socket address */ TcpState *statePtr = NULL; char channelName[SOCK_CHAN_LENGTH]; @@ -1549,7 +1573,45 @@ Tcl_OpenTcpServer( enum { LOOKUP, SOCKET, BIND, LISTEN } howfar = LOOKUP; int my_errno = 0; - if (!TclCreateSocketAddress(interp, &addrlist, myHost, port, 1, &errorMsg)) { + /* + * If we were called with port 0 to listen on a random port number, we + * copy the port number from the first member of the addrinfo list to all + * subsequent members, so that IPv4 and IPv6 listen on the same port. This + * might fail to bind() with EADDRINUSE if a port is free on the first + * address family in the list but already used on the other. In this case + * we revert everything we've done so far and start from scratch hoping + * that next time we'll find a port number that is usable on all address + * families. We try this at most MAXRETRY times to avoid an endless loop + * if all ports are taken. + */ + + int retry = 0; +#define MAXRETRY 10 + + repeat: + if (retry > 0) { + if (statePtr != NULL) { + TcpCloseProc(statePtr, NULL); + statePtr = NULL; + } + if (addrlist != NULL) { + freeaddrinfo(addrlist); + addrlist = NULL; + } + if (retry >= MAXRETRY) { + goto error; + } + } + retry++; + chosenport = 0; + + if (TclSockGetPort(interp, service, "tcp", &port) != TCL_OK) { + errorMsg = "invalid port number"; + goto error; + } + + if (!TclCreateSocketAddress(interp, &addrlist, myHost, port, 1, + &errorMsg)) { my_errno = errno; goto error; } @@ -1579,12 +1641,30 @@ Tcl_OpenTcpServer( TclSockMinimumBuffers(INT2PTR(sock), SOCKET_BUFSIZE); /* - * Set up to reuse server addresses automatically and bind to the - * specified port. + * Set up to reuse server addresses and/or ports if requested. */ - (void) setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, - (char *) &reuseaddr, sizeof(reuseaddr)); + if (GOT_BITS(flags, TCL_TCPSERVER_REUSEADDR)) { + optvalue = 1; + (void) setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, + (char *) &optvalue, sizeof(optvalue)); + } + + if (GOT_BITS(flags, TCL_TCPSERVER_REUSEPORT)) { +#ifndef SO_REUSEPORT + /* + * If the platform doesn't support the SO_REUSEPORT flag we can't + * do much beside erroring out. + */ + + errorMsg = "SO_REUSEPORT isn't supported by this platform"; + goto error; +#else + optvalue = 1; + (void) setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, + (char *) &optvalue, sizeof(optvalue)); +#endif + } /* * Make sure we use the same port number when opening two server @@ -1620,6 +1700,9 @@ Tcl_OpenTcpServer( } close(sock); sock = -1; + if (port == 0 && errno == EADDRINUSE) { + goto repeat; + } continue; } if (port == 0 && chosenport == 0) { @@ -1643,6 +1726,9 @@ Tcl_OpenTcpServer( } close(sock); sock = -1; + if (port == 0 && errno == EADDRINUSE) { + goto repeat; + } continue; } if (statePtr == NULL) { @@ -1717,8 +1803,8 @@ Tcl_OpenTcpServer( static void TcpAccept( - ClientData data, /* Callback token. */ - int mask) /* Not used. */ + void *data, /* Callback token. */ + TCL_UNUSED(int) /*mask*/) { TcpFdList *fds = (TcpFdList *)data; /* Client data of server socket. */ int newsock; /* The new client socket */ @@ -1727,7 +1813,6 @@ TcpAccept( socklen_t len; /* For accept interface */ char channelName[SOCK_CHAN_LENGTH]; char host[NI_MAXHOST], port[NI_MAXSERV]; - (void)mask; len = sizeof(addr); newsock = accept(fds->fd, &addr.sa, &len); |