From ed80f025f6f87f144ee7b63931890efb4421ee78 Mon Sep 17 00:00:00 2001 From: "jan.nijtmans" Date: Tue, 22 Nov 2016 10:06:18 +0000 Subject: This is patch.002 from ticket [0b9d3ba2ba3e1e3fc33c97d5a9fa7ef85d11a696|0b9d3ba2ba], as first start of tip-456 implementation --- generic/tclIOCmd.c | 15 ++++++++++++--- unix/tclUnixSock.c | 20 ++++++++++++++++++++ win/tclWinSock.c | 4 ++-- 3 files changed, 34 insertions(+), 5 deletions(-) diff --git a/generic/tclIOCmd.c b/generic/tclIOCmd.c index de65da5..9dc8f07 100644 --- a/generic/tclIOCmd.c +++ b/generic/tclIOCmd.c @@ -1485,12 +1485,12 @@ Tcl_SocketObjCmd( Tcl_Obj *const objv[]) /* Argument objects. */ { static const char *const socketOptions[] = { - "-async", "-myaddr", "-myport", "-server", NULL + "-async", "-myaddr", "-myport", "-server", "-reuseport", NULL }; enum socketOptions { - SKT_ASYNC, SKT_MYADDR, SKT_MYPORT, SKT_SERVER + SKT_ASYNC, SKT_MYADDR, SKT_MYPORT, SKT_SERVER, SKT_REUSEPORT }; - int optionIndex, a, server = 0, port, myport = 0, async = 0; + int optionIndex, a, server = 0, port, myport = 0, async = 0, reuseport = 0; const char *host, *myaddr = NULL; Tcl_Obj *script = NULL; Tcl_Channel chan; @@ -1557,6 +1557,9 @@ Tcl_SocketObjCmd( } script = objv[a]; break; + case SKT_REUSEPORT: + reuseport = 1; + break; default: Tcl_Panic("Tcl_SocketObjCmd: bad option index to SocketOptions"); } @@ -1600,6 +1603,12 @@ Tcl_SocketObjCmd( Tcl_IncrRefCount(script); acceptCallbackPtr->script = script; acceptCallbackPtr->interp = interp; + + /* Hint for Tcl_OpenTcpServer to set socket option REUSEPORT */ + if(reuseport) { + port |= (1 << 16); + } + chan = Tcl_OpenTcpServer(interp, port, host, AcceptCallbackProc, acceptCallbackPtr); if (chan == NULL) { diff --git a/unix/tclUnixSock.c b/unix/tclUnixSock.c index 170aea9..d8a33f9 100644 --- a/unix/tclUnixSock.c +++ b/unix/tclUnixSock.c @@ -113,6 +113,11 @@ struct TcpState { #define SOCKET_BUFSIZE 4096 +#ifdef SO_REUSEPORT +/* Bitmask to check if the setting of SO_REUSEPORT was requested by the caller. */ +#define USE_SOCK_REUSEPORT (1 << 16) +#endif + /* * Static routines for this file: */ @@ -1435,6 +1440,10 @@ Tcl_OpenTcpServer( char channelName[SOCK_CHAN_LENGTH]; const char *errorMsg = NULL; TcpFdList *fds = NULL, *newfds; +#ifdef SO_REUSEPORT + int reuseport = port & USE_SOCK_REUSEPORT; + CLEAR_BITS(port, USE_SOCK_REUSEPORT); +#endif /* * Try to record and return the most meaningful error message, i.e. the @@ -1512,6 +1521,17 @@ Tcl_OpenTcpServer( (void) setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *) &reuseaddr, sizeof(reuseaddr)); +#ifdef SO_REUSEPORT + /* + * Set up to allows multiple sockets on the same host to bind to the same port. + * The flag can be switched on by setting the lowest bit above the valid maximum port (0xffff). + */ + if(reuseport) { + (void) setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, + (char *) &reuseport, sizeof(reuseport)); + } +#endif + /* * Make sure we use the same port number when opening two server * sockets for IPv4 and IPv6 on a random port. diff --git a/win/tclWinSock.c b/win/tclWinSock.c index ec881d2..af8dda1 100644 --- a/win/tclWinSock.c +++ b/win/tclWinSock.c @@ -2111,8 +2111,8 @@ Tcl_OpenTcpServer( /* * Bind to the specified port. Note that we must not call - * setsockopt with SO_REUSEADDR because Microsoft allows addresses - * to be reused even if they are still in use. + * setsockopt with SO_REUSEADDR or SO_REUSEPORT because Microsoft + * allows addresses and ports to be reused even if they are still in use. * * Bind should not be affected by the socket having already been * set into nonblocking mode. If there is trouble, this is one -- cgit v0.12 From 490b4bf1f8778fddb9814d30cdf2c4bd89d9581d Mon Sep 17 00:00:00 2001 From: "jan.nijtmans" Date: Tue, 22 Nov 2016 11:21:00 +0000 Subject: Added stub entry for tip #456. Documentation and tests still missing. Doesn't conform to TIP yet. --- generic/tcl.decls | 11 +++++++++++ generic/tclDecls.h | 8 ++++++++ generic/tclIOCmd.c | 15 +++++---------- generic/tclIOSock.c | 24 ++++++++++++++++++++++++ generic/tclStubInit.c | 1 + unix/tclUnixSock.c | 14 ++++++-------- win/tclWinSock.c | 5 +++-- 7 files changed, 58 insertions(+), 20 deletions(-) diff --git a/generic/tcl.decls b/generic/tcl.decls index 574b49b..20c3f3e 100644 --- a/generic/tcl.decls +++ b/generic/tcl.decls @@ -2326,6 +2326,17 @@ declare 630 { # ----- BASELINE -- FOR -- 8.6.0 ----- # +# TIP #456 +declare 631 { + Tcl_Channel Tcl_OpenTcpServerEx(Tcl_Interp *interp, int port, + const char *host, int flags, Tcl_TcpAcceptProc *acceptProc, + ClientData callbackData) +} + +# ----- BASELINE -- FOR -- 8.7.0 ----- # + + + ############################################################################## # Define the platform specific public Tcl interface. These functions are only diff --git a/generic/tclDecls.h b/generic/tclDecls.h index b022d3c..d1c1170 100644 --- a/generic/tclDecls.h +++ b/generic/tclDecls.h @@ -1816,6 +1816,11 @@ EXTERN int Tcl_FSUnloadFile(Tcl_Interp *interp, EXTERN void Tcl_ZlibStreamSetCompressionDictionary( Tcl_ZlibStream zhandle, Tcl_Obj *compressionDictionaryObj); +/* 631 */ +EXTERN Tcl_Channel Tcl_OpenTcpServerEx(Tcl_Interp *interp, int port, + const char *host, int flags, + Tcl_TcpAcceptProc *acceptProc, + ClientData callbackData); typedef struct { const struct TclPlatStubs *tclPlatStubs; @@ -2482,6 +2487,7 @@ typedef struct TclStubs { void * (*tcl_FindSymbol) (Tcl_Interp *interp, Tcl_LoadHandle handle, const char *symbol); /* 628 */ int (*tcl_FSUnloadFile) (Tcl_Interp *interp, Tcl_LoadHandle handlePtr); /* 629 */ void (*tcl_ZlibStreamSetCompressionDictionary) (Tcl_ZlibStream zhandle, Tcl_Obj *compressionDictionaryObj); /* 630 */ + Tcl_Channel (*tcl_OpenTcpServerEx) (Tcl_Interp *interp, int port, const char *host, int flags, Tcl_TcpAcceptProc *acceptProc, ClientData callbackData); /* 631 */ } TclStubs; extern const TclStubs *tclStubsPtr; @@ -3774,6 +3780,8 @@ extern const TclStubs *tclStubsPtr; (tclStubsPtr->tcl_FSUnloadFile) /* 629 */ #define Tcl_ZlibStreamSetCompressionDictionary \ (tclStubsPtr->tcl_ZlibStreamSetCompressionDictionary) /* 630 */ +#define Tcl_OpenTcpServerEx \ + (tclStubsPtr->tcl_OpenTcpServerEx) /* 631 */ #endif /* defined(USE_TCL_STUBS) */ diff --git a/generic/tclIOCmd.c b/generic/tclIOCmd.c index 9dc8f07..883c6b7 100644 --- a/generic/tclIOCmd.c +++ b/generic/tclIOCmd.c @@ -1490,7 +1490,7 @@ Tcl_SocketObjCmd( enum socketOptions { SKT_ASYNC, SKT_MYADDR, SKT_MYPORT, SKT_SERVER, SKT_REUSEPORT }; - int optionIndex, a, server = 0, port, myport = 0, async = 0, reuseport = 0; + int optionIndex, a, server = 0, port, myport = 0, async = 0, flags = 0; const char *host, *myaddr = NULL; Tcl_Obj *script = NULL; Tcl_Channel chan; @@ -1557,9 +1557,9 @@ Tcl_SocketObjCmd( } script = objv[a]; break; - case SKT_REUSEPORT: - reuseport = 1; - break; + case SKT_REUSEPORT: + flags |= 1; + break; default: Tcl_Panic("Tcl_SocketObjCmd: bad option index to SocketOptions"); } @@ -1604,12 +1604,7 @@ Tcl_SocketObjCmd( acceptCallbackPtr->script = script; acceptCallbackPtr->interp = interp; - /* Hint for Tcl_OpenTcpServer to set socket option REUSEPORT */ - if(reuseport) { - port |= (1 << 16); - } - - chan = Tcl_OpenTcpServer(interp, port, host, AcceptCallbackProc, + chan = Tcl_OpenTcpServerEx(interp, port, host, flags, AcceptCallbackProc, acceptCallbackPtr); if (chan == NULL) { Tcl_DecrRefCount(script); diff --git a/generic/tclIOSock.c b/generic/tclIOSock.c index 7ed751c..72a0b5a 100644 --- a/generic/tclIOSock.c +++ b/generic/tclIOSock.c @@ -283,6 +283,30 @@ TclCreateSocketAddress( } return 1; } + +/* + *---------------------------------------------------------------------- + * + * Tcl_OpenTcpServer -- + * + * Opens a TCP server socket and creates a channel around it. + * + * Results: + * The channel or NULL if failed. If an error occurred, an error message + * is left in the interp's result if interp is not NULL. + * + * Side effects: + * Opens a server socket and creates a new channel. + * + *---------------------------------------------------------------------- + */ +Tcl_Channel Tcl_OpenTcpServer(Tcl_Interp *interp, int port, + const char *host, Tcl_TcpAcceptProc *acceptProc, + ClientData callbackData) +{ + return Tcl_OpenTcpServerEx(interp, port, host, 0, acceptProc, callbackData); +} + /* * Local Variables: diff --git a/generic/tclStubInit.c b/generic/tclStubInit.c index 2f1bb8b..23da6dc 100644 --- a/generic/tclStubInit.c +++ b/generic/tclStubInit.c @@ -1416,6 +1416,7 @@ const TclStubs tclStubs = { Tcl_FindSymbol, /* 628 */ Tcl_FSUnloadFile, /* 629 */ Tcl_ZlibStreamSetCompressionDictionary, /* 630 */ + Tcl_OpenTcpServerEx, /* 631 */ }; /* !END!: Do not edit above this line. */ diff --git a/unix/tclUnixSock.c b/unix/tclUnixSock.c index d8a33f9..6286b2f 100644 --- a/unix/tclUnixSock.c +++ b/unix/tclUnixSock.c @@ -115,7 +115,7 @@ struct TcpState { #ifdef SO_REUSEPORT /* Bitmask to check if the setting of SO_REUSEPORT was requested by the caller. */ -#define USE_SOCK_REUSEPORT (1 << 16) +#define USE_SOCK_REUSEPORT 1 #endif /* @@ -1410,7 +1410,7 @@ TclpMakeTcpClientChannelMode( /* *---------------------------------------------------------------------- * - * Tcl_OpenTcpServer -- + * Tcl_OpenTcpServerEx -- * * Opens a TCP server socket and creates a channel around it. * @@ -1425,10 +1425,11 @@ TclpMakeTcpClientChannelMode( */ Tcl_Channel -Tcl_OpenTcpServer( +Tcl_OpenTcpServerEx( Tcl_Interp *interp, /* For error reporting - may be NULL. */ int port, /* Port number to open. */ const char *myHost, /* Name of local host. */ + int flags, /* Flags. */ Tcl_TcpAcceptProc *acceptProc, /* Callback for accepting connections from new * clients. */ @@ -1440,10 +1441,6 @@ Tcl_OpenTcpServer( char channelName[SOCK_CHAN_LENGTH]; const char *errorMsg = NULL; TcpFdList *fds = NULL, *newfds; -#ifdef SO_REUSEPORT - int reuseport = port & USE_SOCK_REUSEPORT; - CLEAR_BITS(port, USE_SOCK_REUSEPORT); -#endif /* * Try to record and return the most meaningful error message, i.e. the @@ -1526,7 +1523,8 @@ Tcl_OpenTcpServer( * Set up to allows multiple sockets on the same host to bind to the same port. * The flag can be switched on by setting the lowest bit above the valid maximum port (0xffff). */ - if(reuseport) { + if (flags & USE_SOCK_REUSEPORT) { + int reuseport = 1; (void) setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, (char *) &reuseport, sizeof(reuseport)); } diff --git a/win/tclWinSock.c b/win/tclWinSock.c index af8dda1..a389d45 100644 --- a/win/tclWinSock.c +++ b/win/tclWinSock.c @@ -2020,7 +2020,7 @@ Tcl_MakeTcpClientChannel( /* *---------------------------------------------------------------------- * - * Tcl_OpenTcpServer -- + * Tcl_OpenTcpServerEx -- * * Opens a TCP server socket and creates a channel around it. * @@ -2035,10 +2035,11 @@ Tcl_MakeTcpClientChannel( */ Tcl_Channel -Tcl_OpenTcpServer( +Tcl_OpenTcpServerEx( Tcl_Interp *interp, /* For error reporting - may be NULL. */ int port, /* Port number to open. */ const char *myHost, /* Name of local host. */ + int flags, /* Flags (not used) */ Tcl_TcpAcceptProc *acceptProc, /* Callback for accepting connections from new * clients. */ -- cgit v0.12 From 33ded984ff02df26e0faf1d254abd2ea6acc0070 Mon Sep 17 00:00:00 2001 From: "jan.nijtmans" Date: Tue, 22 Nov 2016 11:24:48 +0000 Subject: Fix indenting --- unix/tclUnixSock.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/unix/tclUnixSock.c b/unix/tclUnixSock.c index 6286b2f..4767522 100644 --- a/unix/tclUnixSock.c +++ b/unix/tclUnixSock.c @@ -1519,15 +1519,15 @@ Tcl_OpenTcpServerEx( (char *) &reuseaddr, sizeof(reuseaddr)); #ifdef SO_REUSEPORT - /* - * Set up to allows multiple sockets on the same host to bind to the same port. - * The flag can be switched on by setting the lowest bit above the valid maximum port (0xffff). - */ - if (flags & USE_SOCK_REUSEPORT) { + /* + * Set up to allows multiple sockets on the same host to bind to the same port. + * The flag can be switched on by setting the lowest bit above the valid maximum port (0xffff). + */ + if (flags & USE_SOCK_REUSEPORT) { int reuseport = 1; - (void) setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, - (char *) &reuseport, sizeof(reuseport)); - } + (void) setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, + (char *) &reuseport, sizeof(reuseport)); + } #endif /* -- cgit v0.12 From 5b33616e5080b53d8b1e8b8608a98d824a952ee9 Mon Sep 17 00:00:00 2001 From: limeboy Date: Thu, 24 Nov 2016 12:47:21 +0000 Subject: Implement the whole TIP 456 specification. Also introduces the `-reuseaddr' and `-reuseport' options for the `socket' command. --- generic/tcl.decls | 2 +- generic/tcl.h | 7 +++++++ generic/tclDecls.h | 4 ++-- generic/tclIOCmd.c | 23 ++++++++++++++++++----- generic/tclIOSock.c | 6 +++--- unix/tclUnixSock.c | 41 ++++++++++++++++++++--------------------- win/tclWinSock.c | 2 +- 7 files changed, 52 insertions(+), 33 deletions(-) diff --git a/generic/tcl.decls b/generic/tcl.decls index 20c3f3e..af496b3 100644 --- a/generic/tcl.decls +++ b/generic/tcl.decls @@ -2329,7 +2329,7 @@ declare 630 { # TIP #456 declare 631 { Tcl_Channel Tcl_OpenTcpServerEx(Tcl_Interp *interp, int port, - const char *host, int flags, Tcl_TcpAcceptProc *acceptProc, + const char *host, unsigned int flags, Tcl_TcpAcceptProc *acceptProc, ClientData callbackData) } diff --git a/generic/tcl.h b/generic/tcl.h index eb53c70..75a947a 100644 --- a/generic/tcl.h +++ b/generic/tcl.h @@ -2371,6 +2371,13 @@ typedef int (Tcl_ArgvGenFuncProc)(ClientData clientData, Tcl_Interp *interp, /* *---------------------------------------------------------------------------- + * Definitions needed for the Tcl_OpenTcpServerEx function. [TIP #456] + */ +#define TCL_TCPSERVER_REUSEADDR (1<<0) +#define TCL_TCPSERVER_REUSEPORT (1<<1) + +/* + *---------------------------------------------------------------------------- * Single public declaration for NRE. */ diff --git a/generic/tclDecls.h b/generic/tclDecls.h index d1c1170..4810c51 100644 --- a/generic/tclDecls.h +++ b/generic/tclDecls.h @@ -1818,7 +1818,7 @@ EXTERN void Tcl_ZlibStreamSetCompressionDictionary( Tcl_Obj *compressionDictionaryObj); /* 631 */ EXTERN Tcl_Channel Tcl_OpenTcpServerEx(Tcl_Interp *interp, int port, - const char *host, int flags, + const char *host, unsigned int flags, Tcl_TcpAcceptProc *acceptProc, ClientData callbackData); @@ -2487,7 +2487,7 @@ typedef struct TclStubs { void * (*tcl_FindSymbol) (Tcl_Interp *interp, Tcl_LoadHandle handle, const char *symbol); /* 628 */ int (*tcl_FSUnloadFile) (Tcl_Interp *interp, Tcl_LoadHandle handlePtr); /* 629 */ void (*tcl_ZlibStreamSetCompressionDictionary) (Tcl_ZlibStream zhandle, Tcl_Obj *compressionDictionaryObj); /* 630 */ - Tcl_Channel (*tcl_OpenTcpServerEx) (Tcl_Interp *interp, int port, const char *host, int flags, Tcl_TcpAcceptProc *acceptProc, ClientData callbackData); /* 631 */ + Tcl_Channel (*tcl_OpenTcpServerEx) (Tcl_Interp *interp, int port, const char *host, unsigned int flags, Tcl_TcpAcceptProc *acceptProc, ClientData callbackData); /* 631 */ } TclStubs; extern const TclStubs *tclStubsPtr; diff --git a/generic/tclIOCmd.c b/generic/tclIOCmd.c index 883c6b7..b4696fd 100644 --- a/generic/tclIOCmd.c +++ b/generic/tclIOCmd.c @@ -1485,12 +1485,15 @@ Tcl_SocketObjCmd( Tcl_Obj *const objv[]) /* Argument objects. */ { static const char *const socketOptions[] = { - "-async", "-myaddr", "-myport", "-server", "-reuseport", NULL + "-async", "-myaddr", "-myport", "-server", "-reuseaddr", "-reuseport", + NULL }; enum socketOptions { - SKT_ASYNC, SKT_MYADDR, SKT_MYPORT, SKT_SERVER, SKT_REUSEPORT + SKT_ASYNC, SKT_MYADDR, SKT_MYPORT, SKT_SERVER, SKT_REUSEADDR, + SKT_REUSEPORT }; - int optionIndex, a, server = 0, port, myport = 0, async = 0, flags = 0; + int optionIndex, a, server = 0, port, myport = 0, async = 0; + unsigned int flags = 0; const char *host, *myaddr = NULL; Tcl_Obj *script = NULL; Tcl_Channel chan; @@ -1557,8 +1560,11 @@ Tcl_SocketObjCmd( } script = objv[a]; break; + case SKT_REUSEADDR: + flags |= TCL_TCPSERVER_REUSEADDR; + break; case SKT_REUSEPORT: - flags |= 1; + flags |= TCL_TCPSERVER_REUSEPORT; break; default: Tcl_Panic("Tcl_SocketObjCmd: bad option index to SocketOptions"); @@ -1583,7 +1589,14 @@ Tcl_SocketObjCmd( "?-myaddr addr? ?-myport myport? ?-async? host port"); iPtr->flags |= INTERP_ALTERNATE_WRONG_ARGS; Tcl_WrongNumArgs(interp, 1, objv, - "-server command ?-myaddr addr? port"); + "-server command ?-reuseaddr? ?-reuseport? ?-myaddr addr? port"); + return TCL_ERROR; + } + + if (!server && flags != 0) { + Tcl_SetObjResult(interp, Tcl_NewStringObj( + "options -reuseaddr and -reuseport are only valid for servers", + -1)); return TCL_ERROR; } diff --git a/generic/tclIOSock.c b/generic/tclIOSock.c index 72a0b5a..b6e99ba 100644 --- a/generic/tclIOSock.c +++ b/generic/tclIOSock.c @@ -283,7 +283,7 @@ TclCreateSocketAddress( } return 1; } - + /* *---------------------------------------------------------------------- * @@ -304,9 +304,9 @@ Tcl_Channel Tcl_OpenTcpServer(Tcl_Interp *interp, int port, const char *host, Tcl_TcpAcceptProc *acceptProc, ClientData callbackData) { - return Tcl_OpenTcpServerEx(interp, port, host, 0, acceptProc, callbackData); + return Tcl_OpenTcpServerEx(interp, port, host, TCL_TCPSERVER_REUSEADDR, + acceptProc, callbackData); } - /* * Local Variables: diff --git a/unix/tclUnixSock.c b/unix/tclUnixSock.c index 4767522..bb75ed3 100644 --- a/unix/tclUnixSock.c +++ b/unix/tclUnixSock.c @@ -113,11 +113,6 @@ struct TcpState { #define SOCKET_BUFSIZE 4096 -#ifdef SO_REUSEPORT -/* Bitmask to check if the setting of SO_REUSEPORT was requested by the caller. */ -#define USE_SOCK_REUSEPORT 1 -#endif - /* * Static routines for this file: */ @@ -1429,13 +1424,13 @@ Tcl_OpenTcpServerEx( Tcl_Interp *interp, /* For error reporting - may be NULL. */ int port, /* Port number to open. */ const char *myHost, /* Name of local host. */ - int flags, /* Flags. */ + unsigned int flags, /* Flags. */ Tcl_TcpAcceptProc *acceptProc, /* Callback for accepting connections from new * clients. */ ClientData acceptProcData) /* Data for the callback. */ { - int status = 0, sock = -1, reuseaddr = 1, chosenport; + int status = 0, sock = -1, optvalue, chosenport; struct addrinfo *addrlist = NULL, *addrPtr; /* socket address */ TcpState *statePtr = NULL; char channelName[SOCK_CHAN_LENGTH]; @@ -1511,24 +1506,28 @@ Tcl_OpenTcpServerEx( 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)); - -#ifdef SO_REUSEPORT - /* - * Set up to allows multiple sockets on the same host to bind to the same port. - * The flag can be switched on by setting the lowest bit above the valid maximum port (0xffff). - */ - if (flags & USE_SOCK_REUSEPORT) { - int reuseport = 1; - (void) setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, - (char *) &reuseport, sizeof(reuseport)); + if (flags & TCL_TCPSERVER_REUSEADDR) { + optvalue = 1; + (void) setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, + (char *) &optvalue, sizeof(optvalue)); } + + if (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; #endif + optvalue = 1; + (void) setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, + (char *) &optvalue, sizeof(optvalue)); + } /* * Make sure we use the same port number when opening two server diff --git a/win/tclWinSock.c b/win/tclWinSock.c index a389d45..b228730 100644 --- a/win/tclWinSock.c +++ b/win/tclWinSock.c @@ -2039,7 +2039,7 @@ Tcl_OpenTcpServerEx( Tcl_Interp *interp, /* For error reporting - may be NULL. */ int port, /* Port number to open. */ const char *myHost, /* Name of local host. */ - int flags, /* Flags (not used) */ + unsigned int flags, /* Flags (not used) */ Tcl_TcpAcceptProc *acceptProc, /* Callback for accepting connections from new * clients. */ -- cgit v0.12 From a3a4e02afa38968088c4f32a658ddb26391bc117 Mon Sep 17 00:00:00 2001 From: "jan.nijtmans" Date: Thu, 24 Nov 2016 13:18:10 +0000 Subject: Fix compile error if SO_REUSEPORT not supported. Put command options in alphabetical order --- generic/tclIOCmd.c | 6 +++--- unix/tclUnixSock.c | 3 ++- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/generic/tclIOCmd.c b/generic/tclIOCmd.c index b4696fd..930f5a3 100644 --- a/generic/tclIOCmd.c +++ b/generic/tclIOCmd.c @@ -1485,12 +1485,12 @@ Tcl_SocketObjCmd( Tcl_Obj *const objv[]) /* Argument objects. */ { static const char *const socketOptions[] = { - "-async", "-myaddr", "-myport", "-server", "-reuseaddr", "-reuseport", + "-async", "-myaddr", "-myport", "-reuseaddr", "-reuseport", "-server", NULL }; enum socketOptions { - SKT_ASYNC, SKT_MYADDR, SKT_MYPORT, SKT_SERVER, SKT_REUSEADDR, - SKT_REUSEPORT + SKT_ASYNC, SKT_MYADDR, SKT_MYPORT, SKT_REUSEADDR, SKT_REUSEPORT, + SKT_SERVER }; int optionIndex, a, server = 0, port, myport = 0, async = 0; unsigned int flags = 0; diff --git a/unix/tclUnixSock.c b/unix/tclUnixSock.c index bb75ed3..187c157 100644 --- a/unix/tclUnixSock.c +++ b/unix/tclUnixSock.c @@ -1523,10 +1523,11 @@ Tcl_OpenTcpServerEx( */ errorMsg = "SO_REUSEPORT isn't supported by this platform"; goto error; -#endif +#else optvalue = 1; (void) setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, (char *) &optvalue, sizeof(optvalue)); +#endif } /* -- cgit v0.12 From ef3c261b6dcd49b4bae9b133b7d402090863ddc9 Mon Sep 17 00:00:00 2001 From: limeboy Date: Thu, 24 Nov 2016 15:00:36 +0000 Subject: Adjust the tests and add a handful of new ones. --- tests/socket.test | 31 +++++++++++++++++++++++++------ 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/tests/socket.test b/tests/socket.test index d43c41c..82dc800 100644 --- a/tests/socket.test +++ b/tests/socket.test @@ -265,13 +265,13 @@ test socket_$af-1.1 {arg parsing for socket command} -constraints [list socket s } -returnCodes error -result {no argument given for -server option} test socket_$af-1.2 {arg parsing for socket command} -constraints [list socket supported_$af] -body { socket -server foo -} -returnCodes error -result {wrong # args: should be "socket ?-myaddr addr? ?-myport myport? ?-async? host port" or "socket -server command ?-myaddr addr? port"} +} -returnCodes error -result {wrong # args: should be "socket ?-myaddr addr? ?-myport myport? ?-async? host port" or "socket -server command ?-reuseaddr? ?-reuseport? ?-myaddr addr? port"} test socket_$af-1.3 {arg parsing for socket command} -constraints [list socket supported_$af] -body { socket -myaddr } -returnCodes error -result {no argument given for -myaddr option} test socket_$af-1.4 {arg parsing for socket command} -constraints [list socket supported_$af] -body { socket -myaddr $localhost -} -returnCodes error -result {wrong # args: should be "socket ?-myaddr addr? ?-myport myport? ?-async? host port" or "socket -server command ?-myaddr addr? port"} +} -returnCodes error -result {wrong # args: should be "socket ?-myaddr addr? ?-myport myport? ?-async? host port" or "socket -server command ?-reuseaddr? ?-reuseport? ?-myaddr addr? port"} test socket_$af-1.5 {arg parsing for socket command} -constraints [list socket supported_$af] -body { socket -myport } -returnCodes error -result {no argument given for -myport option} @@ -280,19 +280,19 @@ test socket_$af-1.6 {arg parsing for socket command} -constraints [list socket s } -returnCodes error -result {expected integer but got "xxxx"} test socket_$af-1.7 {arg parsing for socket command} -constraints [list socket supported_$af] -body { socket -myport 2522 -} -returnCodes error -result {wrong # args: should be "socket ?-myaddr addr? ?-myport myport? ?-async? host port" or "socket -server command ?-myaddr addr? port"} +} -returnCodes error -result {wrong # args: should be "socket ?-myaddr addr? ?-myport myport? ?-async? host port" or "socket -server command ?-reuseaddr? ?-reuseport? ?-myaddr addr? port"} test socket_$af-1.8 {arg parsing for socket command} -constraints [list socket supported_$af] -body { socket -froboz -} -returnCodes error -result {bad option "-froboz": must be -async, -myaddr, -myport, or -server} +} -returnCodes error -result {bad option "-froboz": must be -async, -myaddr, -myport, -server, -reuseaddr, or -reuseport} test socket_$af-1.9 {arg parsing for socket command} -constraints [list socket supported_$af] -body { socket -server foo -myport 2521 3333 } -returnCodes error -result {option -myport is not valid for servers} test socket_$af-1.10 {arg parsing for socket command} -constraints [list socket supported_$af] -body { socket host 2528 -junk -} -returnCodes error -result {wrong # args: should be "socket ?-myaddr addr? ?-myport myport? ?-async? host port" or "socket -server command ?-myaddr addr? port"} +} -returnCodes error -result {wrong # args: should be "socket ?-myaddr addr? ?-myport myport? ?-async? host port" or "socket -server command ?-reuseaddr? ?-reuseport? ?-myaddr addr? port"} test socket_$af-1.11 {arg parsing for socket command} -constraints [list socket supported_$af] -body { socket -server callback 2520 -- -} -returnCodes error -result {wrong # args: should be "socket ?-myaddr addr? ?-myport myport? ?-async? host port" or "socket -server command ?-myaddr addr? port"} +} -returnCodes error -result {wrong # args: should be "socket ?-myaddr addr? ?-myport myport? ?-async? host port" or "socket -server command ?-reuseaddr? ?-reuseport? ?-myaddr addr? port"} test socket_$af-1.12 {arg parsing for socket command} -constraints [list socket supported_$af] -body { socket foo badport } -returnCodes error -result {expected integer but got "badport"} @@ -302,6 +302,12 @@ test socket_$af-1.13 {arg parsing for socket command} -constraints [list socket test socket_$af-1.14 {arg parsing for socket command} -constraints [list socket supported_$af] -body { socket -server foo -async } -returnCodes error -result {cannot set -async option for server sockets} +test socket_$af-1.15 {arg parsing for socket command} -constraints [list socket supported_$af] -body { + socket -reuseaddr 4242 +} -returnCodes error -result {options -reuseaddr and -reuseport are only valid for servers} +test socket_$af-1.16 {arg parsing for socket command} -constraints [list socket supported_$af] -body { + socket -reuseport 4242 +} -returnCodes error -result {options -reuseaddr and -reuseport are only valid for servers} set path(script) [makeFile {} script] @@ -2360,6 +2366,19 @@ test socket-14.18 {bug c6ed4acfd8: running async socket connect made other conne catch {close $csock2} } -result {} +test socket-14.19 {tip 456 -- introduce the -reuseport option} \ + -constraints {socket} \ + -body { + proc accept {channel address port} {} + set port [randport] + set ssock1 [socket -server accept -reuseport $port] + set ssock2 [socket -server accept -reuseport $port] + return ok +} -cleanup { + catch {close $ssock1} + catch {close $ssock2} + } -result ok + set num 0 set x {localhost {socket} 127.0.0.1 {supported_inet} ::1 {supported_inet6}} -- cgit v0.12 From 994c07125e17358a895eddd8f573065b68d83d68 Mon Sep 17 00:00:00 2001 From: limeboy Date: Thu, 24 Nov 2016 20:28:42 +0000 Subject: First round of documentation update. --- doc/OpenTcp.3 | 13 ++++++++++++- doc/socket.n | 10 ++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/doc/OpenTcp.3 b/doc/OpenTcp.3 index 4a7dc1e..040a8e2 100644 --- a/doc/OpenTcp.3 +++ b/doc/OpenTcp.3 @@ -9,7 +9,7 @@ .BS '\" Note: do not modify the .SH NAME line immediately below! .SH NAME -Tcl_OpenTcpClient, Tcl_MakeTcpClientChannel, Tcl_OpenTcpServer \- procedures to open channels using TCP sockets +Tcl_OpenTcpClient, Tcl_MakeTcpClientChannel, Tcl_OpenTcpServer, Tcl_OpenTcpServerEx \- procedures to open channels using TCP sockets .SH SYNOPSIS .nf \fB#include \fR @@ -23,6 +23,9 @@ Tcl_Channel Tcl_Channel \fBTcl_OpenTcpServer\fR(\fIinterp, port, myaddr, proc, clientData\fR) .sp +Tcl_Channel +\fBTcl_OpenTcpServerEx\fR(\fIinterp, port, myaddr, flags, proc, clientData\fR) +.sp .SH ARGUMENTS .AS Tcl_TcpAcceptProc clientData .AP Tcl_Interp *interp in @@ -41,6 +44,9 @@ for the local end of the connection. If NULL, a default interface is chosen. .AP int async in If nonzero, the client socket is connected asynchronously to the server. +.AP "unsigned int" flags in +ORed combination of \fBTCL_TCPSERVER\fR flags that specify additional +informations about the socket being created. .AP ClientData sock in Platform-specific handle for client TCP socket. .AP Tcl_TcpAcceptProc *proc in @@ -158,6 +164,11 @@ register it, use \fBTcl_RegisterChannel\fR. If one of the standard channels, \fBstdin\fR, \fBstdout\fR or \fBstderr\fR was previously closed, the act of creating the new channel also assigns it as a replacement for the standard channel. +.SS TCL_OPENTCPSERVEREX +.PP +\fBTcl_OpenTcpServerEx\fR behaviour is identical to \fBTcl_OpenTcpServer\fR but +gives more flexibility to the user by providing a mean to further customize some +aspects of the socket via the \fIflags\fR parameter. .SH "PLATFORM ISSUES" .PP On Unix platforms, the socket handle is a Unix file descriptor as diff --git a/doc/socket.n b/doc/socket.n index 3efdb37..3d77bab 100644 --- a/doc/socket.n +++ b/doc/socket.n @@ -131,6 +131,16 @@ wildcard address so that it can accept connections from any interface. If \fIaddr\fR is a domain name that resolves to multiple IP addresses that are available on the local machine, the socket will listen on all of them. +.TP +\fB\-reuseaddr\fI boolean\fR +. +Tells the kernel whether to reuse the local address if there's no active socket +listening on it. This is the default on Windows. +.TP +\fB\-reuseport\fI boolean\fR +. +Tells the kernel whether to allow the binding of multiple sockets to the same +socket address. This is the default on Windows. .PP Server channels cannot be used for input or output; their sole use is to accept new client connections. The channels created for each incoming -- cgit v0.12 From c7c008ed87d396afec9a6f9879a22660d6afa490 Mon Sep 17 00:00:00 2001 From: limeboy Date: Thu, 24 Nov 2016 21:01:43 +0000 Subject: Allow a boolean argument to be passed. --- generic/tclIOCmd.c | 42 +++++++++++++++++++++++++++++++++++------- tests/socket.test | 18 +++++++++--------- unix/tclUnixSock.c | 3 ++- 3 files changed, 46 insertions(+), 17 deletions(-) diff --git a/generic/tclIOCmd.c b/generic/tclIOCmd.c index b4696fd..49c7c06 100644 --- a/generic/tclIOCmd.c +++ b/generic/tclIOCmd.c @@ -1485,14 +1485,14 @@ Tcl_SocketObjCmd( Tcl_Obj *const objv[]) /* Argument objects. */ { static const char *const socketOptions[] = { - "-async", "-myaddr", "-myport", "-server", "-reuseaddr", "-reuseport", + "-async", "-myaddr", "-myport", "-reuseaddr", "-reuseport", "server", NULL }; enum socketOptions { - SKT_ASYNC, SKT_MYADDR, SKT_MYPORT, SKT_SERVER, SKT_REUSEADDR, - SKT_REUSEPORT + SKT_ASYNC, SKT_MYADDR, SKT_MYPORT, SKT_REUSEADDR, SKT_REUSEPORT, + SKT_SERVER }; - int optionIndex, a, server = 0, port, myport = 0, async = 0; + int optionIndex, a, server = 0, port, myport = 0, async = 0, boolTmp; unsigned int flags = 0; const char *host, *myaddr = NULL; Tcl_Obj *script = NULL; @@ -1552,6 +1552,7 @@ Tcl_SocketObjCmd( return TCL_ERROR; } server = 1; + flags = TCL_TCPSERVER_REUSEADDR; a++; if (a >= objc) { Tcl_SetObjResult(interp, Tcl_NewStringObj( @@ -1561,10 +1562,36 @@ Tcl_SocketObjCmd( script = objv[a]; break; case SKT_REUSEADDR: - flags |= TCL_TCPSERVER_REUSEADDR; + a++; + if (a >= objc) { + Tcl_SetObjResult(interp, Tcl_NewStringObj( + "no argument given for -reuseaddr option", -1)); + return TCL_ERROR; + } + if (Tcl_GetBooleanFromObj(interp, objv[a], &boolTmp) != TCL_OK) { + return TCL_ERROR; + } + if (boolTmp) { + flags |= TCL_TCPSERVER_REUSEADDR; + } else { + flags &= ~TCL_TCPSERVER_REUSEADDR; + } break; case SKT_REUSEPORT: - flags |= TCL_TCPSERVER_REUSEPORT; + a++; + if (a >= objc) { + Tcl_SetObjResult(interp, Tcl_NewStringObj( + "no argument given for -reuseport option", -1)); + return TCL_ERROR; + } + if (Tcl_GetBooleanFromObj(interp, objv[a], &boolTmp) != TCL_OK) { + return TCL_ERROR; + } + if (boolTmp) { + flags |= TCL_TCPSERVER_REUSEPORT; + } else { + flags &= ~TCL_TCPSERVER_REUSEPORT; + } break; default: Tcl_Panic("Tcl_SocketObjCmd: bad option index to SocketOptions"); @@ -1589,7 +1616,8 @@ Tcl_SocketObjCmd( "?-myaddr addr? ?-myport myport? ?-async? host port"); iPtr->flags |= INTERP_ALTERNATE_WRONG_ARGS; Tcl_WrongNumArgs(interp, 1, objv, - "-server command ?-reuseaddr? ?-reuseport? ?-myaddr addr? port"); + "-server command ?-reuseaddr boolean? ?-reuseport boolean? " + "?-myaddr addr? port"); return TCL_ERROR; } diff --git a/tests/socket.test b/tests/socket.test index 82dc800..387e08e 100644 --- a/tests/socket.test +++ b/tests/socket.test @@ -265,13 +265,13 @@ test socket_$af-1.1 {arg parsing for socket command} -constraints [list socket s } -returnCodes error -result {no argument given for -server option} test socket_$af-1.2 {arg parsing for socket command} -constraints [list socket supported_$af] -body { socket -server foo -} -returnCodes error -result {wrong # args: should be "socket ?-myaddr addr? ?-myport myport? ?-async? host port" or "socket -server command ?-reuseaddr? ?-reuseport? ?-myaddr addr? port"} +} -returnCodes error -result {wrong # args: should be "socket ?-myaddr addr? ?-myport myport? ?-async? host port" or "socket -server command ?-reuseaddr boolean? ?-reuseport boolean? ?-myaddr addr? port"} test socket_$af-1.3 {arg parsing for socket command} -constraints [list socket supported_$af] -body { socket -myaddr } -returnCodes error -result {no argument given for -myaddr option} test socket_$af-1.4 {arg parsing for socket command} -constraints [list socket supported_$af] -body { socket -myaddr $localhost -} -returnCodes error -result {wrong # args: should be "socket ?-myaddr addr? ?-myport myport? ?-async? host port" or "socket -server command ?-reuseaddr? ?-reuseport? ?-myaddr addr? port"} +} -returnCodes error -result {wrong # args: should be "socket ?-myaddr addr? ?-myport myport? ?-async? host port" or "socket -server command ?-reuseaddr boolean? ?-reuseport boolean? ?-myaddr addr? port"} test socket_$af-1.5 {arg parsing for socket command} -constraints [list socket supported_$af] -body { socket -myport } -returnCodes error -result {no argument given for -myport option} @@ -280,7 +280,7 @@ test socket_$af-1.6 {arg parsing for socket command} -constraints [list socket s } -returnCodes error -result {expected integer but got "xxxx"} test socket_$af-1.7 {arg parsing for socket command} -constraints [list socket supported_$af] -body { socket -myport 2522 -} -returnCodes error -result {wrong # args: should be "socket ?-myaddr addr? ?-myport myport? ?-async? host port" or "socket -server command ?-reuseaddr? ?-reuseport? ?-myaddr addr? port"} +} -returnCodes error -result {wrong # args: should be "socket ?-myaddr addr? ?-myport myport? ?-async? host port" or "socket -server command ?-reuseaddr boolean? ?-reuseport boolean? ?-myaddr addr? port"} test socket_$af-1.8 {arg parsing for socket command} -constraints [list socket supported_$af] -body { socket -froboz } -returnCodes error -result {bad option "-froboz": must be -async, -myaddr, -myport, -server, -reuseaddr, or -reuseport} @@ -289,10 +289,10 @@ test socket_$af-1.9 {arg parsing for socket command} -constraints [list socket s } -returnCodes error -result {option -myport is not valid for servers} test socket_$af-1.10 {arg parsing for socket command} -constraints [list socket supported_$af] -body { socket host 2528 -junk -} -returnCodes error -result {wrong # args: should be "socket ?-myaddr addr? ?-myport myport? ?-async? host port" or "socket -server command ?-reuseaddr? ?-reuseport? ?-myaddr addr? port"} +} -returnCodes error -result {wrong # args: should be "socket ?-myaddr addr? ?-myport myport? ?-async? host port" or "socket -server command ?-reuseaddr boolean? ?-reuseport boolean? ?-myaddr addr? port"} test socket_$af-1.11 {arg parsing for socket command} -constraints [list socket supported_$af] -body { socket -server callback 2520 -- -} -returnCodes error -result {wrong # args: should be "socket ?-myaddr addr? ?-myport myport? ?-async? host port" or "socket -server command ?-reuseaddr? ?-reuseport? ?-myaddr addr? port"} +} -returnCodes error -result {wrong # args: should be "socket ?-myaddr addr? ?-myport myport? ?-async? host port" or "socket -server command ?-reuseaddr boolean? ?-reuseport boolean? ?-myaddr addr? port"} test socket_$af-1.12 {arg parsing for socket command} -constraints [list socket supported_$af] -body { socket foo badport } -returnCodes error -result {expected integer but got "badport"} @@ -303,10 +303,10 @@ test socket_$af-1.14 {arg parsing for socket command} -constraints [list socket socket -server foo -async } -returnCodes error -result {cannot set -async option for server sockets} test socket_$af-1.15 {arg parsing for socket command} -constraints [list socket supported_$af] -body { - socket -reuseaddr 4242 + socket -reuseaddr yes 4242 } -returnCodes error -result {options -reuseaddr and -reuseport are only valid for servers} test socket_$af-1.16 {arg parsing for socket command} -constraints [list socket supported_$af] -body { - socket -reuseport 4242 + socket -reuseport yes 4242 } -returnCodes error -result {options -reuseaddr and -reuseport are only valid for servers} set path(script) [makeFile {} script] @@ -2371,8 +2371,8 @@ test socket-14.19 {tip 456 -- introduce the -reuseport option} \ -body { proc accept {channel address port} {} set port [randport] - set ssock1 [socket -server accept -reuseport $port] - set ssock2 [socket -server accept -reuseport $port] + set ssock1 [socket -server accept -reuseport yes $port] + set ssock2 [socket -server accept -reuseport yes $port] return ok } -cleanup { catch {close $ssock1} diff --git a/unix/tclUnixSock.c b/unix/tclUnixSock.c index bb75ed3..187c157 100644 --- a/unix/tclUnixSock.c +++ b/unix/tclUnixSock.c @@ -1523,10 +1523,11 @@ Tcl_OpenTcpServerEx( */ errorMsg = "SO_REUSEPORT isn't supported by this platform"; goto error; -#endif +#else optvalue = 1; (void) setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, (char *) &optvalue, sizeof(optvalue)); +#endif } /* -- cgit v0.12 From bb43812d0c072311a503fa53ef663342c3eed4c1 Mon Sep 17 00:00:00 2001 From: limeboy Date: Fri, 25 Nov 2016 13:27:56 +0000 Subject: Minor documentation touchups. --- doc/socket.n | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/socket.n b/doc/socket.n index 3d77bab..532ea2e 100644 --- a/doc/socket.n +++ b/doc/socket.n @@ -134,13 +134,13 @@ listen on all of them. .TP \fB\-reuseaddr\fI boolean\fR . -Tells the kernel whether to reuse the local address if there's no active socket -listening on it. This is the default on Windows. +Tells the kernel whether to reuse the local address if there is no socket +actively listening on it. This is the default on Windows. .TP \fB\-reuseport\fI boolean\fR . Tells the kernel whether to allow the binding of multiple sockets to the same -socket address. This is the default on Windows. +address and port as long as all of them specify the \fB-reuseport\fR option. .PP Server channels cannot be used for input or output; their sole use is to accept new client connections. The channels created for each incoming -- cgit v0.12 From 644cb60988ea393b62e1e6fc23588ef660c8272d Mon Sep 17 00:00:00 2001 From: limeboy Date: Fri, 25 Nov 2016 14:53:20 +0000 Subject: Windows support and minor touchups to the documentation. --- doc/socket.n | 2 +- win/tclWinSock.c | 17 +++++++++++++---- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/doc/socket.n b/doc/socket.n index 532ea2e..823dbd5 100644 --- a/doc/socket.n +++ b/doc/socket.n @@ -140,7 +140,7 @@ actively listening on it. This is the default on Windows. \fB\-reuseport\fI boolean\fR . Tells the kernel whether to allow the binding of multiple sockets to the same -address and port as long as all of them specify the \fB-reuseport\fR option. +address and port. .PP Server channels cannot be used for input or output; their sole use is to accept new client connections. The channels created for each incoming diff --git a/win/tclWinSock.c b/win/tclWinSock.c index b228730..af22cf8 100644 --- a/win/tclWinSock.c +++ b/win/tclWinSock.c @@ -2039,7 +2039,7 @@ Tcl_OpenTcpServerEx( Tcl_Interp *interp, /* For error reporting - may be NULL. */ int port, /* Port number to open. */ const char *myHost, /* Name of local host. */ - unsigned int flags, /* Flags (not used) */ + unsigned int flags, /* Flags. */ Tcl_TcpAcceptProc *acceptProc, /* Callback for accepting connections from new * clients. */ @@ -2053,6 +2053,7 @@ Tcl_OpenTcpServerEx( char channelName[SOCK_CHAN_LENGTH]; u_long flag = 1; /* Indicates nonblocking mode. */ const char *errorMsg = NULL; + int optvalue; if (TclpHasSockets(interp) != TCL_OK) { return NULL; @@ -2111,9 +2112,17 @@ Tcl_OpenTcpServerEx( } /* - * Bind to the specified port. Note that we must not call - * setsockopt with SO_REUSEADDR or SO_REUSEPORT because Microsoft - * allows addresses and ports to be reused even if they are still in use. + * The SO_REUSEADDR option on Windows behaves like SO_REUSEPORT on unix + * systems. + */ + if (flags & TCL_TCPSERVER_REUSEPORT) { + optvalue = 1; + (void) setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, + (char *) &optvalue, sizeof(optvalue)); + } + + /* + * Bind to the specified port. * * Bind should not be affected by the socket having already been * set into nonblocking mode. If there is trouble, this is one -- cgit v0.12 From fdc5267c5f6488e6f8065453fc078e0ebc45d861 Mon Sep 17 00:00:00 2001 From: limeboy Date: Wed, 14 Dec 2016 15:49:27 +0000 Subject: Make OpenTcpServerEx accept a 'service' string parameter instead of a port. --- generic/tcl.decls | 2 +- generic/tclDecls.h | 7 ++++--- generic/tclIOCmd.c | 25 +++++++++++++++---------- unix/tclUnixSock.c | 9 +++++++-- win/tclWinSock.c | 9 +++++++-- 5 files changed, 34 insertions(+), 18 deletions(-) diff --git a/generic/tcl.decls b/generic/tcl.decls index af496b3..ba047a0 100644 --- a/generic/tcl.decls +++ b/generic/tcl.decls @@ -2328,7 +2328,7 @@ declare 630 { # TIP #456 declare 631 { - Tcl_Channel Tcl_OpenTcpServerEx(Tcl_Interp *interp, int port, + Tcl_Channel Tcl_OpenTcpServerEx(Tcl_Interp *interp, const char *service, const char *host, unsigned int flags, Tcl_TcpAcceptProc *acceptProc, ClientData callbackData) } diff --git a/generic/tclDecls.h b/generic/tclDecls.h index 4810c51..49ac440 100644 --- a/generic/tclDecls.h +++ b/generic/tclDecls.h @@ -1817,8 +1817,9 @@ EXTERN void Tcl_ZlibStreamSetCompressionDictionary( Tcl_ZlibStream zhandle, Tcl_Obj *compressionDictionaryObj); /* 631 */ -EXTERN Tcl_Channel Tcl_OpenTcpServerEx(Tcl_Interp *interp, int port, - const char *host, unsigned int flags, +EXTERN Tcl_Channel Tcl_OpenTcpServerEx(Tcl_Interp *interp, + const char *service, const char *host, + unsigned int flags, Tcl_TcpAcceptProc *acceptProc, ClientData callbackData); @@ -2487,7 +2488,7 @@ typedef struct TclStubs { void * (*tcl_FindSymbol) (Tcl_Interp *interp, Tcl_LoadHandle handle, const char *symbol); /* 628 */ int (*tcl_FSUnloadFile) (Tcl_Interp *interp, Tcl_LoadHandle handlePtr); /* 629 */ void (*tcl_ZlibStreamSetCompressionDictionary) (Tcl_ZlibStream zhandle, Tcl_Obj *compressionDictionaryObj); /* 630 */ - Tcl_Channel (*tcl_OpenTcpServerEx) (Tcl_Interp *interp, int port, const char *host, unsigned int flags, Tcl_TcpAcceptProc *acceptProc, ClientData callbackData); /* 631 */ + Tcl_Channel (*tcl_OpenTcpServerEx) (Tcl_Interp *interp, const char *service, const char *host, unsigned int flags, Tcl_TcpAcceptProc *acceptProc, ClientData callbackData); /* 631 */ } TclStubs; extern const TclStubs *tclStubsPtr; diff --git a/generic/tclIOCmd.c b/generic/tclIOCmd.c index d6637a0..951fb5a 100644 --- a/generic/tclIOCmd.c +++ b/generic/tclIOCmd.c @@ -1492,9 +1492,9 @@ Tcl_SocketObjCmd( SKT_ASYNC, SKT_MYADDR, SKT_MYPORT, SKT_REUSEADDR, SKT_REUSEPORT, SKT_SERVER }; - int optionIndex, a, server = 0, port, myport = 0, async = 0, boolTmp; + int optionIndex, a, server = 0, myport = 0, async = 0, boolTmp; unsigned int flags = 0; - const char *host, *myaddr = NULL; + const char *host, *port, *myaddr = NULL; Tcl_Obj *script = NULL; Tcl_Channel chan; @@ -1628,15 +1628,14 @@ Tcl_SocketObjCmd( return TCL_ERROR; } - if (a == objc-1) { - if (TclSockGetPort(interp, TclGetString(objv[a]), "tcp", - &port) != TCL_OK) { - return TCL_ERROR; - } - } else { + // All the arguments should have been parsed by now, 'a' points to the last + // one, the port number. + if (a != objc-1) { goto wrongNumArgs; } + port = TclGetString(objv[a]); + if (server) { AcceptCallback *acceptCallbackPtr = ckalloc(sizeof(AcceptCallback)); @@ -1646,7 +1645,7 @@ Tcl_SocketObjCmd( acceptCallbackPtr->interp = interp; chan = Tcl_OpenTcpServerEx(interp, port, host, flags, AcceptCallbackProc, - acceptCallbackPtr); + acceptCallbackPtr); if (chan == NULL) { Tcl_DecrRefCount(script); ckfree(acceptCallbackPtr); @@ -1670,7 +1669,13 @@ Tcl_SocketObjCmd( Tcl_CreateCloseHandler(chan, TcpServerCloseProc, acceptCallbackPtr); } else { - chan = Tcl_OpenTcpClient(interp, port, host, myaddr, myport, async); + int portNum; + + if (TclSockGetPort(interp, port, "tcp", &portNum) != TCL_OK) { + return TCL_ERROR; + } + + chan = Tcl_OpenTcpClient(interp, portNum, host, myaddr, myport, async); if (chan == NULL) { return TCL_ERROR; } diff --git a/unix/tclUnixSock.c b/unix/tclUnixSock.c index 187c157..8e97543 100644 --- a/unix/tclUnixSock.c +++ b/unix/tclUnixSock.c @@ -1422,7 +1422,7 @@ TclpMakeTcpClientChannelMode( Tcl_Channel 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, @@ -1430,7 +1430,7 @@ Tcl_OpenTcpServerEx( * clients. */ ClientData acceptProcData) /* Data for the callback. */ { - int status = 0, sock = -1, optvalue, chosenport; + int status = 0, sock = -1, optvalue, port, chosenport; struct addrinfo *addrlist = NULL, *addrPtr; /* socket address */ TcpState *statePtr = NULL; char channelName[SOCK_CHAN_LENGTH]; @@ -1476,6 +1476,11 @@ Tcl_OpenTcpServerEx( 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; diff --git a/win/tclWinSock.c b/win/tclWinSock.c index af22cf8..5e0d7c8 100644 --- a/win/tclWinSock.c +++ b/win/tclWinSock.c @@ -2037,7 +2037,7 @@ Tcl_MakeTcpClientChannel( Tcl_Channel 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, @@ -2053,7 +2053,7 @@ Tcl_OpenTcpServerEx( char channelName[SOCK_CHAN_LENGTH]; u_long flag = 1; /* Indicates nonblocking mode. */ const char *errorMsg = NULL; - int optvalue; + int optvalue, port; if (TclpHasSockets(interp) != TCL_OK) { return NULL; @@ -2073,6 +2073,11 @@ Tcl_OpenTcpServerEx( * Construct the addresses for each end of the socket. */ + if (TclSockGetPort(interp, service, "tcp", &port) != TCL_OK) { + errorMsg = "invalid port number"; + goto error; + } + if (!TclCreateSocketAddress(interp, &addrlist, myHost, port, 1, &errorMsg)) { goto error; } -- cgit v0.12 From b802a6e420e47172714baa9bc52fd03a5bb91f43 Mon Sep 17 00:00:00 2001 From: limeboy Date: Wed, 14 Dec 2016 15:52:21 +0000 Subject: Fix for the argument parsing phase in the [socket] command. --- generic/tclIOCmd.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/generic/tclIOCmd.c b/generic/tclIOCmd.c index 951fb5a..0eb8f99 100644 --- a/generic/tclIOCmd.c +++ b/generic/tclIOCmd.c @@ -1493,7 +1493,7 @@ Tcl_SocketObjCmd( SKT_SERVER }; int optionIndex, a, server = 0, myport = 0, async = 0, boolTmp; - unsigned int flags = 0; + unsigned int flags = TCL_TCPSERVER_REUSEADDR; const char *host, *port, *myaddr = NULL; Tcl_Obj *script = NULL; Tcl_Channel chan; @@ -1552,7 +1552,6 @@ Tcl_SocketObjCmd( return TCL_ERROR; } server = 1; - flags = TCL_TCPSERVER_REUSEADDR; a++; if (a >= objc) { Tcl_SetObjResult(interp, Tcl_NewStringObj( -- cgit v0.12 From a63b9bacf55e153bdb8f470146bf3056156b29e6 Mon Sep 17 00:00:00 2001 From: limeboy Date: Wed, 14 Dec 2016 15:59:43 +0000 Subject: Adjust OpenTcpServer for the latest changes to OpenTcpServerEx --- generic/tclIOSock.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/generic/tclIOSock.c b/generic/tclIOSock.c index b6e99ba..8ad268a 100644 --- a/generic/tclIOSock.c +++ b/generic/tclIOSock.c @@ -304,7 +304,11 @@ Tcl_Channel Tcl_OpenTcpServer(Tcl_Interp *interp, int port, const char *host, Tcl_TcpAcceptProc *acceptProc, ClientData callbackData) { - return Tcl_OpenTcpServerEx(interp, port, host, TCL_TCPSERVER_REUSEADDR, + char portbuf[TCL_INTEGER_SPACE]; + + TclFormatInt(portbuf, port); + + return Tcl_OpenTcpServerEx(interp, portbuf, host, TCL_TCPSERVER_REUSEADDR, acceptProc, callbackData); } -- cgit v0.12 From b8271467dc712230e7eecf2818f7232c2ebda8b4 Mon Sep 17 00:00:00 2001 From: limeboy Date: Wed, 14 Dec 2016 16:03:53 +0000 Subject: Update the documentation --- doc/OpenTcp.3 | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/doc/OpenTcp.3 b/doc/OpenTcp.3 index a39f6f6..10a4815 100644 --- a/doc/OpenTcp.3 +++ b/doc/OpenTcp.3 @@ -24,7 +24,7 @@ Tcl_Channel \fBTcl_OpenTcpServer\fR(\fIinterp, port, myaddr, proc, clientData\fR) .sp Tcl_Channel -\fBTcl_OpenTcpServerEx\fR(\fIinterp, port, myaddr, flags, proc, clientData\fR) +\fBTcl_OpenTcpServerEx\fR(\fIinterp, service, myaddr, flags, proc, clientData\fR) .sp .SH ARGUMENTS .AS Tcl_TcpAcceptProc clientData @@ -33,6 +33,9 @@ Tcl interpreter to use for error reporting. If non-NULL and an error occurs, an error message is left in the interpreter's result. .AP int port in A port number to connect to as a client or to listen on as a server. +.AP "const char" *service in +A string specifying the port number to connect to as a client or to listen on as + a server. .AP "const char" *host in A string specifying a host name or address for the remote end of the connection. .AP int myport in -- cgit v0.12 From d4daeaf6fdc234980b6b7e28b281185896c2dd11 Mon Sep 17 00:00:00 2001 From: limeboy Date: Tue, 20 Dec 2016 10:22:22 +0000 Subject: Correct the handling of -server and its options. --- generic/tclIOCmd.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/generic/tclIOCmd.c b/generic/tclIOCmd.c index 0eb8f99..fb37ff6 100644 --- a/generic/tclIOCmd.c +++ b/generic/tclIOCmd.c @@ -1493,7 +1493,7 @@ Tcl_SocketObjCmd( SKT_SERVER }; int optionIndex, a, server = 0, myport = 0, async = 0, boolTmp; - unsigned int flags = TCL_TCPSERVER_REUSEADDR; + unsigned int flags = 0; const char *host, *port, *myaddr = NULL; Tcl_Obj *script = NULL; Tcl_Channel chan; @@ -1552,6 +1552,7 @@ Tcl_SocketObjCmd( return TCL_ERROR; } server = 1; + flags |= TCL_TCPSERVER_REUSEADDR; a++; if (a >= objc) { Tcl_SetObjResult(interp, Tcl_NewStringObj( -- cgit v0.12 From bbe8ae73e2e5c693a20ea16c964949279ff83045 Mon Sep 17 00:00:00 2001 From: limeboy Date: Tue, 20 Dec 2016 10:56:53 +0000 Subject: Decouple the switch handling. --- generic/tclIOCmd.c | 25 ++++++++++--------------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/generic/tclIOCmd.c b/generic/tclIOCmd.c index fb37ff6..e64e31d 100644 --- a/generic/tclIOCmd.c +++ b/generic/tclIOCmd.c @@ -1492,7 +1492,8 @@ Tcl_SocketObjCmd( SKT_ASYNC, SKT_MYADDR, SKT_MYPORT, SKT_REUSEADDR, SKT_REUSEPORT, SKT_SERVER }; - int optionIndex, a, server = 0, myport = 0, async = 0, boolTmp; + int optionIndex, a, server = 0, myport = 0, async = 0, reusep = 0, + reusea = 0; unsigned int flags = 0; const char *host, *port, *myaddr = NULL; Tcl_Obj *script = NULL; @@ -1552,7 +1553,8 @@ Tcl_SocketObjCmd( return TCL_ERROR; } server = 1; - flags |= TCL_TCPSERVER_REUSEADDR; + /* [TIP#456] Set for backward-compatibility. */ + reusea = 1; a++; if (a >= objc) { Tcl_SetObjResult(interp, Tcl_NewStringObj( @@ -1568,14 +1570,9 @@ Tcl_SocketObjCmd( "no argument given for -reuseaddr option", -1)); return TCL_ERROR; } - if (Tcl_GetBooleanFromObj(interp, objv[a], &boolTmp) != TCL_OK) { + if (Tcl_GetBooleanFromObj(interp, objv[a], &reusea) != TCL_OK) { return TCL_ERROR; } - if (boolTmp) { - flags |= TCL_TCPSERVER_REUSEADDR; - } else { - flags &= ~TCL_TCPSERVER_REUSEADDR; - } break; case SKT_REUSEPORT: a++; @@ -1584,14 +1581,9 @@ Tcl_SocketObjCmd( "no argument given for -reuseport option", -1)); return TCL_ERROR; } - if (Tcl_GetBooleanFromObj(interp, objv[a], &boolTmp) != TCL_OK) { + if (Tcl_GetBooleanFromObj(interp, objv[a], &reusep) != TCL_OK) { return TCL_ERROR; } - if (boolTmp) { - flags |= TCL_TCPSERVER_REUSEPORT; - } else { - flags &= ~TCL_TCPSERVER_REUSEPORT; - } break; default: Tcl_Panic("Tcl_SocketObjCmd: bad option index to SocketOptions"); @@ -1621,13 +1613,16 @@ Tcl_SocketObjCmd( return TCL_ERROR; } - if (!server && flags != 0) { + if (!server && (reusea != 0 || reusep != 0)) { Tcl_SetObjResult(interp, Tcl_NewStringObj( "options -reuseaddr and -reuseport are only valid for servers", -1)); return TCL_ERROR; } + flags |= reusea ? TCL_TCPSERVER_REUSEADDR : 0; + flags |= reusep ? TCL_TCPSERVER_REUSEPORT : 0; + // All the arguments should have been parsed by now, 'a' points to the last // one, the port number. if (a != objc-1) { -- cgit v0.12 From 05119eaad1bb3205fa0a31abacf99d6ae33f20e1 Mon Sep 17 00:00:00 2001 From: "jan.nijtmans" Date: Tue, 20 Dec 2016 12:35:33 +0000 Subject: Make options -reuseaddr/-reuseport forbidden without -server, no matter the value being true or false. Some additional test-cases. --- generic/tclIOCmd.c | 12 +++++------- tests/socket.test | 12 ++++++++++++ 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/generic/tclIOCmd.c b/generic/tclIOCmd.c index e64e31d..682eaf4 100644 --- a/generic/tclIOCmd.c +++ b/generic/tclIOCmd.c @@ -1492,8 +1492,8 @@ Tcl_SocketObjCmd( SKT_ASYNC, SKT_MYADDR, SKT_MYPORT, SKT_REUSEADDR, SKT_REUSEPORT, SKT_SERVER }; - int optionIndex, a, server = 0, myport = 0, async = 0, reusep = 0, - reusea = 0; + int optionIndex, a, server = 0, myport = 0, async = 0, reusep = -1, + reusea = -1; unsigned int flags = 0; const char *host, *port, *myaddr = NULL; Tcl_Obj *script = NULL; @@ -1553,8 +1553,6 @@ Tcl_SocketObjCmd( return TCL_ERROR; } server = 1; - /* [TIP#456] Set for backward-compatibility. */ - reusea = 1; a++; if (a >= objc) { Tcl_SetObjResult(interp, Tcl_NewStringObj( @@ -1613,15 +1611,15 @@ Tcl_SocketObjCmd( return TCL_ERROR; } - if (!server && (reusea != 0 || reusep != 0)) { + if (!server && (reusea != -1 || reusep != -1)) { Tcl_SetObjResult(interp, Tcl_NewStringObj( "options -reuseaddr and -reuseport are only valid for servers", -1)); return TCL_ERROR; } - flags |= reusea ? TCL_TCPSERVER_REUSEADDR : 0; - flags |= reusep ? TCL_TCPSERVER_REUSEPORT : 0; + if (reusea!=0) flags |= TCL_TCPSERVER_REUSEADDR; + if (reusep==1) flags |= TCL_TCPSERVER_REUSEPORT; // All the arguments should have been parsed by now, 'a' points to the last // one, the port number. diff --git a/tests/socket.test b/tests/socket.test index c1076eb..80b0251 100644 --- a/tests/socket.test +++ b/tests/socket.test @@ -306,8 +306,20 @@ test socket_$af-1.15 {arg parsing for socket command} -constraints [list socket socket -reuseaddr yes 4242 } -returnCodes error -result {options -reuseaddr and -reuseport are only valid for servers} test socket_$af-1.16 {arg parsing for socket command} -constraints [list socket supported_$af] -body { + socket -reuseaddr no 4242 +} -returnCodes error -result {options -reuseaddr and -reuseport are only valid for servers} +test socket_$af-1.17 {arg parsing for socket command} -constraints [list socket supported_$af] -body { + socket -reuseaddr +} -returnCodes error -result {no argument given for -reuseaddr option} +test socket_$af-1.18 {arg parsing for socket command} -constraints [list socket supported_$af] -body { socket -reuseport yes 4242 } -returnCodes error -result {options -reuseaddr and -reuseport are only valid for servers} +test socket_$af-1.19 {arg parsing for socket command} -constraints [list socket supported_$af] -body { + socket -reuseport no 4242 +} -returnCodes error -result {options -reuseaddr and -reuseport are only valid for servers} +test socket_$af-1.20 {arg parsing for socket command} -constraints [list socket supported_$af] -body { + socket -reuseport +} -returnCodes error -result {no argument given for -reuseport option} set path(script) [makeFile {} script] -- cgit v0.12 From 7628e8f3871e65f729e55364626f5684b172bddc Mon Sep 17 00:00:00 2001 From: limeboy Date: Sun, 1 Jan 2017 22:19:26 +0000 Subject: Make the code slightly more pleasing to the eyes. --- generic/tclIOCmd.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/generic/tclIOCmd.c b/generic/tclIOCmd.c index 682eaf4..1bd3fe7 100644 --- a/generic/tclIOCmd.c +++ b/generic/tclIOCmd.c @@ -1618,8 +1618,16 @@ Tcl_SocketObjCmd( return TCL_ERROR; } - if (reusea!=0) flags |= TCL_TCPSERVER_REUSEADDR; - if (reusep==1) flags |= TCL_TCPSERVER_REUSEPORT; + // Set the options to their default value if the user didn't override their + // value. + if (reusep == -1) reusep = 0; + if (reusea == -1) reusea = 1; + + // Build the bitset with the flags values. + if (reusea) + flags |= TCL_TCPSERVER_REUSEADDR; + if (reusep) + flags |= TCL_TCPSERVER_REUSEPORT; // All the arguments should have been parsed by now, 'a' points to the last // one, the port number. -- cgit v0.12