summaryrefslogtreecommitdiffstats
path: root/unix/tclUnixSock.c
diff options
context:
space:
mode:
Diffstat (limited to 'unix/tclUnixSock.c')
-rw-r--r--unix/tclUnixSock.c747
1 files changed, 385 insertions, 362 deletions
diff --git a/unix/tclUnixSock.c b/unix/tclUnixSock.c
index f5b9be4..ba3d0d0 100644
--- a/unix/tclUnixSock.c
+++ b/unix/tclUnixSock.c
@@ -5,10 +5,10 @@
*
* Copyright (c) 1995 Sun Microsystems, Inc.
*
- * See the file "license.terms" for information on usage and redistribution
- * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+ * See the file "license.terms" for information on usage and redistribution of
+ * this file, and for a DISCLAIMER OF ALL WARRANTIES.
*
- * RCS: @(#) $Id: tclUnixSock.c,v 1.26 2010/06/21 11:23:23 nijtmans Exp $
+ * RCS: @(#) $Id: tclUnixSock.c,v 1.27 2010/09/28 15:13:55 rmax Exp $
*/
#include "tclInt.h"
@@ -23,18 +23,38 @@
#define CLEAR_BITS(var, bits) ((var) &= ~(bits))
/*
+ * This is needed to comply with the strict aliasing rules of GCC, but it also
+ * simplifies casting between the different sockaddr types.
+ */
+
+typedef union {
+ struct sockaddr sa;
+ struct sockaddr_in sa4;
+ struct sockaddr_in6 sa6;
+ struct sockaddr_storage sas;
+} address;
+
+/*
* This structure describes per-instance state of a tcp based channel.
*/
-typedef struct TcpState {
+typedef struct TcpState TcpState;
+
+typedef struct TcpFdList {
+ TcpState *statePtr;
+ int fd;
+ struct TcpFdList *next;
+} TcpFdList;
+
+struct TcpState {
Tcl_Channel channel; /* Channel associated with this file. */
- int fd; /* The socket itself. */
+ TcpFdList *fds; /* The file descriptors of the sockets. */
int flags; /* ORed combination of the bitfields defined
* below. */
Tcl_TcpAcceptProc *acceptProc;
/* Proc to call on accept. */
ClientData acceptProcData; /* The data for the accept proc. */
-} TcpState;
+};
/*
* These bits may be ORed together into the "flags" field of a TcpState
@@ -71,19 +91,15 @@ typedef struct TcpState {
* Static routines for this file:
*/
-static TcpState * CreateSocket(Tcl_Interp *interp, int port,
- const char *host, int server, const char *myaddr,
+static TcpState * CreateClientSocket(Tcl_Interp *interp, int port,
+ const char *host, const char *myaddr,
int myport, int async);
-static int CreateSocketAddress(struct sockaddr_in *sockaddrPtr,
- const char *host, int port, int willBind,
- const char **errorMsgPtr);
static void TcpAccept(ClientData data, int mask);
static int TcpBlockModeProc(ClientData data, int mode);
static int TcpCloseProc(ClientData instanceData,
Tcl_Interp *interp);
static int TcpClose2Proc(ClientData instanceData,
- Tcl_Interp *interp,
- int flags);
+ Tcl_Interp *interp, int flags);
static int TcpGetHandleProc(ClientData instanceData,
int direction, ClientData *handlePtr);
static int TcpGetOptionProc(ClientData instanceData,
@@ -95,6 +111,7 @@ static int TcpOutputProc(ClientData instanceData,
const char *buf, int toWrite, int *errorCode);
static void TcpWatchProc(ClientData instanceData, int mask);
static int WaitForConnect(TcpState *statePtr, int *errorCodePtr);
+
/*
* This structure describes the channel type structure for TCP socket
* based IO:
@@ -120,7 +137,6 @@ static const Tcl_ChannelType tcpChannelType = {
NULL /* truncate proc. */
};
-
/*
* The following variable holds the network name of this host.
*/
@@ -128,7 +144,6 @@ static const Tcl_ChannelType tcpChannelType = {
static TclInitProcessGlobalValueProc InitializeHostName;
static ProcessGlobalValue hostName =
{0, 0, NULL, NULL, InitializeHostName, NULL, NULL};
-
/*
*----------------------------------------------------------------------
@@ -320,7 +335,7 @@ TcpBlockModeProc(
} else {
SET_BITS(statePtr->flags, TCP_ASYNC_SOCKET);
}
- if (TclUnixSetBlockingMode(statePtr->fd, mode) < 0) {
+ if (TclUnixSetBlockingMode(statePtr->fds->fd, mode) < 0) {
return errno;
}
return 0;
@@ -362,7 +377,7 @@ WaitForConnect(
timeOut = -1;
}
errno = 0;
- state = TclUnixWaitForFile(statePtr->fd,
+ state = TclUnixWaitForFile(statePtr->fds->fd,
TCL_WRITABLE | TCL_EXCEPTION, timeOut);
if (state & TCL_EXCEPTION) {
return -1;
@@ -415,7 +430,7 @@ TcpInputProc(
if (WaitForConnect(statePtr, errorCodePtr) != 0) {
return -1;
}
- bytesRead = recv(statePtr->fd, buf, (size_t) bufSize, 0);
+ bytesRead = recv(statePtr->fds->fd, buf, (size_t) bufSize, 0);
if (bytesRead > -1) {
return bytesRead;
}
@@ -465,7 +480,7 @@ TcpOutputProc(
if (WaitForConnect(statePtr, errorCodePtr) != 0) {
return -1;
}
- written = send(statePtr->fd, buf, (size_t) toWrite, 0);
+ written = send(statePtr->fds->fd, buf, (size_t) toWrite, 0);
if (written > -1) {
return written;
}
@@ -499,6 +514,7 @@ TcpCloseProc(
{
TcpState *statePtr = (TcpState *) instanceData;
int errorCode = 0;
+ TcpFdList *fds;
/*
* Delete a file handler that may be active for this socket if this is a
@@ -507,14 +523,16 @@ TcpCloseProc(
* handlers are already deleted in the generic IO channel closing code
* that called this function, so we do not have to delete them here.
*/
-
- Tcl_DeleteFileHandler(statePtr->fd);
-
- if (close(statePtr->fd) < 0) {
- errorCode = errno;
+
+ for (fds = statePtr->fds; fds != NULL; fds = statePtr->fds) {
+ statePtr->fds = fds->next;
+ Tcl_DeleteFileHandler(fds->fd);
+ if (close(fds->fd) < 0) {
+ errorCode = errno;
+ }
+ ckfree((char *) fds);
}
ckfree((char *) statePtr);
-
return errorCode;
}
@@ -548,21 +566,22 @@ TcpClose2Proc(
/*
* Shutdown the OS socket handle.
*/
- switch(flags)
- {
- case TCL_CLOSE_READ:
- sd=SHUT_RD;
- break;
- case TCL_CLOSE_WRITE:
- sd=SHUT_WR;
- break;
- default:
- if (interp) {
- Tcl_AppendResult(interp, "Socket close2proc called bidirectionally", NULL);
- }
- return TCL_ERROR;
- }
- if (shutdown(statePtr->fd,sd)<0) {
+
+ switch(flags) {
+ case TCL_CLOSE_READ:
+ sd = SHUT_RD;
+ break;
+ case TCL_CLOSE_WRITE:
+ sd = SHUT_WR;
+ break;
+ default:
+ if (interp) {
+ Tcl_AppendResult(interp,
+ "Socket close2proc called bidirectionally", NULL);
+ }
+ return TCL_ERROR;
+ }
+ if (shutdown(statePtr->fds->fd,sd) < 0) {
errorCode = errno;
}
@@ -601,12 +620,9 @@ TcpGetOptionProc(
* initialized by caller. */
{
TcpState *statePtr = (TcpState *) instanceData;
- struct sockaddr_in sockname;
- struct sockaddr_in peername;
- struct hostent *hostEntPtr;
- socklen_t size = sizeof(struct sockaddr_in);
+
+ char host[NI_MAXHOST], port[NI_MAXSERV];
size_t len = 0;
- char buf[TCL_INTEGER_SPACE];
if (optionName != NULL) {
len = strlen(optionName);
@@ -617,7 +633,7 @@ TcpGetOptionProc(
socklen_t optlen = sizeof(int);
int err, ret;
- ret = getsockopt(statePtr->fd, SOL_SOCKET, SO_ERROR,
+ ret = getsockopt(statePtr->fds->fd, SOL_SOCKET, SO_ERROR,
(char *)&err, &optlen);
if (ret < 0) {
err = errno;
@@ -631,27 +647,22 @@ TcpGetOptionProc(
if ((len == 0) ||
((len > 1) && (optionName[1] == 'p') &&
(strncmp(optionName, "-peername", len) == 0))) {
- if (getpeername(statePtr->fd, (struct sockaddr *) &peername,
- &size) >= 0) {
+ address peername;
+ socklen_t size = sizeof(peername);
+
+ if (getpeername(statePtr->fds->fd, &peername.sa, &size) >= 0) {
if (len == 0) {
Tcl_DStringAppendElement(dsPtr, "-peername");
Tcl_DStringStartSublist(dsPtr);
}
- Tcl_DStringAppendElement(dsPtr, inet_ntoa(peername.sin_addr));
- hostEntPtr = TclpGetHostByAddr( /* INTL: Native. */
- (char *) &peername.sin_addr,
- sizeof(peername.sin_addr), AF_INET);
- if (hostEntPtr != NULL) {
- Tcl_DString ds;
-
- Tcl_ExternalToUtfDString(NULL, hostEntPtr->h_name, -1, &ds);
- Tcl_DStringAppendElement(dsPtr, Tcl_DStringValue(&ds));
- Tcl_DStringFree(&ds);
- } else {
- Tcl_DStringAppendElement(dsPtr, inet_ntoa(peername.sin_addr));
- }
- TclFormatInt(buf, ntohs(peername.sin_port));
- Tcl_DStringAppendElement(dsPtr, buf);
+
+ getnameinfo(&peername.sa, size, host, sizeof(host), NULL, 0,
+ NI_NUMERICHOST);
+ Tcl_DStringAppendElement(dsPtr, host);
+ getnameinfo(&peername.sa, size, host, sizeof(host), port,
+ sizeof(port), NI_NUMERICSERV);
+ Tcl_DStringAppendElement(dsPtr, host);
+ Tcl_DStringAppendElement(dsPtr, port);
if (len == 0) {
Tcl_DStringEndSublist(dsPtr);
} else {
@@ -678,46 +689,65 @@ TcpGetOptionProc(
if ((len == 0) ||
((len > 1) && (optionName[1] == 's') &&
(strncmp(optionName, "-sockname", len) == 0))) {
- if (getsockname(statePtr->fd, (struct sockaddr *) &sockname,
- &size) >= 0) {
- if (len == 0) {
- Tcl_DStringAppendElement(dsPtr, "-sockname");
- Tcl_DStringStartSublist(dsPtr);
+ TcpFdList *fds;
+ address sockname;
+ socklen_t size;
+ int found = 0;
+
+ if (len == 0) {
+ Tcl_DStringAppendElement(dsPtr, "-sockname");
+ Tcl_DStringStartSublist(dsPtr);
+ }
+ for (fds = statePtr->fds; fds != NULL; fds = fds->next) {
+ size = sizeof(sockname);
+ if (getsockname(fds->fd, &(sockname.sa), &size) >= 0) {
+ int flags;
+
+ found = 1;
+ getnameinfo(&sockname.sa, size, host, sizeof(host), NULL, 0,
+ NI_NUMERICHOST);
+ Tcl_DStringAppendElement(dsPtr, host);
+
+ /*
+ * We don't want to resolve INADDR_ANY and sin6addr_any; they
+ * can sometimes cause problems (and never have a name).
+ */
+
+ flags = NI_NUMERICSERV;
+ if (sockname.sa.sa_family == AF_INET) {
+ if (sockname.sa4.sin_addr.s_addr == INADDR_ANY) {
+ flags |= NI_NUMERICHOST;
+ }
+#ifndef NEED_FAKE_RFC2553
+ } else if (sockname.sa.sa_family == AF_INET6) {
+ if ((IN6_ARE_ADDR_EQUAL(&sockname.sa6.sin6_addr,
+ &in6addr_any))
+ || (IN6_IS_ADDR_V4MAPPED(&sockname.sa6.sin6_addr) &&
+ sockname.sa6.sin6_addr.s6_addr[12] == 0 &&
+ sockname.sa6.sin6_addr.s6_addr[13] == 0 &&
+ sockname.sa6.sin6_addr.s6_addr[14] == 0 &&
+ sockname.sa6.sin6_addr.s6_addr[15] == 0)) {
+ flags |= NI_NUMERICHOST;
+ }
+#endif
+ }
+ getnameinfo(&sockname.sa, size, host, sizeof(host), port,
+ sizeof(port), flags);
+ Tcl_DStringAppendElement(dsPtr, host);
+ Tcl_DStringAppendElement(dsPtr, port);
}
- Tcl_DStringAppendElement(dsPtr, inet_ntoa(sockname.sin_addr));
- if (sockname.sin_addr.s_addr == INADDR_ANY) {
- /*
- * We don't want to resolve INADDR_ANY; it can sometimes cause
- * problems (and never has a name).
- */
-
- hostEntPtr = NULL;
+ }
+ if (found) {
+ if (len == 0) {
+ Tcl_DStringEndSublist(dsPtr);
} else {
- hostEntPtr = TclpGetHostByAddr( /* INTL: Native. */
- (char *) &sockname.sin_addr,
- sizeof(sockname.sin_addr), AF_INET);
+ return TCL_OK;
+ }
+ } else {
+ if (interp) {
+ Tcl_AppendResult(interp, "can't get sockname: ",
+ Tcl_PosixError(interp), NULL);
}
- if (hostEntPtr != NULL) {
- Tcl_DString ds;
-
- Tcl_ExternalToUtfDString(NULL, hostEntPtr->h_name, -1, &ds);
- Tcl_DStringAppendElement(dsPtr, Tcl_DStringValue(&ds));
- Tcl_DStringFree(&ds);
- } else {
- Tcl_DStringAppendElement(dsPtr, inet_ntoa(sockname.sin_addr));
- }
- TclFormatInt(buf, ntohs(sockname.sin_port));
- Tcl_DStringAppendElement(dsPtr, buf);
- if (len == 0) {
- Tcl_DStringEndSublist(dsPtr);
- } else {
- return TCL_OK;
- }
- } else {
- if (interp) {
- Tcl_AppendResult(interp, "can't get sockname: ",
- Tcl_PosixError(interp), NULL);
- }
return TCL_ERROR;
}
}
@@ -762,12 +792,16 @@ TcpWatchProc(
*/
if (!statePtr->acceptProc) {
- if (mask) {
- Tcl_CreateFileHandler(statePtr->fd, mask,
- (Tcl_FileProc *) Tcl_NotifyChannel,
- (ClientData) statePtr->channel);
- } else {
- Tcl_DeleteFileHandler(statePtr->fd);
+ TcpFdList *fds;
+
+ for (fds = statePtr->fds; fds != NULL; fds = fds->next) {
+ if (mask) {
+ Tcl_CreateFileHandler(fds->fd, mask,
+ (Tcl_FileProc *) Tcl_NotifyChannel,
+ (ClientData) statePtr->channel);
+ } else {
+ Tcl_DeleteFileHandler(fds->fd);
+ }
}
}
}
@@ -799,7 +833,7 @@ TcpGetHandleProc(
{
TcpState *statePtr = (TcpState *) instanceData;
- *handlePtr = INT2PTR(statePtr->fd);
+ *handlePtr = INT2PTR(statePtr->fds->fd);
return TCL_OK;
}
@@ -822,111 +856,126 @@ TcpGetHandleProc(
*/
static TcpState *
-CreateSocket(
+CreateClientSocket(
Tcl_Interp *interp, /* For error reporting; can be NULL. */
int port, /* Port number to open. */
const char *host, /* Name of host on which to open port. NULL
* implies INADDR_ANY */
- int server, /* 1 if socket should be a server socket, else
- * 0 for a client socket. */
const char *myaddr, /* Optional client-side address */
int myport, /* Optional client-side port */
int async) /* If nonzero and creating a client socket,
* attempt to do an async connect. Otherwise
* do a synchronous connect or bind. */
{
- int status = 0, sock = -1;
- struct sockaddr_in sockaddr; /* socket address */
- struct sockaddr_in mysockaddr; /* Socket address for client */
+ int status = 0, connected = 0, sock = -1;
+ struct addrinfo *addrlist = NULL, *addrPtr;
+ /* Socket address */
+ struct addrinfo *myaddrlist = NULL, *myaddrPtr;
+ /* Socket address for client */
TcpState *statePtr;
const char *errorMsg = NULL;
- if (!CreateSocketAddress(&sockaddr, host, port, 0, &errorMsg)) {
- goto error;
- }
- if ((myaddr != NULL || myport != 0) &&
- !CreateSocketAddress(&mysockaddr, myaddr, myport, 1, &errorMsg)) {
+ if (!TclCreateSocketAddress(interp, &addrlist, host, port, 0, &errorMsg)) {
goto error;
}
-
- sock = socket(AF_INET, SOCK_STREAM, 0);
- if (sock < 0) {
+ if (!TclCreateSocketAddress(interp, &myaddrlist, myaddr, myport, 1, &errorMsg)) {
goto error;
}
- /*
- * Set the close-on-exec flag so that the socket will not get inherited by
- * child processes.
- */
-
- fcntl(sock, F_SETFD, FD_CLOEXEC);
+ for (myaddrPtr = myaddrlist; myaddrPtr != NULL;
+ myaddrPtr = myaddrPtr->ai_next) {
+ for (addrPtr = addrlist; addrPtr != NULL;
+ addrPtr = addrPtr->ai_next) {
+ int reuseaddr;
- /*
- * Set kernel space buffering
- */
-
- TclSockMinimumBuffers(sock, SOCKET_BUFSIZE);
-
- status = 0;
- if (server) {
- /*
- * Set up to reuse server addresses automatically and bind to the
- * specified port.
- */
+ /*
+ * No need to try combinations of local and remote addresses of
+ * different families.
+ */
- int reuseaddr = 1;
- (void) setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
- (char *) &reuseaddr, sizeof(reuseaddr));
- status = bind(sock, (struct sockaddr *) &sockaddr,
- sizeof(struct sockaddr));
- if (status != -1) {
- status = listen(sock, SOMAXCONN);
- }
- } else {
- if (myaddr != NULL || myport != 0) {
- int reuseaddr = 1;
- (void) setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
- (char *) &reuseaddr, sizeof(reuseaddr));
- status = bind(sock, (struct sockaddr *) &mysockaddr,
- sizeof(struct sockaddr));
- if (status < 0) {
- goto error;
+ if (myaddrPtr->ai_family != addrPtr->ai_family) {
+ continue;
}
- }
- /*
- * Attempt to connect. The connect may fail at present with an
- * EINPROGRESS but at a later time it will complete. The caller will
- * set up a file handler on the socket if she is interested in being
- * informed when the connect completes.
- */
+ /*
+ * Attempt to connect. The connect may fail at present with an
+ * EINPROGRESS but at a later time it will complete. The caller
+ * will set up a file handler on the socket if she is interested
+ * in being informed when the connect completes.
+ */
+
+ sock = socket(myaddrPtr->ai_family, SOCK_STREAM, 0);
+ if (sock < 0) {
+ continue;
+ }
- if (async) {
- status = TclUnixSetBlockingMode(sock, TCL_MODE_NONBLOCKING);
- if (status < 0) {
- goto error;
+ /*
+ * Set the close-on-exec flag so that the socket will not get
+ * inherited by child processes.
+ */
+
+ fcntl(sock, F_SETFD, FD_CLOEXEC);
+
+ /*
+ * Set kernel space buffering
+ */
+
+ TclSockMinimumBuffers(sock, SOCKET_BUFSIZE);
+
+ if (async) {
+ status = TclUnixSetBlockingMode(sock, TCL_MODE_NONBLOCKING);
+ if (status < 0) {
+ goto looperror;
+ }
}
- }
- status = connect(sock, (struct sockaddr *) &sockaddr,
- sizeof(sockaddr));
- if (status < 0) {
- if (errno == EINPROGRESS) {
+ reuseaddr = 1;
+ (void) setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
+ (char *) &reuseaddr, sizeof(reuseaddr));
+ status = bind(sock, myaddrPtr->ai_addr, myaddrPtr->ai_addrlen);
+ if (status < 0) {
+ goto looperror;
+ }
+
+ status = connect(sock, addrPtr->ai_addr, addrPtr->ai_addrlen);
+ if (status < 0 && errno == EINPROGRESS) {
status = 0;
- } else {
- goto error;
}
+ if (status == 0) {
+ connected = 1;
+ break;
+ }
+ looperror:
+ if (sock != -1) {
+ close(sock);
+ sock = -1;
+ }
+ }
+ if (connected) {
+ break;
}
- if (async) {
- /*
- * Restore blocking mode.
- */
- status = TclUnixSetBlockingMode(sock, TCL_MODE_BLOCKING);
+ status = -1;
+ if (sock >= 0) {
+ close(sock);
+ sock = -1;
}
}
+ if (async) {
+ /*
+ * Restore blocking mode.
+ */
+
+ status = TclUnixSetBlockingMode(sock, TCL_MODE_BLOCKING);
+ }
if (status < 0) {
-error:
+ error:
+ if (addrlist != NULL) {
+ freeaddrinfo(addrlist);
+ }
+ if (myaddrlist != NULL) {
+ freeaddrinfo(myaddrlist);
+ }
if (interp != NULL) {
Tcl_AppendResult(interp, "couldn't open socket: ",
Tcl_PosixError(interp), NULL);
@@ -940,13 +989,22 @@ error:
return NULL;
}
+ if (addrlist == NULL) {
+ freeaddrinfo(addrlist);
+ }
+ if (myaddrlist == NULL) {
+ freeaddrinfo(myaddrlist);
+ }
+
/*
* Allocate a new TcpState for this socket.
*/
statePtr = (TcpState *) ckalloc((unsigned) sizeof(TcpState));
statePtr->flags = async ? TCP_ASYNC_CONNECT : 0;
- statePtr->fd = sock;
+ statePtr->fds = (TcpFdList *) ckalloc((unsigned) sizeof(TcpFdList));
+ memset(statePtr->fds, (int) 0, sizeof(TcpFdList));
+ statePtr->fds->fd = sock;
return statePtr;
}
@@ -954,152 +1012,6 @@ error:
/*
*----------------------------------------------------------------------
*
- * CreateSocketAddress --
- *
- * This function initializes a sockaddr structure for a host and port.
- *
- * Results:
- * 1 if the host was valid, 0 if the host could not be converted to an IP
- * address.
- *
- * Side effects:
- * Fills in the *sockaddrPtr structure.
- *
- *----------------------------------------------------------------------
- */
-
-static int
-CreateSocketAddress(
- struct sockaddr_in *sockaddrPtr, /* Socket address */
- const char *host, /* Host. NULL implies INADDR_ANY */
- int port, /* Port number */
- int willBind, /* Is this an address to bind() to or
- * to connect() to? */
- const char **errorMsgPtr) /* Place to store the error message
- * detail, if available. */
-{
-#ifdef HAVE_GETADDRINFO
- struct addrinfo hints, *resPtr = NULL;
- char *native;
- Tcl_DString ds;
- int result;
-
- if (host == NULL) {
- sockaddrPtr->sin_family = AF_INET;
- sockaddrPtr->sin_addr.s_addr = INADDR_ANY;
- addPort:
- sockaddrPtr->sin_port = htons((unsigned short) (port & 0xFFFF));
- return 1;
- }
-
- (void) memset(&hints, 0, sizeof(struct addrinfo));
- hints.ai_family = AF_INET;
- hints.ai_socktype = SOCK_STREAM;
- if (willBind) {
- hints.ai_flags |= AI_PASSIVE;
- }
-
- /*
- * Note that getaddrinfo() *is* thread-safe. If a platform doesn't get
- * that right, it shouldn't use this part of the code.
- */
-
- native = Tcl_UtfToExternalDString(NULL, host, -1, &ds);
- result = getaddrinfo(native, NULL, &hints, &resPtr);
- Tcl_DStringFree(&ds);
- if (result == 0) {
- memcpy(sockaddrPtr, resPtr->ai_addr, sizeof(struct sockaddr_in));
- freeaddrinfo(resPtr);
- goto addPort;
- }
-
- /*
- * Ought to use gai_strerror() here...
- */
-
- switch (result) {
- case EAI_NONAME:
- case EAI_SERVICE:
-#if defined(EAI_ADDRFAMILY) && EAI_ADDRFAMILY != EAI_NONAME
- case EAI_ADDRFAMILY:
-#endif
-#if defined(EAI_NODATA) && EAI_NODATA != EAI_NONAME
- case EAI_NODATA:
-#endif
- *errorMsgPtr = gai_strerror(result);
- errno = EHOSTUNREACH;
- return 0;
- case EAI_SYSTEM:
- return 0;
- default:
- *errorMsgPtr = gai_strerror(result);
- errno = ENXIO;
- return 0;
- }
-#else /* !HAVE_GETADDRINFO */
- struct in_addr addr; /* For 64/32 bit madness */
-
- (void) memset(sockaddrPtr, '\0', sizeof(struct sockaddr_in));
- sockaddrPtr->sin_family = AF_INET;
- sockaddrPtr->sin_port = htons((unsigned short) (port & 0xFFFF));
- if (host == NULL) {
- addr.s_addr = INADDR_ANY;
- } else {
- struct hostent *hostent; /* Host database entry */
- Tcl_DString ds;
- const char *native;
-
- if (host == NULL) {
- native = NULL;
- } else {
- native = Tcl_UtfToExternalDString(NULL, host, -1, &ds);
- }
- addr.s_addr = inet_addr(native); /* INTL: Native. */
-
- /*
- * This is 0xFFFFFFFF to ensure that it compares as a 32bit -1 on
- * either 32 or 64 bits systems.
- */
-
- if (addr.s_addr == 0xFFFFFFFF) {
- hostent = TclpGetHostByName(native); /* INTL: Native. */
- if (hostent != NULL) {
- memcpy(&addr, hostent->h_addr_list[0],
- (size_t) hostent->h_length);
- } else {
-#ifdef EHOSTUNREACH
- errno = EHOSTUNREACH;
-#else /* !EHOSTUNREACH */
-#ifdef ENXIO
- errno = ENXIO;
-#endif /* ENXIO */
-#endif /* EHOSTUNREACH */
- if (native != NULL) {
- Tcl_DStringFree(&ds);
- }
- return 0; /* Error. */
- }
- }
- if (native != NULL) {
- Tcl_DStringFree(&ds);
- }
- }
-
- /*
- * NOTE: On 64 bit machines the assignment below is rumored to not do the
- * right thing. Please report errors related to this if you observe
- * incorrect behavior on 64 bit machines such as DEC Alphas. Should we
- * modify this code to do an explicit memcpy?
- */
-
- sockaddrPtr->sin_addr.s_addr = addr.s_addr;
- return 1; /* Success. */
-#endif /* HAVE_GETADDRINFO */
-}
-
-/*
- *----------------------------------------------------------------------
- *
* Tcl_OpenTcpClient --
*
* Opens a TCP client socket and creates a channel around it.
@@ -1132,7 +1044,7 @@ Tcl_OpenTcpClient(
* Create a new client socket and wrap it in a channel.
*/
- statePtr = CreateSocket(interp, port, host, 0, myaddr, myport, async);
+ statePtr = CreateClientSocket(interp, port, host, myaddr, myport, async);
if (statePtr == NULL) {
return NULL;
}
@@ -1140,7 +1052,7 @@ Tcl_OpenTcpClient(
statePtr->acceptProc = NULL;
statePtr->acceptProcData = NULL;
- sprintf(channelName, "sock%d", statePtr->fd);
+ sprintf(channelName, "sock%d", statePtr->fds->fd);
statePtr->channel = Tcl_CreateChannel(&tcpChannelType, channelName,
statePtr, (TCL_READABLE | TCL_WRITABLE));
@@ -1202,12 +1114,14 @@ TclpMakeTcpClientChannelMode(
char channelName[16 + TCL_INTEGER_SPACE];
statePtr = (TcpState *) ckalloc((unsigned) sizeof(TcpState));
- statePtr->fd = PTR2INT(sock);
+ statePtr->fds = (TcpFdList *) ckalloc((unsigned) sizeof(TcpFdList));
+ memset(statePtr->fds, (int) 0, sizeof(TcpFdList));
+ statePtr->fds->fd = PTR2INT(sock);
statePtr->flags = 0;
statePtr->acceptProc = NULL;
statePtr->acceptProcData = NULL;
- sprintf(channelName, "sock%d", statePtr->fd);
+ sprintf(channelName, "sock%d", statePtr->fds->fd);
statePtr->channel = Tcl_CreateChannel(&tcpChannelType, channelName,
statePtr, mode);
@@ -1246,32 +1160,137 @@ Tcl_OpenTcpServer(
* clients. */
ClientData acceptProcData) /* Data for the callback. */
{
- TcpState *statePtr;
+ int status = 0, sock = -1, reuseaddr = 1, chosenport = 0;
+ struct addrinfo *addrlist = NULL, *addrPtr; /* socket address */
+ TcpState *statePtr = NULL;
char channelName[16 + TCL_INTEGER_SPACE];
+ const char *errorMsg = NULL;
+ TcpFdList *fds = NULL, *newfds;
- /*
- * Create a new client socket and wrap it in a channel.
- */
-
- statePtr = CreateSocket(interp, port, myHost, 1, NULL, 0, 0);
- if (statePtr == NULL) {
- return NULL;
+ if (!TclCreateSocketAddress(interp, &addrlist, myHost, port, 1, &errorMsg)) {
+ goto error;
}
- statePtr->acceptProc = acceptProc;
- statePtr->acceptProcData = acceptProcData;
+ for (addrPtr = addrlist; addrPtr != NULL; addrPtr = addrPtr->ai_next) {
+ sock = socket(addrPtr->ai_family, SOCK_STREAM, 0);
+ if (sock == -1) {
+ continue;
+ }
+
+ /*
+ * Set the close-on-exec flag so that the socket will not get
+ * inherited by child processes.
+ */
+
+ fcntl(sock, F_SETFD, FD_CLOEXEC);
+
+ /*
+ * Set kernel space buffering
+ */
+
+ TclSockMinimumBuffers(sock, SOCKET_BUFSIZE);
+
+ /*
+ * Set up to reuse server addresses automatically and bind to the
+ * specified port.
+ */
+
+ (void) setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
+ (char *) &reuseaddr, sizeof(reuseaddr));
+
+ /*
+ * Make sure we use the same port when opening two server sockets for
+ * IPv4 and IPv6.
+ *
+ * As sockaddr_in6 uses the same offset and size for the port member
+ * as sockaddr_in, we can handle both through the IPv4 API.
+ */
+
+ if (port == 0 && chosenport != 0) {
+ ((struct sockaddr_in *) addrPtr->ai_addr)->sin_port =
+ htons(chosenport);
+ }
- /*
- * Set up the callback mechanism for accepting connections from new
- * clients.
- */
+#ifdef IPV6_V6ONLY
+ /* Missing on: Solaris 2.8 */
+ if (addrPtr->ai_family == AF_INET6) {
+ int v6only = 1;
- Tcl_CreateFileHandler(statePtr->fd, TCL_READABLE, TcpAccept,
- statePtr);
- sprintf(channelName, "sock%d", statePtr->fd);
- statePtr->channel = Tcl_CreateChannel(&tcpChannelType, channelName,
- statePtr, 0);
- return statePtr->channel;
+ (void) setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY,
+ &v6only, sizeof(v6only));
+ }
+#endif
+
+ status = bind(sock, addrPtr->ai_addr, addrPtr->ai_addrlen);
+ if (status == -1) {
+ close(sock);
+ continue;
+ }
+ if (port == 0 && chosenport == 0) {
+ address sockname;
+ socklen_t namelen = sizeof(sockname);
+
+ /*
+ * Synchronize port numbers when binding to port 0 of multiple
+ * addresses.
+ */
+
+ if (getsockname(sock, &sockname.sa, &namelen) >= 0) {
+ chosenport = ntohs(sockname.sa4.sin_port);
+ }
+ }
+ status = listen(sock, SOMAXCONN);
+ if (status < 0) {
+ close(sock);
+ continue;
+ }
+ newfds = (TcpFdList *) ckalloc((unsigned) sizeof(TcpFdList));
+ memset(newfds, (int) 0, sizeof(TcpFdList));
+ if (statePtr == NULL) {
+ /*
+ * Allocate a new TcpState for this socket.
+ */
+
+ statePtr = (TcpState *) ckalloc((unsigned) sizeof(TcpState));
+ statePtr->fds = newfds;
+ statePtr->acceptProc = acceptProc;
+ statePtr->acceptProcData = acceptProcData;
+ sprintf(channelName, "sock%d", sock);
+ } else {
+ fds->next = newfds;
+ }
+ newfds->fd = sock;
+ newfds->statePtr = statePtr;
+ fds = newfds;
+
+ /*
+ * Set up the callback mechanism for accepting connections from new
+ * clients.
+ */
+
+ Tcl_CreateFileHandler(sock, TCL_READABLE, TcpAccept, fds);
+ }
+
+ error:
+ if (addrlist != NULL) {
+ freeaddrinfo(addrlist);
+ }
+ if (statePtr != NULL) {
+ statePtr->channel = Tcl_CreateChannel(&tcpChannelType, channelName,
+ statePtr, 0);
+ return statePtr->channel;
+ }
+ if (interp != NULL) {
+ Tcl_AppendResult(interp, "couldn't open socket: ",
+ Tcl_PosixError(interp), NULL);
+ if (errorMsg != NULL) {
+ Tcl_AppendResult(interp, " (", errorMsg, ")", NULL);
+ }
+ }
+ if (sock != -1) {
+ close(sock);
+ }
+ return NULL;
}
/*
@@ -1296,17 +1315,18 @@ TcpAccept(
ClientData data, /* Callback token. */
int mask) /* Not used. */
{
- TcpState *sockState; /* Client data of server socket. */
+ TcpFdList *fds; /* Client data of server socket. */
int newsock; /* The new client socket */
TcpState *newSockState; /* State for new socket. */
- struct sockaddr_in addr; /* The remote address */
+ address addr; /* The remote address */
socklen_t len; /* For accept interface */
char channelName[16 + TCL_INTEGER_SPACE];
+ char host[NI_MAXHOST], port[NI_MAXSERV];
+
+ fds = (TcpFdList *) data;
- sockState = (TcpState *) data;
-
- len = sizeof(struct sockaddr_in);
- newsock = accept(sockState->fd, (struct sockaddr *) &addr, &len);
+ len = sizeof(addr);
+ newsock = accept(fds->fd, &(addr.sa), &len);
if (newsock < 0) {
return;
}
@@ -1321,7 +1341,9 @@ TcpAccept(
newSockState = (TcpState *) ckalloc((unsigned) sizeof(TcpState));
newSockState->flags = 0;
- newSockState->fd = newsock;
+ newSockState->fds = (TcpFdList *) ckalloc(sizeof(TcpFdList));
+ memset(newSockState->fds, (int) 0, sizeof(TcpFdList));
+ newSockState->fds->fd = newsock;
newSockState->acceptProc = NULL;
newSockState->acceptProcData = NULL;
@@ -1332,10 +1354,11 @@ TcpAccept(
Tcl_SetChannelOption(NULL, newSockState->channel, "-translation",
"auto crlf");
- if (sockState->acceptProc != NULL) {
- sockState->acceptProc(sockState->acceptProcData,
- newSockState->channel, inet_ntoa(addr.sin_addr),
- ntohs(addr.sin_port));
+ if (fds->statePtr->acceptProc != NULL) {
+ getnameinfo(&(addr.sa), len, host, sizeof(host), port, sizeof(port),
+ NI_NUMERICHOST|NI_NUMERICSERV);
+ fds->statePtr->acceptProc(fds->statePtr->acceptProcData,
+ newSockState->channel, host, atoi(port));
}
}