From ff0c8839ffcef4c5177d37cde1d733a716b70883 Mon Sep 17 00:00:00 2001 From: patthoyts Date: Fri, 13 Feb 2004 21:38:15 +0000 Subject: [PATCH] * generic/udp_tcl.c: An extensive interface rewrite. We now have just a udp command and the socket is controlled via the fconfigure command. This is now a much more similar to the tcl socket command. For backwards compatability, we define the old functions in terms of the new interface. * * * INTERFACE INCOMPATABILITY * * * --- ChangeLog | 15 + configure | 4 +- configure.in | 4 +- generic/udp.tcl | 52 +++ generic/udp_tcl.c | 969 ++++++++++++++++++++++++++-------------------- generic/udp_tcl.h | 53 +-- tests/udp.test | 1 + win/makefile.vc | 4 +- win/tcludp.dsp | 12 + win/tcludp.rc | 10 +- 10 files changed, 661 insertions(+), 463 deletions(-) create mode 100644 generic/udp.tcl diff --git a/ChangeLog b/ChangeLog index df04053..e734115 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,20 @@ +2004-02-13 Pat Thoyts + + * generic/udp_tcl.c: An extensive interface rewrite. We now have + just a udp command and the socket is controlled via the fconfigure + command. This is now a much more similar to the tcl socket + command. For backwards compatability, we define the old functions + in terms of the new interface. + * * * INTERFACE INCOMPATABILITY * * * + + * all: Created branch dev-2 to hold udp 2.0 development + 2004-02-09 Pat Thoyts + * generic/udp_tcl.c: Followed up a suggestion from patch #828180 + to support address reuse using SO_REUSEADDR. Implemented as an + fconfigure option on the channel. + * generic/udp_tcl.c: Followed up a suggestion in patch #794072 to support udp broadcast packets. This can be queried/enabled using fconfigure $s -broadcast ?1|0? diff --git a/configure b/configure index db9c7e2..687e4bd 100755 --- a/configure +++ b/configure @@ -571,9 +571,9 @@ CONFIGDIR=${srcdir}/tclconfig PACKAGE=udp -MAJOR_VERSION=1 +MAJOR_VERSION=2 MINOR_VERSION=0 -PATCHLEVEL=6 +PATCHLEVEL=0 VERSION=${MAJOR_VERSION}.${MINOR_VERSION}.${PATCHLEVEL} NODOT_VERSION=${MAJOR_VERSION}${MINOR_VERSION} diff --git a/configure.in b/configure.in index 4355ec6..fff387d 100644 --- a/configure.in +++ b/configure.in @@ -37,9 +37,9 @@ dnl# AC_CONFIG_HEADER(config.h) PACKAGE=udp -MAJOR_VERSION=1 +MAJOR_VERSION=2 MINOR_VERSION=0 -PATCHLEVEL=6 +PATCHLEVEL=0 VERSION=${MAJOR_VERSION}.${MINOR_VERSION}.${PATCHLEVEL} NODOT_VERSION=${MAJOR_VERSION}${MINOR_VERSION} diff --git a/generic/udp.tcl b/generic/udp.tcl new file mode 100644 index 0000000..d83a9c3 --- /dev/null +++ b/generic/udp.tcl @@ -0,0 +1,52 @@ + +namespace eval udp { + namespace export udp_open udp_conf +} + +proc ::udp::udp_open {{port -1}} { + set s [udp] + if {$port != -1} { + fconfigure $s -sockname [list {} $port] + } + return $s +} + +# udpConf fileId [-mcastadd] [-mcastdrop] groupaddr +# udpConf fileId remotehost remoteport +# udpConf fileId [-myport] [-remote] [-peer] +proc ::udp::udp_conf {s args} { + if {[llength $args] < 1 || [llength $args] > 2} { + return -code error "wrong \# args: should be \ + udp_conf socket ?option ...?" + } + + while {[string match -* [set option [lindex $args 0]]]} { + switch -exact -- $option { + -myport { return [lindex [fconfigure $s -sockname] 1] } + -remote { return [fconfigure $s -remote] } + -peer { return [fconfigure $s -peer] } + -mcastadd { return [fconfigure $f -mcastadd [Pop args 1]] } + -mcastdrop { return [fconfigure $f -mcastdrop [Pop args 1]] } + -- { Pop args ; break } + default { + return -code error "bad option $option: must be one of\ + -mcastadd, -mcastdrop, -myport, -remote, or -peer" + } + } + Pop args + } + + if {[llength $args] == 2} { + return [fconfigure $s -remote $args] + } else { + return -code error "wrong \# args: should be\ + udp_conf socket ?option ...?" + } +} + +proc ::udp::Pop {varname {nth 0}} { + upvar $varname args + set r [lindex $args $nth] + set args [lreplace $args $nth $nth] + return $r +} diff --git a/generic/udp_tcl.c b/generic/udp_tcl.c index 1ef237b..187729a 100644 --- a/generic/udp_tcl.c +++ b/generic/udp_tcl.c @@ -46,9 +46,9 @@ typedef int socklen_t; #endif /* WIN32 */ #ifdef DEBUG -#define UDPTRACE udpTrace +#define UDPTRACE UdpTrace #else -#define UDPTRACE 1 ? ((void)0) : udpTrace +#define UDPTRACE 1 ? ((void)0) : UdpTrace #endif FILE *dbg; @@ -72,26 +72,31 @@ static Tcl_DriverGetOptionProc udpGetOption; * Tcl command procedures */ int Udp_CmdProc(ClientData, Tcl_Interp *, int, Tcl_Obj *CONST []); -int udpOpen(ClientData , Tcl_Interp *, int , CONST84 char * []); -int udpConf(ClientData , Tcl_Interp *, int , CONST84 char * []); -int udpPeek(ClientData , Tcl_Interp *, int , CONST84 char * []); +int UdpPeek(ClientData , Tcl_Interp *, int , CONST84 char * []); + +Tcl_Channel Tcl_OpenUdpSocket(Tcl_Interp *interp, CONST char *myaddr, + unsigned short myport); +Tcl_Channel Tcl_MakeUdpChannel(SOCKET sock); /* * internal functions */ -static void udpTrace(const char *format, ...); +static void UdpTrace(const char *format, ...); +static SOCKET UdpCreateSock(int protocol); static int udpGetService(Tcl_Interp *interp, const char *service, unsigned short *servicePort); +static int UdpGetProtocolFromObj(Tcl_Interp *interp, + Tcl_Obj *objPtr, int *resultPtr); /* * Windows specific functions */ #ifdef WIN32 -int UdpEventProc(Tcl_Event *evPtr, int flags); +static int UdpEventProc(Tcl_Event *evPtr, int flags); static void UDP_SetupProc(ClientData data, int flags); -void UDP_CheckProc(ClientData data, int flags); -int Udp_WinHasSockets(Tcl_Interp *interp); +static void UDP_CheckProc(ClientData data, int flags); +static int Udp_WinHasSockets(Tcl_Interp *interp); /* FIX ME - these should be part of a thread/package specific structure */ static HANDLE waitForSock; @@ -106,11 +111,7 @@ static UdpState *sockTail; * This structure describes the channel type for accessing UDP. */ static Tcl_ChannelType Udp_ChannelType = { -#ifdef SIPC_IPV6 - "udp6", /* Type name. */ -#else "udp", /* Type name. */ -#endif NULL, /* Set blocking/nonblocking behaviour. NULL'able */ udpClose, /* Close channel, clean instance data */ udpInput, /* Handle read request */ @@ -122,6 +123,15 @@ static Tcl_ChannelType Udp_ChannelType = { udpGetHandle, /* Get OS handle from the channel. */ }; +/* + * Package initialization: + * tcl_findLibrary basename version patch initScript enVarName varName + */ + +static char initScript[] = + "tcl_findLibrary udp " TCLUDP_PACKAGE_VERSION " " TCLUDP_PACKAGE_VERSION + " udp.tcl TCLUDP_LIBRARY udp_library"; + /* * ---------------------------------------------------------------------- * udpInit @@ -147,15 +157,15 @@ Udp_Init(Tcl_Interp *interp) Tcl_CreateEventSource(UDP_SetupProc, UDP_CheckProc, NULL); #endif - Tcl_CreateCommand(interp, "udp_open", udpOpen , - (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL); - Tcl_CreateCommand(interp, "udp_conf", udpConf , - (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL); - Tcl_CreateCommand(interp, "udp_peek", udpPeek , - (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL); + Tcl_CreateCommand(interp, "udp_peek", UdpPeek , + (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL); - r = Tcl_PkgProvide(interp, TCLUDP_PACKAGE_NAME, TCLUDP_PACKAGE_VERSION); - return r; + Tcl_CreateObjCommand(interp, "udp", Udp_CmdProc, + (ClientData)NULL, (Tcl_CmdDeleteProc *)NULL); + + Tcl_PkgProvide(interp, TCLUDP_PACKAGE_NAME, TCLUDP_PACKAGE_VERSION); + + return Tcl_Eval(interp, initScript); } /* @@ -163,126 +173,366 @@ Udp_Init(Tcl_Interp *interp) * Udp_CmdProc -- * Provide a user interface similar to the Tcl stock 'socket' command. * - * udp ?options? - * udp ?options? host port - * udp -server command ?options? port + * udp ?options? ?port? * + * Options (from socket): + * -myaddr addr which interface to create the socket on. + * -myport port specify a port to use (0 or omitted means system chooses). + * + * -reuseaddr is specfied, permit address reuse. * ---------------------------------------------------------------------- */ int -Udp_CmdProc(ClientData clientData, Tcl_Interp *interp, - int objc, Tcl_Obj *CONST objv[]) +Udp_CmdProc( + ClientData clientData, /* Not used */ + Tcl_Interp *interp, /* Current interpreter */ + int objc, /* Number of arguments */ + Tcl_Obj *CONST objv[]) /* Arguments */ { - Tcl_SetResult(interp, "E_NOTIMPL", TCL_STATIC); - return TCL_ERROR; + static CONST char *options[] = { + "-myaddr", "-myport", "-reuseaddr", "-protocol", "--", (char *)NULL + }; + enum { + OPT_MYADDR, OPT_MYPORT, OPT_REUSEADDR, OPT_PROTOCOL, OPT_END, + }; + CONST char *myaddr = NULL; + uint16_t myport = 0; + int protocol = AF_INET; + int reuseaddr = 0, index, i, r = TCL_OK; + + for (i = 1; r == TCL_OK && i < objc; i++) { + if (Tcl_GetIndexFromObj(interp, objv[i], options, + "option", 0, &index) != TCL_OK) { + return TCL_ERROR; + } + + switch (index) { + case OPT_MYADDR: + ++i; + if (objc == i) { + Tcl_WrongNumArgs(interp, 1, objv, "-myaddr address"); + r = TCL_ERROR; + } else { + myaddr = Tcl_GetString(objv[i]); + } + break; + + case OPT_MYPORT: + ++i; + if (objc == i) { + Tcl_WrongNumArgs(interp, 1, objv, "-myport port"); + r = TCL_ERROR; + } else { + r = udpGetService(interp, Tcl_GetString(objv[i]), &myport); + } + break; + + case OPT_REUSEADDR: + ++i; + if (objc == i) { + Tcl_WrongNumArgs(interp, 1, objv, "-reuseaddr boolean"); + r = TCL_ERROR; + } else { + r = Tcl_GetBooleanFromObj(interp, objv[i], &reuseaddr); + } + break; + + case OPT_PROTOCOL: + ++i; + if (objc == i) { + Tcl_WrongNumArgs(interp, 1, objv, "-protocol ipv4|ipv6"); + r = TCL_ERROR; + } else { + r = UdpGetProtocolFromObj(interp, objv[i], &protocol); + } + break; + + case OPT_END: + break; + + default: + Tcl_ResetResult(interp); + Tcl_AppendResult(interp, "bad option \"", + Tcl_GetString(objv[i]), "\": must be -myaddr, -myport, " + "-reuseaddr or --", (char *)NULL); + r = TCL_ERROR; + } + + if (i == OPT_END) { + break; + } + } + + if (r == TCL_OK) { + Tcl_Channel channel = NULL; + SOCKET sock = UdpCreateSock(protocol); + if (sock == INVALID_SOCKET) { + r = TCL_ERROR; + } else { + channel = Tcl_MakeUdpChannel(sock); + if (channel == NULL) { + closesocket(sock); + r = TCL_ERROR; + } + } + + if (r == TCL_OK) { + Tcl_RegisterChannel(interp, channel); + Tcl_SetObjResult(interp, + Tcl_NewStringObj(Tcl_GetChannelName(channel), -1)); + } + } + return r; } -/* - * Probably we should provide an equivalent to the C API for TCP. - * - * Tcl_Channel Tcl_OpenUdpClient(interp, port, host, myaddr, myport, async); - * Tcl_Channel Tcl_OpenUdpServer(interp, port, myaddr, proc, clientData); - * Tcl_Channel Tcl_MakeUdpClientChannel(sock); - */ + +static int +UdpGetProtocolFromObj(Tcl_Interp *interp, Tcl_Obj *objPtr, int *resultPtr) +{ + /* Odd values are AF_INET6 */ + static const char *protocolStrings[] = { + "ipv4", "ipv6", "inet", "inet6", "4", "6", NULL + }; + int af = 0; + int r = Tcl_GetIndexFromObj(interp, objPtr, + protocolStrings, "protocol", 0, &af); + if (r == TCL_OK) { + if (af & 1) { + *resultPtr = AF_INET6; + } else { + *resultPtr = AF_INET; + } + } + return r; +} /* * ---------------------------------------------------------------------- - * udpOpen -- + * UdpCreateSock -- + * + * Create a UDP socket and optionally specify the local address. * - * opens a UDP socket and addds the file descriptor to the tcl - * interpreter * ---------------------------------------------------------------------- */ + +SOCKET +UdpCreateSock(int protocol) +{ + SOCKET sock; + unsigned long nonblocking = 1; + + sock = socket(protocol, SOCK_DGRAM, 0); + + /* Make this a non-blocking socket */ + ioctlsocket(sock, FIONBIO, &nonblocking); + return sock; +} + +/* + * UdpGetAddressFromObj -- + * Convert a Tcl object into a sockaddr_in socket name. + * The Tcl object may be just a hostname, or a list + * made up of {hostname port} where port can be the + * service name or the port number. + * Returns: + * A standard tcl result. + */ + int -udpOpen(ClientData clientData, Tcl_Interp *interp, - int argc, CONST84 char * argv[]) +UdpGetAddressFromObj( + Tcl_Interp *interp, + Tcl_Obj *objPtr, + struct sockaddr *saddr) { - int sock; - char channelName[20]; - UdpState *statePtr; - uint16_t localport = 0; -#ifdef SIPC_IPV6 - struct sockaddr_in6 addr, sockaddr; -#else - struct sockaddr_in addr, sockaddr; -#endif - unsigned long status = 1; - int len; - - if (argc >= 2) { - if (udpGetService(interp, argv[1], &localport) != TCL_OK) - return TCL_ERROR; + int len, r = TCL_OK; + unsigned short port = 0; + CONST char *hostname = NULL; + struct hostent *hostent; + + r = Tcl_ListObjLength(interp, objPtr, &len); + if (r == TCL_OK) { + if (len < 1 || len > 2) { + Tcl_SetResult(interp, "wrong # args", TCL_STATIC); + r = TCL_ERROR; + } else { + Tcl_Obj *hostPtr, *portPtr; + + Tcl_ListObjIndex(interp, objPtr, 0, &hostPtr); + hostname = Tcl_GetString(hostPtr); + + if (len == 2) { + Tcl_ListObjIndex(interp, objPtr, 1, &portPtr); + r = udpGetService(interp, Tcl_GetString(portPtr), &port); + } + } } - - memset(channelName, 0, sizeof(channelName)); - -#ifdef SIPC_IPV6 - sock = socket(AF_INET6, SOCK_DGRAM, 0); -#else - sock = socket(AF_INET, SOCK_DGRAM, 0); -#endif - if (sock < 0) { - sprintf(errBuf,"%s","udp - socket"); - UDPTRACE("UDP error - socket\n"); - Tcl_AppendResult(interp, errBuf, (char *)NULL); - return TCL_ERROR; - } - memset(&addr, 0, sizeof(addr)); -#ifdef SIPC_IPV6 - addr.sin6_family = AF_INET6; - addr.sin6_port = localport; -#else - addr.sin_family = AF_INET; - addr.sin_addr.s_addr = 0; - addr.sin_port = localport; + if (r == TCL_OK) { + + if (saddr->sa_family == AF_INET6) { + + struct sockaddr_in6 * addr = (struct sockaddr_in6 *)saddr; + +#if HAVE_GETADDRINFO + char service[TCL_INTEGER_SPACE]; + struct addrinfo ai; + struct addrinfo *pai = 0; + + sprintf(service, "%u", ntohs(port)); + memset(&ai, 0, sizeof(ai)); + ai.ai_family = PF_INET6; + ai.ai_socktype = SOCK_DGRAM; + if (getaddrinfo(hostname, service, &ai, &pai) == 0) + memcpy(&addr->sin6_addr, pai->ai_addr, pai->ai_addrlen); + freeaddrinfo(pai); + +#elif HAVE_INET_PTON + int n, errnum; + n = inet_pton(AF_INET6, hostname, &addr->sin6_addr); + if (n <= 0) { + name = getipnodebyname(hostname, AF_INET6, AI_DEFAULT, &errnum); + } #endif - if (bind(sock,(struct sockaddr *)&addr, sizeof(addr)) < 0) { - sprintf(errBuf,"%s","udp - bind"); - UDPTRACE("UDP error - bind\n"); - Tcl_AppendResult(interp, errBuf, (char *)NULL); - return TCL_ERROR; + addr->sin6_family = AF_INET6; + addr->sin6_port = port; + + } else { + + struct sockaddr_in *addr = (struct sockaddr_in *)saddr; + addr->sin_family = AF_INET; + addr->sin_port = port; + if (hostname == NULL) { + addr->sin_addr.s_addr = INADDR_ANY; + } else { + addr->sin_addr.s_addr = inet_addr(hostname); + if (addr->sin_addr.s_addr == INADDR_NONE) { + hostent = gethostbyname(hostname); + if (hostent != NULL) { + memcpy(&addr->sin_addr, hostent->h_addr, + (size_t)hostent->h_length); + } else { + Tcl_SetResult(interp, "host not found", TCL_STATIC); + return TCL_ERROR; + } + } + } + } } - ioctlsocket(sock, FIONBIO, &status); + return r; +}; - if (localport == 0) { - len = sizeof(sockaddr); - getsockname(sock, (struct sockaddr *)&sockaddr, &len); -#ifdef SIPC_IPV6 - localport = sockaddr.sin6_port; -#else - localport = sockaddr.sin_port; -#endif +int +UdpGetObjFromAddress( + Tcl_Interp *interp, + struct sockaddr *saddr, + Tcl_Obj **objPtrPtr) +{ + struct sockaddr_in *addr = (struct sockaddr_in *)saddr; + Tcl_Obj *parts[2]; + + if (addr->sin_addr.s_addr == INADDR_NONE) { + parts[0] = Tcl_NewStringObj(NULL, 0); + } else { + parts[0] = Tcl_NewStringObj(inet_ntoa(addr->sin_addr), -1); } - - UDPTRACE("Open socket %d. Bind socket to port %d\n", - sock, ntohs(localport)); + parts[1] = Tcl_NewIntObj(ntohs(addr->sin_port)); + + *objPtrPtr = Tcl_NewListObj(2, parts); + return TCL_OK; +} + +/* + * ---------------------------------------------------------------------- + * + * Tcl_OpenUdpSocket -- + * + * Opens a UDP socket and wraps it in a Tcl channel. There is no + * difference between client and server UDP sockets except for the + * port number. If the port is 0 then the system will select a port + * for us, otherwise we use the specified port. + * + * Results: + * The newly created channel is returned, or NULL. The interpreters + * error message will be set in the event of failure. + * + * Side effects: + * Opens a socket and registers a new channel in interp. + * + * ---------------------------------------------------------------------- + */ + +Tcl_Channel +Tcl_OpenUdpSocket( + Tcl_Interp *interp, /* May be NULL - used for returning errors */ + CONST char *myaddr, /* Client-side address */ + unsigned short myport) /* Client-side port in network byte order */ +{ + /* FIX ME: can probably tell from the address info what protocol */ + Tcl_Channel chan = NULL; + SOCKET sock = UdpCreateSock(AF_INET); + if (sock != INVALID_SOCKET) { + chan = Tcl_MakeUdpChannel(sock); + } + return chan; +} + +/* + * ---------------------------------------------------------------------- + * + * Tcl_MakeUdpChannel -- + * + * Creates a Tcl_Channel from an existing UDP socket. + * + * Results: + * Wraps a system socket in a Tcl_Channel structure. + * + * Side effects: + * Under Win32, the channel instance data is appended to the list + * of available listeners. For all platforms, a new channel is + * registered. + * + * ---------------------------------------------------------------------- + */ +Tcl_Channel +Tcl_MakeUdpChannel(SOCKET sock) +{ + UdpState *statePtr; + char channelName[TCL_INTEGER_SPACE + 5]; + sockaddr_t name; + int len = sizeof(sockaddr_t); + statePtr = (UdpState *) ckalloc((unsigned) sizeof(UdpState)); memset(statePtr, 0, sizeof(UdpState)); statePtr->sock = sock; + + getsockname(sock, (struct sockaddr *)&name, &len); + if (name.ss_family == AF_INET) { + statePtr->saddr_local.ipv4.sin_addr.s_addr = INADDR_NONE; + statePtr->saddr_remote.ipv4.sin_addr.s_addr = INADDR_NONE; + statePtr->saddr_peer.ipv4.sin_addr.s_addr = INADDR_NONE; + } + sprintf(channelName, "sock%d", statePtr->sock); - statePtr->channel = Tcl_CreateChannel(&Udp_ChannelType, channelName, - (ClientData) statePtr, - (TCL_READABLE | TCL_WRITABLE | TCL_MODE_NONBLOCKING)); + statePtr->channel = Tcl_CreateChannel(&Udp_ChannelType, + channelName, (ClientData) statePtr, + (TCL_READABLE | TCL_WRITABLE | TCL_MODE_NONBLOCKING)); statePtr->doread = 1; - statePtr->localport = localport; - Tcl_RegisterChannel(interp, statePtr->channel); + + len = sizeof(sockaddr_t); + getsockname(sock, (struct sockaddr *)&statePtr->saddr_local, &len); + #ifdef WIN32 statePtr->threadId = Tcl_GetCurrentThread(); statePtr->packetNum = 0; statePtr->next = NULL; statePtr->packets = NULL; statePtr->packetsTail = NULL; -#endif - /* Tcl_SetChannelOption(interp, statePtr->channel, "-blocking", "0"); */ - Tcl_AppendResult(interp, channelName, (char *)NULL); -#ifdef WIN32 + WaitForSingleObject(sockListLock, INFINITE); if (sockList == NULL) { - sockList = statePtr; - sockTail = statePtr; + sockList = sockTail = statePtr; } else { sockTail->next = statePtr; sockTail = statePtr; @@ -292,160 +542,18 @@ udpOpen(ClientData clientData, Tcl_Interp *interp, SetEvent(sockListLock); SetEvent(waitForSock); #endif - return TCL_OK; -} -/* - * ---------------------------------------------------------------------- - * udpConf -- - * ---------------------------------------------------------------------- - */ -int -udpConf(ClientData clientData, Tcl_Interp *interp, - int argc, CONST84 char * argv[]) -{ - Tcl_Channel chan; - UdpState *statePtr; - char *result; - char buf[128]; - struct hostent *name; - struct ip_mreq mreq; - struct sockaddr_in maddr; - int sock; - - if (argc != 4 && argc != 3) { - result = "udpConf fileId [-mcastadd] [-mcastdrop] groupaddr | udpConf fileId remotehost remoteport | udpConf fileId [-myport] [-remote] [-peer] [-broadcast]"; - Tcl_SetResult (interp, result, NULL); - return TCL_ERROR; - } - chan = Tcl_GetChannel(interp, (char *)argv[1], NULL); - if (chan == (Tcl_Channel) NULL) { - return TCL_ERROR; - } - statePtr = (UdpState *) Tcl_GetChannelInstanceData(chan); - sock = statePtr->sock; - - if (argc == 3) { - if (!strcmp(argv[2], "-myport")) { - sprintf(buf, "%d", ntohs(statePtr->localport)); - Tcl_AppendResult(interp, buf, (char *)NULL); - } else if (!strcmp(argv[2], "-remote")) { - if (statePtr->remotehost && *statePtr->remotehost) { - sprintf(buf, "%s", statePtr->remotehost); - Tcl_AppendResult(interp, buf, (char *)NULL); - sprintf(buf, "%d", ntohs(statePtr->remoteport)); - Tcl_AppendElement(interp, buf); - } - } else if (!strcmp(argv[2], "-peer")) { - if (statePtr->peerhost && *statePtr->peerhost) { - sprintf(buf, "%s", statePtr->peerhost); - Tcl_AppendResult(interp, buf, (char *)NULL); - sprintf(buf, "%d", statePtr->peerport); - Tcl_AppendElement(interp, buf); - } - } else if (!strcmp(argv[2], "-broadcast")) { - int tmp = 1; - socklen_t optlen = sizeof(int); - if (getsockopt(statePtr->sock, SOL_SOCKET, SO_BROADCAST, - (char *)&tmp, &optlen)) { - sprintf(errBuf, "%s", "udp - setsockopt"); - UDPTRACE("UDP error - setsockopt\n"); - Tcl_AppendResult(interp, errBuf, (char *)NULL); - return TCL_ERROR; - } else { - Tcl_SetObjResult(interp, Tcl_NewIntObj(tmp)); - return TCL_OK; - } - } else { - result = "udpConf fileId [-mcastadd] [-mcastdrop] groupaddr | udpConf fileId remotehost remoteport | udpConf fileId [-myport] [-remote] [-peer]"; - Tcl_SetResult (interp, result, NULL); - return TCL_ERROR; - } - return TCL_OK; - } else if (argc == 4) { - if (!strcmp(argv[2], "-mcastadd")) { - if (strlen(argv[3]) >= sizeof(statePtr->remotehost)) { - result = "hostname too long"; - Tcl_SetResult (interp, result, NULL); - return TCL_ERROR; - } - mreq.imr_multiaddr.s_addr = inet_addr(argv[3]); - if (mreq.imr_multiaddr.s_addr == -1) { - name = gethostbyname(argv[3]); - if (name == NULL) { - UDPTRACE("UDP error - gethostbyname"); - return TCL_ERROR; - } - memcpy(&mreq.imr_multiaddr.s_addr, name->h_addr, sizeof(mreq.imr_multiaddr.s_addr)); - } - mreq.imr_interface.s_addr = htonl(INADDR_ANY); - if (setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, (const char*)&mreq, sizeof(mreq)) < 0) { - UDPTRACE("UDP error - setsockopt - IP_ADD_MEMBERSHIP"); - return TCL_ERROR; - } - maddr.sin_addr.s_addr = htonl(INADDR_ANY); - return TCL_OK; - } else if (!strcmp(argv[2], "-mcastdrop")) { - if (strlen(argv[3]) >= sizeof(statePtr->remotehost)) { - result = "hostname too long"; - Tcl_SetResult (interp, result, NULL); - return TCL_ERROR; - } - mreq.imr_multiaddr.s_addr = inet_addr(argv[3]); - if (mreq.imr_multiaddr.s_addr == -1) { - name = gethostbyname(argv[3]); - if (name == NULL) { - UDPTRACE("UDP error - gethostbyname"); - return TCL_ERROR; - } - memcpy(&mreq.imr_multiaddr.s_addr, name->h_addr, sizeof(mreq.imr_multiaddr.s_addr)); - } - mreq.imr_interface.s_addr = htonl(INADDR_ANY); - if (setsockopt(sock, IPPROTO_IP, IP_DROP_MEMBERSHIP, (const char*)&mreq, sizeof(mreq)) < 0) { - UDPTRACE("UDP error - setsockopt - IP_DROP_MEMBERSHIP"); - return TCL_ERROR; - } - return TCL_OK; - } else if (!strcmp(argv[2], "-broadcast")) { - socklen_t optlen = sizeof(int); - int tmp = 0; - int r = Tcl_GetInt(interp, argv[3], &tmp); - if (r == TCL_OK) { - if (setsockopt(statePtr->sock, SOL_SOCKET, SO_BROADCAST, - (const char *)&tmp, optlen)) { - sprintf(errBuf, "%s", "udp - setsockopt"); - UDPTRACE("UDP error - setsockopt\n"); - Tcl_AppendResult(interp, errBuf, (char *)NULL); - r = TCL_ERROR; - } else { - Tcl_SetObjResult(interp, Tcl_NewIntObj(tmp)); - } - } - return r; - } else { - if (strlen(argv[2]) >= sizeof(statePtr->remotehost)) { - result = "hostname too long"; - Tcl_SetResult (interp, result, NULL); - return TCL_ERROR; - } - strcpy(statePtr->remotehost, argv[2]); - return udpGetService(interp, argv[3], &(statePtr->remoteport)); - } - } else { - result = "udpConf fileId [-mcastadd] [-mcastdrop] groupaddr | udpConf fileId remotehost remoteport | udpConf fileId [-myport] [-remote] [-peer]"; - Tcl_SetResult (interp, result, NULL); - return TCL_ERROR; - } + return statePtr->channel; } /* * ---------------------------------------------------------------------- - * udpPeek -- + * UdpPeek -- * peek some data and set the peer information * ---------------------------------------------------------------------- */ int -udpPeek(ClientData clientData, Tcl_Interp *interp, +UdpPeek(ClientData clientData, Tcl_Interp *interp, int argc, CONST84 char * argv[]) { #ifndef WIN32 @@ -483,11 +591,9 @@ udpPeek(ClientData clientData, Tcl_Interp *interp, return TCL_ERROR; } #ifdef SIPC_IPV6 - remotehost = (char *)inet_ntop(AF_INET6, &recvaddr.sin_addr, statePtr->peerhost, sizeof(statePtr->peerhost) ); - statePtr->peerport = ntohs(recvaddr.sin_port); + memcpy(&statePtr->saddr6_peer, &recvaddr, sizeof(recvaddr)); #else - strcpy(statePtr->peerhost, (char *)inet_ntoa(recvaddr.sin_addr)); - statePtr->peerport = ntohs(recvaddr.sin_port); + memcpy(&statePtr->saddr_peer, &recvaddr, sizeof(recvaddr)); #endif message[16]='\0'; @@ -533,7 +639,7 @@ UDP_SetupProc(ClientData data, int flags) UdpState *statePtr; Tcl_Time blockTime = { 0, 0 }; - UDPTRACE("setupProc\n"); + /* UDPTRACE("setupProc\n"); */ if (!(flags & TCL_FILE_EVENTS)) { return; @@ -563,17 +669,9 @@ UDP_CheckProc(ClientData data, int flags) int actual_size, socksize; int buffer_size = MAXBUFFERSIZE; char *message; -#ifdef SIPC_IPV6 - char number[128], *remotehost; - struct sockaddr_in6 recvaddr; -#else - char number[32]; - struct sockaddr_in recvaddr; -#endif + sockaddr_t recvaddr; PacketList *p; - UDPTRACE("checkProc\n"); - /* synchronized */ WaitForSingleObject(sockListLock, INFINITE); @@ -582,13 +680,8 @@ UDP_CheckProc(ClientData data, int flags) UDPTRACE("UDP_CheckProc\n"); /* Read the data from socket and put it into statePtr */ socksize = sizeof(recvaddr); -#ifdef SIPC_IPV6 - memset(number, 0, 128); -#else - memset(number, 0, 32); -#endif memset(&recvaddr, 0, socksize); - + message = (char *)calloc(1, MAXBUFFERSIZE); if (message == NULL) { UDPTRACE("calloc error\n"); @@ -601,28 +694,14 @@ UDP_CheckProc(ClientData data, int flags) if (actual_size < 0) { UDPTRACE("UDP error - recvfrom %d\n", statePtr->sock); - ckfree(message); + free(message); } else { p = (PacketList *)calloc(1, sizeof(struct PacketList)); p->message = message; p->actual_size = actual_size; -#ifdef SIPC_IPV6 - remotehost = (char *)inet_ntoa(AF_INET6, &recvaddr.sin6_addr, p->r_host, sizeof(p->r_host)); - p->r_port = ntohs(recvaddr.sin6_port); -#else - strcpy(p->r_host, (char *)inet_ntoa(recvaddr.sin_addr)); - p->r_port = ntohs(recvaddr.sin_port); -#endif + memcpy(&p->peer, &recvaddr, sizeof(recvaddr)); p->next = NULL; -#ifdef SIPC_IPV6 - remotehost = (char *)inet_ntoa(AF_INET6, &recvaddr.sin6_addr, statePtr->peerhost, sizeof(statePtr->peerhost) ); - statePtr->peerport = ntohs(recvaddr.sin6_port); -#else - strcpy(statePtr->peerhost, (char *)inet_ntoa(recvaddr.sin_addr)); - statePtr->peerport = ntohs(recvaddr.sin_port); -#endif - if (statePtr->packets == NULL) { statePtr->packets = p; statePtr->packetsTail = p; @@ -631,9 +710,9 @@ UDP_CheckProc(ClientData data, int flags) statePtr->packetsTail = p; } - UDPTRACE("Received %d bytes from %s:%d through %d\n", - p->actual_size, p->r_host, p->r_port, statePtr->sock); - UDPTRACE("%s\n", p->message); + UDPTRACE("Received %d bytes from through %d\n", + p->actual_size, statePtr->sock); + //UDPTRACE("%s\n", p->message); } statePtr->packetNum--; @@ -667,7 +746,7 @@ InitSockets() * Load the socket DLL and initialize the function table. */ - if (WSAStartup(0x0101, &wsaData)) + if (WSAStartup(0x0002, &wsaData)) return 0; return 1; @@ -709,15 +788,15 @@ SocketThread(LPVOID arg) /* set each socket for select */ for (statePtr = sockList; statePtr != NULL; statePtr=statePtr->next) { - FD_SET(statePtr->sock, &readfds); - UDPTRACE("SET sock %d\n", statePtr->sock); + FD_SET((SOCKET)statePtr->sock, &readfds); + /* UDPTRACE("SET sock %d\n", statePtr->sock); */ } SetEvent(sockListLock); - UDPTRACE("Wait for select\n"); + /* UDPTRACE("Wait for select\n"); */ /* block here */ found = select(0, &readfds, NULL, NULL, &timeout); - UDPTRACE("select end\n"); + /* UDPTRACE("select end\n"); */ if (found <= 0) { /* We closed the socket during select or time out */ @@ -794,7 +873,7 @@ Udp_WinHasSockets(Tcl_Interp *interp) sockList = NULL; sockTail = NULL; - waitForSock = CreateEvent(NULL, FALSE, FALSE, NULL); + waitForSock = CreateEvent(NULL, FALSE, FALSE, NULL); waitSockRead = CreateEvent(NULL, FALSE, FALSE, NULL); sockListLock = CreateEvent(NULL, FALSE, TRUE, NULL); @@ -840,32 +919,32 @@ udpClose(ClientData instanceData, Tcl_Interp *interp) int errorCode = 0; UdpState *statePtr = (UdpState *) instanceData; #ifdef WIN32 - UdpState *statePre, *searchPtr; - - WaitForSingleObject(sockListLock, INFINITE); + UdpState *p, *q; #endif /* ! WIN32 */ sock = statePtr->sock; #ifdef WIN32 /* remove the statePtr from the list */ - for (searchPtr = sockList, statePre = sockList; - searchPtr != NULL; - statePre=statePtr, searchPtr=searchPtr->next) { - if (searchPtr->sock == sock) { - UDPTRACE("Remove %d from the list\n", sock); - if (searchPtr == sockList) { - sockList = sockList->next; - } else { - statePre->next = searchPtr->next; - if (sockTail == searchPtr) - sockTail = statePre; - } - } + WaitForSingleObject(sockListLock, INFINITE); + + for (p = q = sockList; p != NULL; q = p, p = p->next) { + if (p->sock == sock) { + UDPTRACE("Remove sock%d from list\n", sock); + if (p == sockList) { + sockList = q = p->next; + } else { + q->next = p->next; + } + if (p == sockTail) { + sockTail = q; + } + break; + } } + #endif /* ! WIN32 */ - Tcl_UnregisterChannel(interp, statePtr->channel); if (closesocket(sock) < 0) { errorCode = errno; } @@ -941,14 +1020,8 @@ udpOutput(ClientData instanceData, CONST84 char *buf, int toWrite, int *errorCod { UdpState *statePtr = (UdpState *) instanceData; int written; - int socksize; - struct hostent *name; -#ifdef SIPC_IPV6 - struct sockaddr_in6 sendaddr; - int n, errnum; -#else - struct sockaddr_in sendaddr; -#endif + int socksize = sizeof(sockaddr_t); + sockaddr_t name; *errorCode = 0; errno = 0; @@ -957,43 +1030,29 @@ udpOutput(ClientData instanceData, CONST84 char *buf, int toWrite, int *errorCod UDPTRACE("UDP error - MAXBUFFERSIZE"); return -1; } - socksize = sizeof(sendaddr); - memset(&sendaddr, 0, socksize); - + + getsockname(statePtr->sock, (struct sockaddr *)&name, &socksize); + + if ((name.ss_family == AF_INET + && statePtr->saddr_remote.ipv4.sin_addr.s_addr == INADDR_NONE) #ifdef SIPC_IPV6 - n = inet_pton(AF_INET6, statePtr->remotehost, &sendaddr.sin6_addr); - if (n <= 0) { - name = getipnodebyname(statePtr->remotehost, AF_INET6, - AI_DEFAULT, &errnum); -#else - sendaddr.sin_addr.s_addr = inet_addr(statePtr->remotehost); - if (sendaddr.sin_addr.s_addr == -1) { - name = gethostbyname(statePtr->remotehost); + || (name.ss_family == AF_INET6 + && IN6_IS_ADDR_UNSPECIFIED(&statePtr->saddr_remote.ipv6.sin6_addr)) #endif - if (name == NULL) { - UDPTRACE("UDP error - gethostbyname"); - return -1; - } -#ifdef SIPC_IPV6 - memcpy(&sendaddr.sin6_addr, name->h_addr, sizeof(sendaddr.sin6_addr)); - } - sendaddr.sin6_family = AF_INET6; - sendaddr.sin6_port = statePtr->remoteport; -#else - memcpy(&sendaddr.sin_addr, name->h_addr, sizeof(sendaddr.sin_addr)); + ) { + UDPTRACE("UDP error - no host set"); + return -1; } - sendaddr.sin_family = AF_INET; - sendaddr.sin_port = statePtr->remoteport; -#endif + + socksize = sizeof(statePtr->saddr_remote); written = sendto(statePtr->sock, buf, toWrite, 0, - (struct sockaddr *)&sendaddr, socksize); + (const struct sockaddr *)&statePtr->saddr_remote, socksize); if (written < 0) { UDPTRACE("UDP error - sendto"); return -1; } - - UDPTRACE("Send %d to %s:%d through %d\n", written, statePtr->remotehost, - ntohs(statePtr->remoteport), statePtr->sock); + + UDPTRACE("Send %d through socket %u\n", written, statePtr->sock); return written; } @@ -1017,13 +1076,8 @@ udpInput(ClientData instanceData, char *buf, int bufSize, int *errorCode) int buffer_size = MAXBUFFERSIZE; char *remotehost; int sock = statePtr->sock; -#ifdef SIPC_IPV6 char number[128]; - struct sockaddr_in6 recvaddr; -#else /* ! SIPC_IPV6 */ - char number[32]; - struct sockaddr_in recvaddr; -#endif /* ! SIPC_IPV6 */ + sockaddr_t recvaddr; #endif /* ! WIN32 */ UDPTRACE("In udpInput\n"); @@ -1062,18 +1116,13 @@ udpInput(ClientData instanceData, char *buf, int bufSize, int *errorCode) free((char *) packets->message); UDPTRACE("udp_recv message\n%s", buf); bufSize = packets->actual_size; - strcpy(statePtr->peerhost, packets->r_host); - statePtr->peerport = packets->r_port; + memcpy(&statePtr->saddr_peer, &packets->peer, sizeof(packets->peer)); statePtr->packets = packets->next; free((char *) packets); bytesRead = bufSize; #else /* ! WIN32 */ socksize = sizeof(recvaddr); -#ifdef SIPC_IPV6 memset(number, 0, 128); -#else - memset(number, 0, 32); -#endif memset(&recvaddr, 0, socksize); bytesRead = recvfrom(sock, buf, buffer_size, 0, @@ -1083,20 +1132,18 @@ udpInput(ClientData instanceData, char *buf, int bufSize, int *errorCode) *errorCode = errno; return -1; } + memcpy(&statePtr->saddr_peer, &recvaddr, sizeof(recvaddr)); #ifdef SIPC_IPV6 - remotehost = (char *)inet_ntop(AF_INET6, - &recvaddr.sin6_addr, statePtr->peerhost, - sizeof(statePtr->peerhost)); - port = ntohs(recvaddr.sin6_port); + inet_ntop(recvaddr.sin_family, &statePtr->saddr_peer.sin_addr, + number, 128); #else - remotehost = (char *)inet_ntoa(recvaddr.sin_addr); - port = ntohs(recvaddr.sin_port); - strcpy(statePtr->peerhost, remotehost); + inet_ntop(statePtr->saddr_peer.sin_family, &statePtr->saddr_peer.sin_addr, + number, 128); #endif - - UDPTRACE("remotehost: %s:%d\n", remotehost, port); - statePtr->peerport = port; + + UDPTRACE("remotehost: %s:%d\n", number, ntohs(recvaddr.sin6_port)); + #endif /* ! WIN32 */ /* we don't want to return anything next time */ @@ -1115,6 +1162,42 @@ udpInput(ClientData instanceData, char *buf, int bufSize, int *errorCode) return -1; } +/* + * ---------------------------------------------------------------------- + * + * UdpMulticast -- + * + * Action should be IP_ADD_MEMBERSHIP | IP_DROP_MEMBERSHIP + * + */ +int +UdpMulticast(Tcl_Interp *interp, + SOCKET sock, CONST84 char *grp, int action) +{ + struct ip_mreq mreq; + struct hostent *name; + + memset(&mreq, 0, sizeof(mreq)); + + mreq.imr_multiaddr.s_addr = inet_addr(grp); + if (mreq.imr_multiaddr.s_addr == -1) { + name = gethostbyname(grp); + if (name == NULL) { + Tcl_SetResult(interp, "invalid hostname", TCL_STATIC); + return TCL_ERROR; + } + memcpy(&mreq.imr_multiaddr.s_addr, name->h_addr, + sizeof(mreq.imr_multiaddr)); + } + mreq.imr_interface.s_addr = INADDR_ANY; + if (setsockopt(sock, IPPROTO_IP, action, + (const char*)&mreq, sizeof(mreq)) < 0) { + Tcl_SetResult(interp, "error changind multicast group", TCL_STATIC); + return TCL_ERROR; + } + return TCL_OK; +} + /* * ---------------------------------------------------------------------- * udpGetOption -- @@ -1125,23 +1208,22 @@ udpGetOption(ClientData instanceData, Tcl_Interp *interp, CONST84 char *optionName, Tcl_DString *optionValue) { UdpState *statePtr = (UdpState *)instanceData; - CONST84 char * options = "myport remote peer mcastadd mcastdrop"; + CONST84 char * options = + "sockname remote peer broadcast reuseaddr"; int r = TCL_OK; if (optionName == NULL) { - Tcl_DStringAppend(optionValue, " -myport ", -1); - udpGetOption(instanceData, interp, "-myport", optionValue); + Tcl_DStringAppend(optionValue, " -sockname ", -1); + udpGetOption(instanceData, interp, "-sockname", optionValue); Tcl_DStringAppend(optionValue, " -remote ", -1); udpGetOption(instanceData, interp, "-remote", optionValue); Tcl_DStringAppend(optionValue, " -peer ", -1); udpGetOption(instanceData, interp, "-peer", optionValue); - Tcl_DStringAppend(optionValue, " -mcastadd ", -1); - udpGetOption(instanceData, interp, "-mcastadd", optionValue); - Tcl_DStringAppend(optionValue, " -mcastdrop ", -1); - udpGetOption(instanceData, interp, "-mcastdrop", optionValue); Tcl_DStringAppend(optionValue, " -broadcast ", -1); udpGetOption(instanceData, interp, "-broadcast", optionValue); + Tcl_DStringAppend(optionValue, " -reuseaddr ", -1); + udpGetOption(instanceData, interp, "-reuseaddr", optionValue); } else { @@ -1149,38 +1231,44 @@ udpGetOption(ClientData instanceData, Tcl_Interp *interp, Tcl_DStringInit(&ds); Tcl_DStringInit(&dsInt); - if (!strcmp("-myport", optionName)) { + if (!strcmp("-sockname", optionName)) { - Tcl_DStringSetLength(&ds, TCL_INTEGER_SPACE); - sprintf(Tcl_DStringValue(&ds), "%u", ntohs(statePtr->localport)); + Tcl_Obj *nameObj; + UdpGetObjFromAddress(interp, (struct sockaddr *)&statePtr->saddr_local, &nameObj); + Tcl_DStringAppend(&ds, Tcl_GetString(nameObj), -1); } else if (!strcmp("-remote", optionName)) { - Tcl_DStringSetLength(&dsInt, TCL_INTEGER_SPACE); - sprintf(Tcl_DStringValue(&dsInt), "%u", - ntohs(statePtr->remoteport)); - Tcl_DStringAppendElement(&ds, statePtr->remotehost); - Tcl_DStringAppendElement(&ds, Tcl_DStringValue(&dsInt)); + Tcl_Obj *nameObj; + UdpGetObjFromAddress(interp, (struct sockaddr *)&statePtr->saddr_remote, &nameObj); + Tcl_DStringAppend(&ds, Tcl_GetString(nameObj), -1); } else if (!strcmp("-peer", optionName)) { - Tcl_DStringSetLength(&dsInt, TCL_INTEGER_SPACE); - sprintf(Tcl_DStringValue(&dsInt), "%u", statePtr->peerport); - Tcl_DStringAppendElement(&ds, statePtr->peerhost); - Tcl_DStringAppendElement(&ds, Tcl_DStringValue(&dsInt)); + Tcl_Obj *nameObj; + UdpGetObjFromAddress(interp, (struct sockaddr *)&statePtr->saddr_peer, &nameObj); + Tcl_DStringAppend(&ds, Tcl_GetString(nameObj), -1); - } else if (!strcmp("-mcastadd", optionName)) { - ; - } else if (!strcmp("-mcastdrop", optionName)) { - ; } else if (!strcmp("-broadcast", optionName)) { int tmp = 1; socklen_t optlen = sizeof(int); if (getsockopt(statePtr->sock, SOL_SOCKET, SO_BROADCAST, (char *)&tmp, &optlen)) { - UDPTRACE("UDP error - setsockopt\n"); - Tcl_SetResult(interp, "error in setsockopt", TCL_STATIC); + Tcl_SetResult(interp, "error in setsockopt SO_BROADCAST", TCL_STATIC); + r = TCL_ERROR; + } else { + Tcl_DStringSetLength(&ds, TCL_INTEGER_SPACE); + sprintf(Tcl_DStringValue(&ds), "%d", tmp); + } + + } else if (!strcmp("-reuseaddr", optionName)) { + + int tmp = 1; + socklen_t optlen = sizeof(int); + if (getsockopt(statePtr->sock, SOL_SOCKET, SO_REUSEADDR, + (char *)&tmp, &optlen)) { + Tcl_SetResult(interp, "error in setsockopt SO_REUSEADDR", TCL_STATIC); r = TCL_ERROR; } else { Tcl_DStringSetLength(&ds, TCL_INTEGER_SPACE); @@ -1214,43 +1302,47 @@ udpSetOption(ClientData instanceData, Tcl_Interp *interp, CONST84 char *optionName, CONST84 char *newValue) { UdpState *statePtr = (UdpState *)instanceData; - CONST84 char * options = "remote mcastadd mcastdrop broadcast"; + CONST84 char * options = "sockname remote mcastadd mcastdrop broadcast reuseaddr"; int r = TCL_OK; - if (!strcmp("-remote", optionName)) { - - Tcl_Obj *valPtr; - int len; - - valPtr = Tcl_NewStringObj(newValue, -1); - r = Tcl_ListObjLength(interp, valPtr, &len); - if (r == TCL_OK) { - if (len < 1 || len > 2) { - Tcl_SetResult(interp, "wrong # args", TCL_STATIC); - r = TCL_ERROR; - } else { - Tcl_Obj *hostPtr, *portPtr; - - Tcl_ListObjIndex(interp, valPtr, 0, &hostPtr); - strcpy(statePtr->remotehost, Tcl_GetString(hostPtr)); - - if (len == 2) { - Tcl_ListObjIndex(interp, valPtr, 1, &portPtr); - r = udpGetService(interp, Tcl_GetString(portPtr), - &(statePtr->remoteport)); - } - } - } + if (!strcmp("-sockname", optionName)) { + + struct sockaddr_in addr; + memset(&addr, 0, sizeof(struct sockaddr_in)); + + r = UdpGetAddressFromObj(interp, Tcl_NewStringObj(newValue, -1), + (struct sockaddr *)&addr); + if (r == TCL_OK) { + int e = bind(statePtr->sock, (struct sockaddr *)&addr, + sizeof(struct sockaddr_in)); + if (e < 0) { + Tcl_SetErrno(e); + Tcl_SetResult(interp, "bind error", TCL_STATIC); + r = TCL_ERROR; + } else { + int len = sizeof(statePtr->saddr_local); + getsockname(statePtr->sock, (struct sockaddr *)&statePtr->saddr_local, &len); + } + } + + } else if (!strcmp("-remote", optionName)) { + + struct sockaddr_in addr; + memset(&addr, 0, sizeof(struct sockaddr_in)); + + r = UdpGetAddressFromObj(interp, Tcl_NewStringObj(newValue, -1), + (struct sockaddr *)&addr); + if (r == TCL_OK) { + memcpy(&statePtr->saddr_remote, &addr, sizeof(addr)); + } } else if (!strcmp("-mcastadd", optionName)) { - Tcl_SetResult(interp, "E_NOTIMPL", TCL_STATIC); - r = TCL_ERROR; + r = UdpMulticast(interp, statePtr->sock, newValue, IP_ADD_MEMBERSHIP); } else if (!strcmp("-mcastdrop", optionName)) { - Tcl_SetResult(interp, "E_NOTIMPL", TCL_STATIC); - r = TCL_ERROR; + r = UdpMulticast(interp, statePtr->sock, newValue, IP_DROP_MEMBERSHIP); } else if (!strcmp("-broadcast", optionName)) { @@ -1259,9 +1351,28 @@ udpSetOption(ClientData instanceData, Tcl_Interp *interp, if (r == TCL_OK) { if (setsockopt(statePtr->sock, SOL_SOCKET, SO_BROADCAST, (const char *)&tmp, sizeof(int))) { - sprintf(errBuf, "%s", "udp - setsockopt"); UDPTRACE("UDP error - setsockopt\n"); - Tcl_SetObjResult(interp, Tcl_NewStringObj(errBuf, -1)); + Tcl_SetResult(interp, "udp - setsockopt SO_BROADCAST", TCL_STATIC); + r = TCL_ERROR; + } else { + Tcl_SetObjResult(interp, Tcl_NewIntObj(tmp)); + } + } + + } else if (!strcmp("-reuseaddr", optionName)) { + /* + * FIX ME: this doesn't work here. + * what we want is a udp command and to be able to specify this to that + * command. This will only work if re-attaching a client socket. + * (if we can do that) + */ + int tmp = 1; + r = Tcl_GetInt(interp, newValue, &tmp); + if (r == TCL_OK) { + if (setsockopt(statePtr->sock, SOL_SOCKET, SO_REUSEADDR, + (const char *)&tmp, sizeof(int))) { + UDPTRACE("UDP error - setsockopt\n"); + Tcl_SetResult(interp, "udp - setsockopt SO_REUSEADDR", TCL_STATIC); r = TCL_ERROR; } else { Tcl_SetObjResult(interp, Tcl_NewIntObj(tmp)); @@ -1279,11 +1390,11 @@ udpSetOption(ClientData instanceData, Tcl_Interp *interp, /* * ---------------------------------------------------------------------- - * udpTrace -- + * UdpTrace -- * ---------------------------------------------------------------------- */ static void -udpTrace(const char *format, ...) +UdpTrace(const char *format, ...) { va_list args; diff --git a/generic/udp_tcl.h b/generic/udp_tcl.h index fdcf899..0eec82e 100644 --- a/generic/udp_tcl.h +++ b/generic/udp_tcl.h @@ -5,7 +5,9 @@ * Copyright 1999-2000 by Columbia University; all rights reserved * * Written by Xiaotao Wu - * Last modified: 11/03/2000 + * Modifications by Pat Thoyts + * + * $Id$ *---------------------------------------------------------------------- */ @@ -25,7 +27,8 @@ #endif #ifdef WIN32 -# include +# include +# include #else # if HAVE_UNISTD_H # include @@ -51,41 +54,45 @@ #define TCL_STORAGE_CLASS DLLEXPORT #endif /* BUILD_udp */ +typedef union { + short ss_family; + /*struct sockaddr_storage stg;*/ + struct sockaddr_in ipv4; + struct sockaddr_in6 ipv6; +} sockaddr_t; + #ifdef WIN32 typedef u_short uint16_t; typedef struct { - Tcl_Event header; /* Information that is standard for */ - Tcl_Channel chan; /* Socket descriptor that is ready */ + Tcl_Event header; /* Information that is standard for */ + Tcl_Channel chan; /* Socket descriptor that is ready */ } UdpEvent; typedef struct PacketList { - char *message; - int actual_size; - char r_host[256]; - int r_port; - struct PacketList *next; + char *message; + int actual_size; + sockaddr_t peer; + struct PacketList *next; } PacketList; #endif /* WIN32 */ typedef struct UdpState { - Tcl_Channel channel; - int sock; - char remotehost[256]; /* send packets to */ - uint16_t remoteport; - char peerhost[256]; /* receive packets from */ - uint16_t peerport; - uint16_t localport; - int doread; + Tcl_Channel channel; + int sock; + int doread; + sockaddr_t saddr_local; + sockaddr_t saddr_remote; + sockaddr_t saddr_peer; #ifdef WIN32 - HWND hwnd; - PacketList *packets; - PacketList *packetsTail; - int packetNum; - struct UdpState *next; - Tcl_ThreadId threadId; /* for Tcl_ThreadAlert */ + HWND hwnd; + PacketList *packets; + PacketList *packetsTail; + int packetNum; + struct UdpState *next; + Tcl_ThreadId threadId; /* for Tcl_ThreadAlert */ #endif } UdpState; diff --git a/tests/udp.test b/tests/udp.test index 14318b4..6d18876 100644 --- a/tests/udp.test +++ b/tests/udp.test @@ -19,6 +19,7 @@ if {[lsearch [namespace children] ::tcltest] == -1} { package require udp # ------------------------------------------------------------------------- +# Original tcludp < 1.0.4 user interface test udp-1.0 {udp_open with any port} { global _udp diff --git a/win/makefile.vc b/win/makefile.vc index e909bd9..65eb0ae 100644 --- a/win/makefile.vc +++ b/win/makefile.vc @@ -156,7 +156,7 @@ Please `cd` to its location first. PROJECT = udp !include "rules.vc" -DOTVERSION = 1.0.6 +DOTVERSION = 2.0.0 VERSION = $(DOTVERSION:.=) STUBPREFIX = $(PROJECT)stub @@ -278,7 +278,7 @@ lflags = $(lflags) -ws:aggressive dlllflags = $(lflags) -dll conlflags = $(lflags) -subsystem:console guilflags = $(lflags) -subsystem:windows -baselibs = $(TCLSTUBLIB) wsock32.lib +baselibs = $(TCLSTUBLIB) ws2_32.lib #wsock32.lib #--------------------------------------------------------------------- # TclTest flags diff --git a/win/tcludp.dsp b/win/tcludp.dsp index 1d7ae3b..1d3f328 100644 --- a/win/tcludp.dsp +++ b/win/tcludp.dsp @@ -105,5 +105,17 @@ SOURCE=..\generic\udp_tcl.h SOURCE=.\tcludp.rc # End Source File # End Group +# Begin Source File + +SOURCE=.\bcastdemo.tcl +# End Source File +# Begin Source File + +SOURCE=.\demo.tcl +# End Source File +# Begin Source File + +SOURCE=..\generic\udp.tcl +# End Source File # End Target # End Project diff --git a/win/tcludp.rc b/win/tcludp.rc index fb16bf5..821a500 100644 --- a/win/tcludp.rc +++ b/win/tcludp.rc @@ -8,8 +8,8 @@ LANGUAGE 0x9, 0x1 /* LANG_ENGLISH, SUBLANG_DEFAULT */ VS_VERSION_INFO VERSIONINFO - FILEVERSION 1,0,6,0 - PRODUCTVERSION 1,0,6,0 + FILEVERSION 2,0,0,0 + PRODUCTVERSION 2,0,0,0 FILEFLAGSMASK 0x3fL #ifdef DEBUG FILEFLAGS VS_FF_DEBUG @@ -25,11 +25,11 @@ BEGIN BLOCK "040904b0" /* LANG_ENGLISH/SUBLANG_ENGLISH_US, Unicode CP */ BEGIN VALUE "FileDescription", "Tcl UDP extension\0" - VALUE "OriginalFilename", "udp106.dll\0" - VALUE "FileVersion", "1.0.6.0\0" + VALUE "OriginalFilename", "udp200.dll\0" + VALUE "FileVersion", "2.0.0.0\0" VALUE "LegalCopyright", "Copyright \251 1999-2000 Columbia University; all rights reserved\0" VALUE "ProductName", "TclUDP\0" - VALUE "ProductVersion", "1.0.6.0\0" + VALUE "ProductVersion", "2.0.0.0\0" END END BLOCK "VarFileInfo" -- 2.23.0