summaryrefslogtreecommitdiffstats
path: root/unix/tclUnixSock.c
diff options
context:
space:
mode:
Diffstat (limited to 'unix/tclUnixSock.c')
-rw-r--r--unix/tclUnixSock.c217
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);