#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;
* 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;
* 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 */
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
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);
}
/*
* 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;
+}
+\f
+/*
+ * ----------------------------------------------------------------------
+ *
+ * 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;
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
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';
UdpState *statePtr;
Tcl_Time blockTime = { 0, 0 };
- UDPTRACE("setupProc\n");
+ /* UDPTRACE("setupProc\n"); */
if (!(flags & TCL_FILE_EVENTS)) {
return;
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);
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");
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;
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--;
* Load the socket DLL and initialize the function table.
*/
- if (WSAStartup(0x0101, &wsaData))
+ if (WSAStartup(0x0002, &wsaData))
return 0;
return 1;
/* 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 */
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);
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;
}
{
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;
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;
}
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");
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,
*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 */
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 --
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 {
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);
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)) {
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));
/*
* ----------------------------------------------------------------------
- * udpTrace --
+ * UdpTrace --
* ----------------------------------------------------------------------
*/
static void
-udpTrace(const char *format, ...)
+UdpTrace(const char *format, ...)
{
va_list args;