| /********** |
| This library is free software; you can redistribute it and/or modify it under |
| the terms of the GNU Lesser General Public License as published by the |
| Free Software Foundation; either version 3 of the License, or (at your |
| option) any later version. (See <http://www.gnu.org/copyleft/lesser.html>.) |
| |
| This library is distributed in the hope that it will be useful, but WITHOUT |
| ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
| FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for |
| more details. |
| |
| You should have received a copy of the GNU Lesser General Public License |
| along with this library; if not, write to the Free Software Foundation, Inc., |
| 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
| **********/ |
| // "mTunnel" multicast access service |
| // Copyright (c) 1996-2020 Live Networks, Inc. All rights reserved. |
| // Helper routines to implement 'group sockets' |
| // Implementation |
| |
| #include "GroupsockHelper.hh" |
| |
| #if (defined(__WIN32__) || defined(_WIN32)) && !defined(__MINGW32__) |
| #include <time.h> |
| extern "C" int initializeWinsockIfNecessary(); |
| #else |
| #include <stdarg.h> |
| #include <time.h> |
| #include <sys/time.h> |
| #if !defined(_WIN32) |
| #include <netinet/tcp.h> |
| #ifdef __ANDROID_NDK__ |
| #include <android/ndk-version.h> |
| #define ANDROID_OLD_NDK __NDK_MAJOR__ < 17 |
| #endif |
| #endif |
| #include <fcntl.h> |
| #define initializeWinsockIfNecessary() 1 |
| #endif |
| #if defined(__WIN32__) || defined(_WIN32) || defined(_QNX4) |
| #else |
| #include <signal.h> |
| #define USE_SIGNALS 1 |
| #endif |
| #include <stdio.h> |
| |
| // By default, use INADDR_ANY for the sending and receiving interfaces: |
| netAddressBits SendingInterfaceAddr = INADDR_ANY; |
| netAddressBits ReceivingInterfaceAddr = INADDR_ANY; |
| |
| static void socketErr(UsageEnvironment& env, char const* errorMsg) { |
| env.setResultErrMsg(errorMsg); |
| } |
| |
| NoReuse::NoReuse(UsageEnvironment& env) |
| : fEnv(env) { |
| groupsockPriv(fEnv)->reuseFlag = 0; |
| } |
| |
| NoReuse::~NoReuse() { |
| groupsockPriv(fEnv)->reuseFlag = 1; |
| reclaimGroupsockPriv(fEnv); |
| } |
| |
| |
| _groupsockPriv* groupsockPriv(UsageEnvironment& env) { |
| if (env.groupsockPriv == NULL) { // We need to create it |
| _groupsockPriv* result = new _groupsockPriv; |
| result->socketTable = NULL; |
| result->reuseFlag = 1; // default value => allow reuse of socket numbers |
| env.groupsockPriv = result; |
| } |
| return (_groupsockPriv*)(env.groupsockPriv); |
| } |
| |
| void reclaimGroupsockPriv(UsageEnvironment& env) { |
| _groupsockPriv* priv = (_groupsockPriv*)(env.groupsockPriv); |
| if (priv->socketTable == NULL && priv->reuseFlag == 1/*default value*/) { |
| // We can delete the structure (to save space); it will get created again, if needed: |
| delete priv; |
| env.groupsockPriv = NULL; |
| } |
| } |
| |
| static int createSocket(int type) { |
| // Call "socket()" to create a (IPv4) socket of the specified type. |
| // But also set it to have the 'close on exec' property (if we can) |
| int sock; |
| |
| #ifdef SOCK_CLOEXEC |
| sock = socket(AF_INET, type|SOCK_CLOEXEC, 0); |
| if (sock != -1 || errno != EINVAL) return sock; |
| // An "errno" of EINVAL likely means that the system wasn't happy with the SOCK_CLOEXEC; fall through and try again without it: |
| #endif |
| |
| sock = socket(AF_INET, type, 0); |
| #ifdef FD_CLOEXEC |
| if (sock != -1) fcntl(sock, F_SETFD, FD_CLOEXEC); |
| #endif |
| return sock; |
| } |
| |
| int setupDatagramSocket(UsageEnvironment& env, Port port) { |
| if (!initializeWinsockIfNecessary()) { |
| socketErr(env, "Failed to initialize 'winsock': "); |
| return -1; |
| } |
| |
| int newSocket = createSocket(SOCK_DGRAM); |
| if (newSocket < 0) { |
| socketErr(env, "unable to create datagram socket: "); |
| return newSocket; |
| } |
| |
| int reuseFlag = groupsockPriv(env)->reuseFlag; |
| reclaimGroupsockPriv(env); |
| if (setsockopt(newSocket, SOL_SOCKET, SO_REUSEADDR, |
| (const char*)&reuseFlag, sizeof reuseFlag) < 0) { |
| socketErr(env, "setsockopt(SO_REUSEADDR) error: "); |
| closeSocket(newSocket); |
| return -1; |
| } |
| |
| #if defined(__WIN32__) || defined(_WIN32) |
| // Windoze doesn't properly handle SO_REUSEPORT or IP_MULTICAST_LOOP |
| #else |
| #ifdef SO_REUSEPORT |
| if (setsockopt(newSocket, SOL_SOCKET, SO_REUSEPORT, |
| (const char*)&reuseFlag, sizeof reuseFlag) < 0) { |
| socketErr(env, "setsockopt(SO_REUSEPORT) error: "); |
| closeSocket(newSocket); |
| return -1; |
| } |
| #endif |
| |
| #ifdef IP_MULTICAST_LOOP |
| const u_int8_t loop = 1; |
| if (setsockopt(newSocket, IPPROTO_IP, IP_MULTICAST_LOOP, |
| (const char*)&loop, sizeof loop) < 0) { |
| socketErr(env, "setsockopt(IP_MULTICAST_LOOP) error: "); |
| closeSocket(newSocket); |
| return -1; |
| } |
| #endif |
| #endif |
| |
| // Note: Windoze requires binding, even if the port number is 0 |
| netAddressBits addr = INADDR_ANY; |
| #if defined(__WIN32__) || defined(_WIN32) |
| #else |
| if (port.num() != 0 || ReceivingInterfaceAddr != INADDR_ANY) { |
| #endif |
| if (port.num() == 0) addr = ReceivingInterfaceAddr; |
| MAKE_SOCKADDR_IN(name, addr, port.num()); |
| if (bind(newSocket, (struct sockaddr*)&name, sizeof name) != 0) { |
| char tmpBuffer[100]; |
| sprintf(tmpBuffer, "bind() error (port number: %d): ", |
| ntohs(port.num())); |
| socketErr(env, tmpBuffer); |
| closeSocket(newSocket); |
| return -1; |
| } |
| #if defined(__WIN32__) || defined(_WIN32) |
| #else |
| } |
| #endif |
| |
| // Set the sending interface for multicasts, if it's not the default: |
| if (SendingInterfaceAddr != INADDR_ANY) { |
| struct in_addr addr; |
| addr.s_addr = SendingInterfaceAddr; |
| |
| if (setsockopt(newSocket, IPPROTO_IP, IP_MULTICAST_IF, |
| (const char*)&addr, sizeof addr) < 0) { |
| socketErr(env, "error setting outgoing multicast interface: "); |
| closeSocket(newSocket); |
| return -1; |
| } |
| } |
| |
| return newSocket; |
| } |
| |
| Boolean makeSocketNonBlocking(int sock) { |
| #if defined(__WIN32__) || defined(_WIN32) |
| unsigned long arg = 1; |
| return ioctlsocket(sock, FIONBIO, &arg) == 0; |
| #elif defined(VXWORKS) |
| int arg = 1; |
| return ioctl(sock, FIONBIO, (int)&arg) == 0; |
| #else |
| int curFlags = fcntl(sock, F_GETFL, 0); |
| return fcntl(sock, F_SETFL, curFlags|O_NONBLOCK) >= 0; |
| #endif |
| } |
| |
| Boolean makeSocketBlocking(int sock, unsigned writeTimeoutInMilliseconds) { |
| Boolean result; |
| #if defined(__WIN32__) || defined(_WIN32) |
| unsigned long arg = 0; |
| result = ioctlsocket(sock, FIONBIO, &arg) == 0; |
| #elif defined(VXWORKS) |
| int arg = 0; |
| result = ioctl(sock, FIONBIO, (int)&arg) == 0; |
| #else |
| int curFlags = fcntl(sock, F_GETFL, 0); |
| result = fcntl(sock, F_SETFL, curFlags&(~O_NONBLOCK)) >= 0; |
| #endif |
| |
| if (writeTimeoutInMilliseconds > 0) { |
| #ifdef SO_SNDTIMEO |
| #if defined(__WIN32__) || defined(_WIN32) |
| DWORD msto = (DWORD)writeTimeoutInMilliseconds; |
| setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, (char *)&msto, sizeof(msto) ); |
| #else |
| struct timeval tv; |
| tv.tv_sec = writeTimeoutInMilliseconds/1000; |
| tv.tv_usec = (writeTimeoutInMilliseconds%1000)*1000; |
| setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, (char *)&tv, sizeof tv); |
| #endif |
| #endif |
| } |
| |
| return result; |
| } |
| |
| Boolean setSocketKeepAlive(int sock) { |
| #if defined(__WIN32__) || defined(_WIN32) |
| // How do we do this in Windows? For now, just make this a no-op in Windows: |
| #else |
| int const keepalive_enabled = 1; |
| if (setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, (void*)&keepalive_enabled, sizeof keepalive_enabled) < 0) { |
| return False; |
| } |
| |
| #ifdef TCP_KEEPIDLE |
| int const keepalive_time = 180; |
| if (setsockopt(sock, IPPROTO_TCP, TCP_KEEPIDLE, (void*)&keepalive_time, sizeof keepalive_time) < 0) { |
| return False; |
| } |
| #endif |
| |
| #ifdef TCP_KEEPCNT |
| int const keepalive_count = 5; |
| if (setsockopt(sock, IPPROTO_TCP, TCP_KEEPCNT, (void*)&keepalive_count, sizeof keepalive_count) < 0) { |
| return False; |
| } |
| #endif |
| |
| #ifdef TCP_KEEPINTVL |
| int const keepalive_interval = 20; |
| if (setsockopt(sock, IPPROTO_TCP, TCP_KEEPINTVL, (void*)&keepalive_interval, sizeof keepalive_interval) < 0) { |
| return False; |
| } |
| #endif |
| #endif |
| |
| return True; |
| } |
| |
| int setupStreamSocket(UsageEnvironment& env, |
| Port port, Boolean makeNonBlocking, Boolean setKeepAlive) { |
| if (!initializeWinsockIfNecessary()) { |
| socketErr(env, "Failed to initialize 'winsock': "); |
| return -1; |
| } |
| |
| int newSocket = createSocket(SOCK_STREAM); |
| if (newSocket < 0) { |
| socketErr(env, "unable to create stream socket: "); |
| return newSocket; |
| } |
| |
| int reuseFlag = groupsockPriv(env)->reuseFlag; |
| reclaimGroupsockPriv(env); |
| if (setsockopt(newSocket, SOL_SOCKET, SO_REUSEADDR, |
| (const char*)&reuseFlag, sizeof reuseFlag) < 0) { |
| socketErr(env, "setsockopt(SO_REUSEADDR) error: "); |
| closeSocket(newSocket); |
| return -1; |
| } |
| |
| // SO_REUSEPORT doesn't really make sense for TCP sockets, so we |
| // normally don't set them. However, if you really want to do this |
| // #define REUSE_FOR_TCP |
| #ifdef REUSE_FOR_TCP |
| #if defined(__WIN32__) || defined(_WIN32) |
| // Windoze doesn't properly handle SO_REUSEPORT |
| #else |
| #ifdef SO_REUSEPORT |
| if (setsockopt(newSocket, SOL_SOCKET, SO_REUSEPORT, |
| (const char*)&reuseFlag, sizeof reuseFlag) < 0) { |
| socketErr(env, "setsockopt(SO_REUSEPORT) error: "); |
| closeSocket(newSocket); |
| return -1; |
| } |
| #endif |
| #endif |
| #endif |
| |
| // Note: Windoze requires binding, even if the port number is 0 |
| #if defined(__WIN32__) || defined(_WIN32) |
| #else |
| if (port.num() != 0 || ReceivingInterfaceAddr != INADDR_ANY) { |
| #endif |
| MAKE_SOCKADDR_IN(name, ReceivingInterfaceAddr, port.num()); |
| if (bind(newSocket, (struct sockaddr*)&name, sizeof name) != 0) { |
| char tmpBuffer[100]; |
| sprintf(tmpBuffer, "bind() error (port number: %d): ", |
| ntohs(port.num())); |
| socketErr(env, tmpBuffer); |
| closeSocket(newSocket); |
| return -1; |
| } |
| #if defined(__WIN32__) || defined(_WIN32) |
| #else |
| } |
| #endif |
| |
| if (makeNonBlocking) { |
| if (!makeSocketNonBlocking(newSocket)) { |
| socketErr(env, "failed to make non-blocking: "); |
| closeSocket(newSocket); |
| return -1; |
| } |
| } |
| |
| // Set the keep alive mechanism for the TCP socket, to avoid "ghost sockets" |
| // that remain after an interrupted communication. |
| if (setKeepAlive) { |
| if (!setSocketKeepAlive(newSocket)) { |
| socketErr(env, "failed to set keep alive: "); |
| closeSocket(newSocket); |
| return -1; |
| } |
| } |
| |
| return newSocket; |
| } |
| |
| int readSocket(UsageEnvironment& env, |
| int socket, unsigned char* buffer, unsigned bufferSize, |
| struct sockaddr_in& fromAddress) { |
| SOCKLEN_T addressSize = sizeof fromAddress; |
| int bytesRead = recvfrom(socket, (char*)buffer, bufferSize, 0, |
| (struct sockaddr*)&fromAddress, |
| &addressSize); |
| if (bytesRead < 0) { |
| //##### HACK to work around bugs in Linux and Windows: |
| int err = env.getErrno(); |
| if (err == 111 /*ECONNREFUSED (Linux)*/ |
| #if defined(__WIN32__) || defined(_WIN32) |
| // What a piece of crap Windows is. Sometimes |
| // recvfrom() returns -1, but with an 'errno' of 0. |
| // This appears not to be a real error; just treat |
| // it as if it were a read of zero bytes, and hope |
| // we don't have to do anything else to 'reset' |
| // this alleged error: |
| || err == 0 || err == EWOULDBLOCK |
| #else |
| || err == EAGAIN |
| #endif |
| || err == 113 /*EHOSTUNREACH (Linux)*/) { // Why does Linux return this for datagram sock? |
| fromAddress.sin_addr.s_addr = 0; |
| return 0; |
| } |
| //##### END HACK |
| socketErr(env, "recvfrom() error: "); |
| } else if (bytesRead == 0) { |
| // "recvfrom()" on a stream socket can return 0 if the remote end has closed the connection. Treat this as an error: |
| return -1; |
| } |
| |
| return bytesRead; |
| } |
| |
| Boolean writeSocket(UsageEnvironment& env, |
| int socket, struct in_addr address, portNumBits portNum, |
| u_int8_t ttlArg, |
| unsigned char* buffer, unsigned bufferSize) { |
| // Before sending, set the socket's TTL: |
| #if defined(__WIN32__) || defined(_WIN32) |
| #define TTL_TYPE int |
| #else |
| #define TTL_TYPE u_int8_t |
| #endif |
| TTL_TYPE ttl = (TTL_TYPE)ttlArg; |
| if (setsockopt(socket, IPPROTO_IP, IP_MULTICAST_TTL, |
| (const char*)&ttl, sizeof ttl) < 0) { |
| socketErr(env, "setsockopt(IP_MULTICAST_TTL) error: "); |
| return False; |
| } |
| |
| return writeSocket(env, socket, address, portNum, buffer, bufferSize); |
| } |
| |
| Boolean writeSocket(UsageEnvironment& env, |
| int socket, struct in_addr address, portNumBits portNum, |
| unsigned char* buffer, unsigned bufferSize) { |
| do { |
| MAKE_SOCKADDR_IN(dest, address.s_addr, portNum); |
| int bytesSent = sendto(socket, (char*)buffer, bufferSize, 0, |
| (struct sockaddr*)&dest, sizeof dest); |
| if (bytesSent != (int)bufferSize) { |
| char tmpBuf[100]; |
| sprintf(tmpBuf, "writeSocket(%d), sendTo() error: wrote %d bytes instead of %u: ", socket, bytesSent, bufferSize); |
| socketErr(env, tmpBuf); |
| break; |
| } |
| |
| return True; |
| } while (0); |
| |
| return False; |
| } |
| |
| void ignoreSigPipeOnSocket(int socketNum) { |
| #ifdef USE_SIGNALS |
| #ifdef SO_NOSIGPIPE |
| int set_option = 1; |
| setsockopt(socketNum, SOL_SOCKET, SO_NOSIGPIPE, &set_option, sizeof set_option); |
| #else |
| signal(SIGPIPE, SIG_IGN); |
| #endif |
| #endif |
| } |
| |
| static unsigned getBufferSize(UsageEnvironment& env, int bufOptName, |
| int socket) { |
| unsigned curSize; |
| SOCKLEN_T sizeSize = sizeof curSize; |
| if (getsockopt(socket, SOL_SOCKET, bufOptName, |
| (char*)&curSize, &sizeSize) < 0) { |
| socketErr(env, "getBufferSize() error: "); |
| return 0; |
| } |
| |
| return curSize; |
| } |
| unsigned getSendBufferSize(UsageEnvironment& env, int socket) { |
| return getBufferSize(env, SO_SNDBUF, socket); |
| } |
| unsigned getReceiveBufferSize(UsageEnvironment& env, int socket) { |
| return getBufferSize(env, SO_RCVBUF, socket); |
| } |
| |
| static unsigned setBufferTo(UsageEnvironment& env, int bufOptName, |
| int socket, unsigned requestedSize) { |
| SOCKLEN_T sizeSize = sizeof requestedSize; |
| setsockopt(socket, SOL_SOCKET, bufOptName, (char*)&requestedSize, sizeSize); |
| |
| // Get and return the actual, resulting buffer size: |
| return getBufferSize(env, bufOptName, socket); |
| } |
| unsigned setSendBufferTo(UsageEnvironment& env, |
| int socket, unsigned requestedSize) { |
| return setBufferTo(env, SO_SNDBUF, socket, requestedSize); |
| } |
| unsigned setReceiveBufferTo(UsageEnvironment& env, |
| int socket, unsigned requestedSize) { |
| return setBufferTo(env, SO_RCVBUF, socket, requestedSize); |
| } |
| |
| static unsigned increaseBufferTo(UsageEnvironment& env, int bufOptName, |
| int socket, unsigned requestedSize) { |
| // First, get the current buffer size. If it's already at least |
| // as big as what we're requesting, do nothing. |
| unsigned curSize = getBufferSize(env, bufOptName, socket); |
| |
| // Next, try to increase the buffer to the requested size, |
| // or to some smaller size, if that's not possible: |
| while (requestedSize > curSize) { |
| SOCKLEN_T sizeSize = sizeof requestedSize; |
| if (setsockopt(socket, SOL_SOCKET, bufOptName, |
| (char*)&requestedSize, sizeSize) >= 0) { |
| // success |
| return requestedSize; |
| } |
| requestedSize = (requestedSize+curSize)/2; |
| } |
| |
| return getBufferSize(env, bufOptName, socket); |
| } |
| unsigned increaseSendBufferTo(UsageEnvironment& env, |
| int socket, unsigned requestedSize) { |
| return increaseBufferTo(env, SO_SNDBUF, socket, requestedSize); |
| } |
| unsigned increaseReceiveBufferTo(UsageEnvironment& env, |
| int socket, unsigned requestedSize) { |
| return increaseBufferTo(env, SO_RCVBUF, socket, requestedSize); |
| } |
| |
| static void clearMulticastAllSocketOption(int socket) { |
| #ifdef IP_MULTICAST_ALL |
| // This option is defined in modern versions of Linux to overcome a bug in the Linux kernel's default behavior. |
| // When set to 0, it ensures that we receive only packets that were sent to the specified IP multicast address, |
| // even if some other process on the same system has joined a different multicast group with the same port number. |
| int multicastAll = 0; |
| (void)setsockopt(socket, IPPROTO_IP, IP_MULTICAST_ALL, (void*)&multicastAll, sizeof multicastAll); |
| // Ignore the call's result. Should it fail, we'll still receive packets (just perhaps more than intended) |
| #endif |
| } |
| |
| Boolean socketJoinGroup(UsageEnvironment& env, int socket, |
| netAddressBits groupAddress){ |
| if (!IsMulticastAddress(groupAddress)) return True; // ignore this case |
| |
| struct ip_mreq imr; |
| imr.imr_multiaddr.s_addr = groupAddress; |
| imr.imr_interface.s_addr = ReceivingInterfaceAddr; |
| if (setsockopt(socket, IPPROTO_IP, IP_ADD_MEMBERSHIP, |
| (const char*)&imr, sizeof (struct ip_mreq)) < 0) { |
| #if defined(__WIN32__) || defined(_WIN32) |
| if (env.getErrno() != 0) { |
| // That piece-of-shit toy operating system (Windows) sometimes lies |
| // about setsockopt() failing! |
| #endif |
| socketErr(env, "setsockopt(IP_ADD_MEMBERSHIP) error: "); |
| return False; |
| #if defined(__WIN32__) || defined(_WIN32) |
| } |
| #endif |
| } |
| |
| clearMulticastAllSocketOption(socket); |
| |
| return True; |
| } |
| |
| Boolean socketLeaveGroup(UsageEnvironment&, int socket, |
| netAddressBits groupAddress) { |
| if (!IsMulticastAddress(groupAddress)) return True; // ignore this case |
| |
| struct ip_mreq imr; |
| imr.imr_multiaddr.s_addr = groupAddress; |
| imr.imr_interface.s_addr = ReceivingInterfaceAddr; |
| if (setsockopt(socket, IPPROTO_IP, IP_DROP_MEMBERSHIP, |
| (const char*)&imr, sizeof (struct ip_mreq)) < 0) { |
| return False; |
| } |
| |
| return True; |
| } |
| |
| // The source-specific join/leave operations require special setsockopt() |
| // commands, and a special structure (ip_mreq_source). If the include files |
| // didn't define these, we do so here: |
| #if !defined(IP_ADD_SOURCE_MEMBERSHIP) |
| struct ip_mreq_source { |
| struct in_addr imr_multiaddr; /* IP multicast address of group */ |
| struct in_addr imr_sourceaddr; /* IP address of source */ |
| struct in_addr imr_interface; /* local IP address of interface */ |
| }; |
| #endif |
| |
| #ifndef IP_ADD_SOURCE_MEMBERSHIP |
| |
| #ifdef LINUX |
| #define IP_ADD_SOURCE_MEMBERSHIP 39 |
| #define IP_DROP_SOURCE_MEMBERSHIP 40 |
| #else |
| #define IP_ADD_SOURCE_MEMBERSHIP 25 |
| #define IP_DROP_SOURCE_MEMBERSHIP 26 |
| #endif |
| |
| #endif |
| |
| Boolean socketJoinGroupSSM(UsageEnvironment& env, int socket, |
| netAddressBits groupAddress, |
| netAddressBits sourceFilterAddr) { |
| if (!IsMulticastAddress(groupAddress)) return True; // ignore this case |
| |
| struct ip_mreq_source imr; |
| #if ANDROID_OLD_NDK |
| imr.imr_multiaddr = groupAddress; |
| imr.imr_sourceaddr = sourceFilterAddr; |
| imr.imr_interface = ReceivingInterfaceAddr; |
| #else |
| imr.imr_multiaddr.s_addr = groupAddress; |
| imr.imr_sourceaddr.s_addr = sourceFilterAddr; |
| imr.imr_interface.s_addr = ReceivingInterfaceAddr; |
| #endif |
| if (setsockopt(socket, IPPROTO_IP, IP_ADD_SOURCE_MEMBERSHIP, |
| (const char*)&imr, sizeof (struct ip_mreq_source)) < 0) { |
| socketErr(env, "setsockopt(IP_ADD_SOURCE_MEMBERSHIP) error: "); |
| return False; |
| } |
| |
| clearMulticastAllSocketOption(socket); |
| |
| return True; |
| } |
| |
| Boolean socketLeaveGroupSSM(UsageEnvironment& /*env*/, int socket, |
| netAddressBits groupAddress, |
| netAddressBits sourceFilterAddr) { |
| if (!IsMulticastAddress(groupAddress)) return True; // ignore this case |
| |
| struct ip_mreq_source imr; |
| #if ANDROID_OLD_NDK |
| imr.imr_multiaddr = groupAddress; |
| imr.imr_sourceaddr = sourceFilterAddr; |
| imr.imr_interface = ReceivingInterfaceAddr; |
| #else |
| imr.imr_multiaddr.s_addr = groupAddress; |
| imr.imr_sourceaddr.s_addr = sourceFilterAddr; |
| imr.imr_interface.s_addr = ReceivingInterfaceAddr; |
| #endif |
| if (setsockopt(socket, IPPROTO_IP, IP_DROP_SOURCE_MEMBERSHIP, |
| (const char*)&imr, sizeof (struct ip_mreq_source)) < 0) { |
| return False; |
| } |
| |
| return True; |
| } |
| |
| static Boolean getSourcePort0(int socket, portNumBits& resultPortNum/*host order*/) { |
| sockaddr_in test; test.sin_port = 0; |
| SOCKLEN_T len = sizeof test; |
| if (getsockname(socket, (struct sockaddr*)&test, &len) < 0) return False; |
| |
| resultPortNum = ntohs(test.sin_port); |
| return True; |
| } |
| |
| Boolean getSourcePort(UsageEnvironment& env, int socket, Port& port) { |
| portNumBits portNum = 0; |
| if (!getSourcePort0(socket, portNum) || portNum == 0) { |
| // Hack - call bind(), then try again: |
| MAKE_SOCKADDR_IN(name, INADDR_ANY, 0); |
| bind(socket, (struct sockaddr*)&name, sizeof name); |
| |
| if (!getSourcePort0(socket, portNum) || portNum == 0) { |
| socketErr(env, "getsockname() error: "); |
| return False; |
| } |
| } |
| |
| port = Port(portNum); |
| return True; |
| } |
| |
| static Boolean badAddressForUs(netAddressBits addr) { |
| // Check for some possible erroneous addresses: |
| netAddressBits nAddr = htonl(addr); |
| return (nAddr == 0x7F000001 /* 127.0.0.1 */ |
| || nAddr == 0 |
| || nAddr == (netAddressBits)(~0)); |
| } |
| |
| Boolean loopbackWorks = 1; |
| |
| netAddressBits ourIPAddress(UsageEnvironment& env) { |
| static netAddressBits ourAddress = 0; |
| int sock = -1; |
| struct in_addr testAddr; |
| |
| if (ReceivingInterfaceAddr != INADDR_ANY) { |
| // Hack: If we were told to receive on a specific interface address, then |
| // define this to be our ip address: |
| ourAddress = ReceivingInterfaceAddr; |
| } |
| |
| if (ourAddress == 0) { |
| // We need to find our source address |
| struct sockaddr_in fromAddr; |
| fromAddr.sin_addr.s_addr = 0; |
| |
| // Get our address by sending a (0-TTL) multicast packet, |
| // receiving it, and looking at the source address used. |
| // (This is kinda bogus, but it provides the best guarantee |
| // that other nodes will think our address is the same as we do.) |
| do { |
| loopbackWorks = 0; // until we learn otherwise |
| |
| #ifndef DISABLE_LOOPBACK_IP_ADDRESS_CHECK |
| testAddr.s_addr = our_inet_addr("228.67.43.91"); // arbitrary |
| Port testPort(15947); // ditto |
| |
| sock = setupDatagramSocket(env, testPort); |
| if (sock < 0) break; |
| |
| if (!socketJoinGroup(env, sock, testAddr.s_addr)) break; |
| |
| unsigned char testString[] = "hostIdTest"; |
| unsigned testStringLength = sizeof testString; |
| |
| if (!writeSocket(env, sock, testAddr, testPort.num(), 0, |
| testString, testStringLength)) break; |
| |
| // Block until the socket is readable (with a 5-second timeout): |
| fd_set rd_set; |
| FD_ZERO(&rd_set); |
| FD_SET((unsigned)sock, &rd_set); |
| const unsigned numFds = sock+1; |
| struct timeval timeout; |
| timeout.tv_sec = 5; |
| timeout.tv_usec = 0; |
| int result = select(numFds, &rd_set, NULL, NULL, &timeout); |
| if (result <= 0) break; |
| |
| unsigned char readBuffer[20]; |
| int bytesRead = readSocket(env, sock, |
| readBuffer, sizeof readBuffer, |
| fromAddr); |
| if (bytesRead != (int)testStringLength |
| || strncmp((char*)readBuffer, (char*)testString, testStringLength) != 0) { |
| break; |
| } |
| |
| // We use this packet's source address, if it's good: |
| loopbackWorks = !badAddressForUs(fromAddr.sin_addr.s_addr); |
| #endif |
| } while (0); |
| |
| if (sock >= 0) { |
| socketLeaveGroup(env, sock, testAddr.s_addr); |
| closeSocket(sock); |
| } |
| |
| if (!loopbackWorks) do { |
| // We couldn't find our address using multicast loopback, |
| // so try instead to look it up directly - by first getting our host name, and then resolving this host name |
| char hostname[100]; |
| hostname[0] = '\0'; |
| int result = gethostname(hostname, sizeof hostname); |
| if (result != 0 || hostname[0] == '\0') { |
| env.setResultErrMsg("initial gethostname() failed"); |
| break; |
| } |
| |
| // Try to resolve "hostname" to an IP address: |
| NetAddressList addresses(hostname); |
| NetAddressList::Iterator iter(addresses); |
| NetAddress const* address; |
| |
| // Take the first address that's not bad: |
| netAddressBits addr = 0; |
| while ((address = iter.nextAddress()) != NULL) { |
| netAddressBits a = *(netAddressBits*)(address->data()); |
| if (!badAddressForUs(a)) { |
| addr = a; |
| break; |
| } |
| } |
| |
| // Assign the address that we found to "fromAddr" (as if the 'loopback' method had worked), to simplify the code below: |
| fromAddr.sin_addr.s_addr = addr; |
| } while (0); |
| |
| // Make sure we have a good address: |
| netAddressBits from = fromAddr.sin_addr.s_addr; |
| if (badAddressForUs(from)) { |
| char tmp[100]; |
| sprintf(tmp, "This computer has an invalid IP address: %s", AddressString(from).val()); |
| env.setResultMsg(tmp); |
| from = 0; |
| } |
| |
| ourAddress = from; |
| |
| // Use our newly-discovered IP address, and the current time, |
| // to initialize the random number generator's seed: |
| struct timeval timeNow; |
| gettimeofday(&timeNow, NULL); |
| unsigned seed = ourAddress^timeNow.tv_sec^timeNow.tv_usec; |
| our_srandom(seed); |
| } |
| return ourAddress; |
| } |
| |
| netAddressBits chooseRandomIPv4SSMAddress(UsageEnvironment& env) { |
| // First, a hack to ensure that our random number generator is seeded: |
| (void) ourIPAddress(env); |
| |
| // Choose a random address in the range [232.0.1.0, 232.255.255.255) |
| // i.e., [0xE8000100, 0xE8FFFFFF) |
| netAddressBits const first = 0xE8000100, lastPlus1 = 0xE8FFFFFF; |
| netAddressBits const range = lastPlus1 - first; |
| |
| return ntohl(first + ((netAddressBits)our_random())%range); |
| } |
| |
| char const* timestampString() { |
| struct timeval tvNow; |
| gettimeofday(&tvNow, NULL); |
| |
| #if !defined(_WIN32_WCE) |
| static char timeString[9]; // holds hh:mm:ss plus trailing '\0' |
| |
| time_t tvNow_t = tvNow.tv_sec; |
| char const* ctimeResult = ctime(&tvNow_t); |
| if (ctimeResult == NULL) { |
| sprintf(timeString, "??:??:??"); |
| } else { |
| char const* from = &ctimeResult[11]; |
| int i; |
| for (i = 0; i < 8; ++i) { |
| timeString[i] = from[i]; |
| } |
| timeString[i] = '\0'; |
| } |
| #else |
| // WinCE apparently doesn't have "ctime()", so instead, construct |
| // a timestamp string just using the integer and fractional parts |
| // of "tvNow": |
| static char timeString[50]; |
| sprintf(timeString, "%lu.%06ld", tvNow.tv_sec, tvNow.tv_usec); |
| #endif |
| |
| return (char const*)&timeString; |
| } |
| |
| #if (defined(__WIN32__) || defined(_WIN32)) && !defined(__MINGW32__) |
| // For Windoze, we need to implement our own gettimeofday() |
| |
| // used to make sure that static variables in gettimeofday() aren't initialized simultaneously by multiple threads |
| static LONG initializeLock_gettimeofday = 0; |
| |
| #if !defined(_WIN32_WCE) |
| #include <sys/timeb.h> |
| #endif |
| |
| int gettimeofday(struct timeval* tp, int* /*tz*/) { |
| static LARGE_INTEGER tickFrequency, epochOffset; |
| |
| static Boolean isInitialized = False; |
| |
| LARGE_INTEGER tickNow; |
| |
| #if !defined(_WIN32_WCE) |
| QueryPerformanceCounter(&tickNow); |
| #else |
| tickNow.QuadPart = GetTickCount(); |
| #endif |
| |
| if (!isInitialized) { |
| if(1 == InterlockedIncrement(&initializeLock_gettimeofday)) { |
| #if !defined(_WIN32_WCE) |
| // For our first call, use "ftime()", so that we get a time with a proper epoch. |
| // For subsequent calls, use "QueryPerformanceCount()", because it's more fine-grain. |
| struct timeb tb; |
| ftime(&tb); |
| tp->tv_sec = tb.time; |
| tp->tv_usec = 1000*tb.millitm; |
| |
| // Also get our counter frequency: |
| QueryPerformanceFrequency(&tickFrequency); |
| #else |
| /* FILETIME of Jan 1 1970 00:00:00. */ |
| const LONGLONG epoch = 116444736000000000LL; |
| FILETIME fileTime; |
| LARGE_INTEGER time; |
| GetSystemTimeAsFileTime(&fileTime); |
| |
| time.HighPart = fileTime.dwHighDateTime; |
| time.LowPart = fileTime.dwLowDateTime; |
| |
| // convert to from 100ns time to unix timestamp in seconds, 1000*1000*10 |
| tp->tv_sec = (long)((time.QuadPart - epoch) / 10000000L); |
| |
| /* |
| GetSystemTimeAsFileTime has just a seconds resolution, |
| thats why wince-version of gettimeofday is not 100% accurate, usec accuracy would be calculated like this: |
| // convert 100 nanoseconds to usec |
| tp->tv_usec= (long)((time.QuadPart - epoch)%10000000L) / 10L; |
| */ |
| tp->tv_usec = 0; |
| |
| // resolution of GetTickCounter() is always milliseconds |
| tickFrequency.QuadPart = 1000; |
| #endif |
| // compute an offset to add to subsequent counter times, so we get a proper epoch: |
| epochOffset.QuadPart |
| = tp->tv_sec * tickFrequency.QuadPart + (tp->tv_usec * tickFrequency.QuadPart) / 1000000L - tickNow.QuadPart; |
| |
| // next caller can use ticks for time calculation |
| isInitialized = True; |
| return 0; |
| } else { |
| InterlockedDecrement(&initializeLock_gettimeofday); |
| // wait until first caller has initialized static values |
| while(!isInitialized){ |
| Sleep(1); |
| } |
| } |
| } |
| |
| // adjust our tick count so that we get a proper epoch: |
| tickNow.QuadPart += epochOffset.QuadPart; |
| |
| tp->tv_sec = (long)(tickNow.QuadPart / tickFrequency.QuadPart); |
| tp->tv_usec = (long)(((tickNow.QuadPart % tickFrequency.QuadPart) * 1000000L) / tickFrequency.QuadPart); |
| |
| return 0; |
| } |
| #endif |
| #undef ANDROID_OLD_NDK |