diff --git a/pvAccessApp/remote/blockingUDP.h b/pvAccessApp/remote/blockingUDP.h index cad6b22..02dc3da 100644 --- a/pvAccessApp/remote/blockingUDP.h +++ b/pvAccessApp/remote/blockingUDP.h @@ -35,6 +35,8 @@ namespace epics { namespace pvAccess { + enum InetAddressType { inetAddressType_all, inetAddressType_unicast, inetAddressType_broadcast_multicast }; + class BlockingUDPTransport : public epics::pvData::NoDefaultMethods, public Transport, public TransportSendControl, @@ -65,8 +67,7 @@ namespace epics { } virtual const osiSockAddr* getRemoteAddress() const { - // always connected - return &_bindAddress; + return &_remoteAddress; } virtual epics::pvData::String getType() const { @@ -228,7 +229,7 @@ namespace epics { bool send(epics::pvData::ByteBuffer* buffer, const osiSockAddr& address); - bool send(epics::pvData::ByteBuffer* buffer); + bool send(epics::pvData::ByteBuffer* buffer, InetAddressType target = inetAddressType_all); /** * Get list of send addresses. @@ -246,15 +247,31 @@ namespace epics { return &_bindAddress; } + bool isBroadcastAddress(const osiSockAddr* address, InetAddrVector *broadcastAddresses) + { + if (broadcastAddresses) + for (size_t i = 0; i < broadcastAddresses->size(); i++) + if ((*broadcastAddresses)[i].ia.sin_addr.s_addr == address->ia.sin_addr.s_addr) + return true; + return false; + } + /** * Set list of send addresses. * @param addresses list of send addresses, non-null. */ - void setBroadcastAddresses(InetAddrVector* addresses) { + void setSendAddresses(InetAddrVector* addresses) { if (addresses) { if (!_sendAddresses) _sendAddresses = new InetAddrVector; *_sendAddresses = *addresses; + + std::auto_ptr broadcastAddresses(getBroadcastAddresses(_channel, 0)); + _isSendAddressUnicast.resize(_sendAddresses->size()); + for (std::size_t i = 0; i < _sendAddresses->size(); i++) + _isSendAddressUnicast[i] = + !isBroadcastAddress(&(*_sendAddresses)[i], broadcastAddresses.get()) && + !isMulticastAddress(&(*_sendAddresses)[i]); } else { @@ -291,11 +308,18 @@ namespace epics { */ osiSockAddr _bindAddress; + /** + * Remote address. + */ + osiSockAddr _remoteAddress; + /** * Send addresses. */ InetAddrVector* _sendAddresses; + std::vector _isSendAddressUnicast; + /** * Ignore addresses. */ diff --git a/pvAccessApp/remote/blockingUDPConnector.cpp b/pvAccessApp/remote/blockingUDPConnector.cpp index ce6fa7b..a0a9ac4 100644 --- a/pvAccessApp/remote/blockingUDPConnector.cpp +++ b/pvAccessApp/remote/blockingUDPConnector.cpp @@ -45,7 +45,13 @@ namespace epics { } /* - IPv4 multicast addresses are defined by the leading address bits of 1110, originating from the classful network design of the early Internet when this group of addresses was designated as Class D. The Classless Inter-Domain Routing (CIDR) prefix of this group is 224.0.0.0/4. The group includes the addresses from 224.0.0.0 to 239.255.255.255. Address assignments from within this range are specified in RFC 5771, an Internet Engineering Task Force (IETF) Best Current Practice document (BCP 51).*/ + IPv4 multicast addresses are defined by the leading address bits of 1110, + originating from the classful network design of the early Internet when this + group of addresses was designated as Class D. The Classless Inter-Domain Routing (CIDR) + prefix of this group is 224.0.0.0/4. + The group includes the addresses from 224.0.0.0 to 239.255.255.255. + Address assignments from within this range are specified in RFC 5771, + an Internet Engineering Task Force (IETF) Best Current Practice document (BCP 51).*/ // set SO_REUSEADDR or SO_REUSEPORT, OS dependant diff --git a/pvAccessApp/remote/blockingUDPTransport.cpp b/pvAccessApp/remote/blockingUDPTransport.cpp index 3d20eae..934bf5c 100644 --- a/pvAccessApp/remote/blockingUDPTransport.cpp +++ b/pvAccessApp/remote/blockingUDPTransport.cpp @@ -52,6 +52,18 @@ inline int sendto(int s, const char *buf, size_t len, int flags, const struct so _threadId(0) { PVACCESS_REFCOUNT_MONITOR_CONSTRUCT(blockingUDPTransport); + + osiSocklen_t sockLen = sizeof(sockaddr); + // read the actual socket info + int retval = ::getsockname(_channel, &_remoteAddress.sa, &sockLen); + if(retval<0) { + // error obtaining remote address, fallback to bindAddress + _remoteAddress = _bindAddress; + + char strBuffer[64]; + epicsSocketConvertErrnoToString(strBuffer, sizeof(strBuffer)); + LOG(logLevelDebug, "getsockname error: %s", strBuffer); + } } BlockingUDPTransport::~BlockingUDPTransport() { @@ -332,16 +344,26 @@ inline int sendto(int s, const char *buf, size_t len, int flags, const struct so return false; } + // all sent + buffer->setPosition(buffer->getLimit()); + return true; } - bool BlockingUDPTransport::send(ByteBuffer* buffer) { + bool BlockingUDPTransport::send(ByteBuffer* buffer, InetAddressType target) { if(!_sendAddresses) return false; buffer->flip(); bool allOK = true; for(size_t i = 0; i<_sendAddresses->size(); i++) { + + // filter + if (target != inetAddressType_all) + if ((target == inetAddressType_unicast && !_isSendAddressUnicast[i]) || + (target == inetAddressType_broadcast_multicast && _isSendAddressUnicast[i])) + continue; + int retval = sendto(_channel, buffer->getArray(), buffer->getLimit(), 0, &((*_sendAddresses)[i].sa), sizeof(sockaddr)); @@ -354,6 +376,9 @@ inline int sendto(int s, const char *buf, size_t len, int flags, const struct so } } + // all sent + buffer->setPosition(buffer->getLimit()); + return allOK; } diff --git a/pvAccessApp/remote/simpleChannelSearchManagerImpl.cpp b/pvAccessApp/remote/simpleChannelSearchManagerImpl.cpp index f747bf5..bf234a6 100644 --- a/pvAccessApp/remote/simpleChannelSearchManagerImpl.cpp +++ b/pvAccessApp/remote/simpleChannelSearchManagerImpl.cpp @@ -20,8 +20,9 @@ using namespace epics::pvData; namespace epics { namespace pvAccess { -const int SimpleChannelSearchManagerImpl::DATA_COUNT_POSITION = PVA_MESSAGE_HEADER_SIZE + sizeof(int32)/sizeof(int8) + 1; -const int SimpleChannelSearchManagerImpl::PAYLOAD_POSITION = sizeof(int16)/sizeof(int8) + 2; +const int SimpleChannelSearchManagerImpl::DATA_COUNT_POSITION = PVA_MESSAGE_HEADER_SIZE + 4+1+3+16+2+1+4; +const int SimpleChannelSearchManagerImpl::CAST_POSITION = PVA_MESSAGE_HEADER_SIZE + 4; +const int SimpleChannelSearchManagerImpl::PAYLOAD_POSITION = 4; // 225ms +/- 25ms random const double SimpleChannelSearchManagerImpl::ATOMIC_PERIOD = 0.225; @@ -46,6 +47,7 @@ SimpleChannelSearchManagerImpl::create(Context::shared_pointer const & context) SimpleChannelSearchManagerImpl::SimpleChannelSearchManagerImpl(Context::shared_pointer const & context) : m_context(context), + m_responseAddress(*context->getSearchTransport()->getRemoteAddress()), m_canceled(), m_sequenceNumber(0), m_sendBuffer(MAX_UDP_UNFRAGMENTED_SEND), @@ -56,6 +58,7 @@ SimpleChannelSearchManagerImpl::SimpleChannelSearchManagerImpl(Context::shared_p m_userValueMutex(), m_mutex() { + // initialize send buffer initializeSendBuffer(); @@ -177,15 +180,25 @@ void SimpleChannelSearchManagerImpl::initializeSendBuffer() m_sendBuffer.putByte(PVA_VERSION); m_sendBuffer.putByte((EPICS_BYTE_ORDER == EPICS_ENDIAN_BIG) ? 0x80 : 0x00); // data + 7-bit endianess m_sendBuffer.putByte((int8_t)3); // search - m_sendBuffer.putInt(sizeof(int32_t)/sizeof(int8_t) + 1); // "zero" payload + m_sendBuffer.putInt(4+1+3+16+2+1); // "zero" payload m_sendBuffer.putInt(m_sequenceNumber); - /* - final boolean REQUIRE_REPLY = false; - sendBuffer.put(REQUIRE_REPLY ? (byte)QoS.REPLY_REQUIRED.getMaskValue() : (byte)QoS.DEFAULT.getMaskValue()); - */ + // multicast vs unicast mask + m_sendBuffer.putByte((int8_t)0); - m_sendBuffer.putByte((int8_t)QOS_DEFAULT); + // reserved part + m_sendBuffer.putByte((int8_t)0); + m_sendBuffer.putShort((int16_t)0); + + // NOTE: is it possible (very likely) that address is any local address ::ffff:0.0.0.0 + encodeAsIPv6Address(&m_sendBuffer, &m_responseAddress); + m_sendBuffer.putShort((int16_t)ntohs(m_responseAddress.ia.sin_port)); + + // TODO now only TCP is supported + // note: this affects DATA_COUNT_POSITION + m_sendBuffer.putByte((int8_t)1); + // TODO "tcp" constant + SerializeHelper::serializeString("tcp", &m_sendBuffer, &m_mockTransportSendControl); m_sendBuffer.putShort((int16_t)0); // count } @@ -195,8 +208,14 @@ void SimpleChannelSearchManagerImpl::flushSendBuffer() Transport::shared_pointer tt = m_context.lock()->getSearchTransport(); BlockingUDPTransport::shared_pointer ut = std::tr1::static_pointer_cast(tt); - ut->send(&m_sendBuffer); // TODO - initializeSendBuffer(); + + m_sendBuffer.putByte(CAST_POSITION, (int8_t)0x80); // unicast, no reply required + ut->send(&m_sendBuffer, inetAddressType_unicast); + + m_sendBuffer.putByte(CAST_POSITION, (int8_t)0x00); // b/m-cast, no reply required + ut->send(&m_sendBuffer, inetAddressType_broadcast_multicast); + + initializeSendBuffer(); } diff --git a/pvAccessApp/remote/simpleChannelSearchManagerImpl.h b/pvAccessApp/remote/simpleChannelSearchManagerImpl.h index d0a2e6b..d929468 100644 --- a/pvAccessApp/remote/simpleChannelSearchManagerImpl.h +++ b/pvAccessApp/remote/simpleChannelSearchManagerImpl.h @@ -133,6 +133,11 @@ class SimpleChannelSearchManagerImpl : */ Context::weak_pointer m_context; + /** + * Response address. + */ + osiSockAddr m_responseAddress; + /** * Canceled flag. */ @@ -179,6 +184,7 @@ class SimpleChannelSearchManagerImpl : epics::pvData::Mutex m_mutex; static const int DATA_COUNT_POSITION; + static const int CAST_POSITION; static const int PAYLOAD_POSITION; static const double ATOMIC_PERIOD; diff --git a/pvAccessApp/remoteClient/clientContextImpl.cpp b/pvAccessApp/remoteClient/clientContextImpl.cpp index d778059..69a1f92 100644 --- a/pvAccessApp/remoteClient/clientContextImpl.cpp +++ b/pvAccessApp/remoteClient/clientContextImpl.cpp @@ -2573,13 +2573,12 @@ namespace epics { { AbstractClientResponseHandler::handleResponse(responseFrom, transport, version, command, payloadSize, payloadBuffer); - transport->ensureData(5); - int32 searchSequenceId = payloadBuffer->getInt(); - bool found = payloadBuffer->getByte() != 0; - if (!found) - return; + transport->ensureData(12+4+16+2); - transport->ensureData((128+2*16)/8); + GUID guid; + payloadBuffer->get(guid.value, 0, sizeof(guid.value)); + + int32 searchSequenceId = payloadBuffer->getInt(); osiSockAddr serverAddress; serverAddress.ia.sin_family = AF_INET; @@ -2603,7 +2602,14 @@ namespace epics { serverAddress.ia.sin_addr = responseFrom->ia.sin_addr; serverAddress.ia.sin_port = htons(payloadBuffer->getShort()); - + + /*String protocol =*/ SerializeHelper::deserializeString(payloadBuffer, transport.get()); + + transport->ensureData(1); + bool found = payloadBuffer->getByte() != 0; + if (!found) + return; + // reads CIDs // TODO optimize std::tr1::shared_ptr csm = _context.lock()->getChannelSearchManager(); @@ -4070,14 +4076,14 @@ TODO m_connector.reset(new BlockingTCPConnector(thisPointer, m_receiveBufferSize, m_beaconPeriod)); m_transportRegistry.reset(new TransportRegistry()); - // setup search manager - m_channelSearchManager = SimpleChannelSearchManagerImpl::create(thisPointer); - // TODO put memory barrier here... (if not already called withing a lock?) // setup UDP transport initializeUDPTransport(); + // setup search manager + m_channelSearchManager = SimpleChannelSearchManagerImpl::create(thisPointer); + // TODO what if initialization failed!!! } @@ -4129,7 +4135,7 @@ TODO PVA_DEFAULT_PRIORITY)); if (!m_broadcastTransport.get()) return false; - m_broadcastTransport->setBroadcastAddresses(broadcastAddresses.get()); + m_broadcastTransport->setSendAddresses(broadcastAddresses.get()); // undefined address osiSockAddr undefinedAddress; @@ -4145,7 +4151,7 @@ TODO PVA_DEFAULT_PRIORITY)); if (!m_searchTransport.get()) return false; - m_searchTransport->setBroadcastAddresses(broadcastAddresses.get()); + m_searchTransport->setSendAddresses(broadcastAddresses.get()); // become active m_broadcastTransport->start(); diff --git a/pvAccessApp/server/responseHandlers.cpp b/pvAccessApp/server/responseHandlers.cpp index d1b94da..dbb7ec1 100644 --- a/pvAccessApp/server/responseHandlers.cpp +++ b/pvAccessApp/server/responseHandlers.cpp @@ -176,6 +176,8 @@ void ServerIntrospectionSearchHandler::handleResponse(osiSockAddr* responseFrom, /****************************************************************************************/ +std::string ServerSearchHandler::SUPPORTED_PROTOCOL = "tcp"; + ServerSearchHandler::ServerSearchHandler(ServerContextImpl::shared_pointer const & context) : AbstractServerResponseHandler(context, "Search request"), _providers(context->getChannelProviders()) { @@ -188,48 +190,110 @@ void ServerSearchHandler::handleResponse(osiSockAddr* responseFrom, AbstractServerResponseHandler::handleResponse(responseFrom, transport, version, command, payloadSize, payloadBuffer); - transport->ensureData((sizeof(int32)+sizeof(int16))/sizeof(int8)+1); + transport->ensureData(4+1+3+16+2); const int32 searchSequenceId = payloadBuffer->getInt(); const int8 qosCode = payloadBuffer->getByte(); + + // reserved part + payloadBuffer->getByte(); + payloadBuffer->getShort(); + + osiSockAddr responseAddress; + responseAddress.ia.sin_family = AF_INET; + + // 128-bit IPv6 address + /* +int8* byteAddress = new int8[16]; +for (int i = 0; i < 16; i++) +byteAddress[i] = payloadBuffer->getByte(); }; + */ + + // IPv4 compatible IPv6 address expected + // first 80-bit are 0 + if (payloadBuffer->getLong() != 0) return; + if (payloadBuffer->getShort() != 0) return; + if (payloadBuffer->getShort() != (int16)0xFFFF) return; + + // accept given address if explicitly specified by sender + responseAddress.ia.sin_addr.s_addr = htonl(payloadBuffer->getInt()); + if (responseAddress.ia.sin_addr.s_addr == INADDR_ANY) + responseAddress.ia.sin_addr = responseFrom->ia.sin_addr; + + responseAddress.ia.sin_port = htons(payloadBuffer->getShort()); + + size_t protocolsCount = SerializeHelper::readSize(payloadBuffer, transport.get()); + bool allowed = (protocolsCount == 0); + for (size_t i = 0; i < protocolsCount; i++) + { + String protocol = SerializeHelper::deserializeString(payloadBuffer, transport.get()); + if (SUPPORTED_PROTOCOL == protocol) + allowed = true; + } + + // NOTE: we do not stop reading the buffer + + transport->ensureData(2); const int32 count = payloadBuffer->getShort() & 0xFFFF; + const bool responseRequired = (QOS_REPLY_REQUIRED & qosCode) != 0; - for (int32 i = 0; i < count; i++) - { - transport->ensureData(sizeof(int32)/sizeof(int8)); - const int32 cid = payloadBuffer->getInt(); - const String name = SerializeHelper::deserializeString(payloadBuffer, transport.get()); - // no name check here... + // TODO locally broadcast if qosCode & 0x80 == 0x80 - // TODO object pool!!! - int providerCount = _providers.size(); - ServerChannelFindRequesterImpl* pr = new ServerChannelFindRequesterImpl(_context, providerCount); - pr->set(name, searchSequenceId, cid, responseFrom, responseRequired); - ChannelFindRequester::shared_pointer spr(pr); - - for (int i = 0; i < providerCount; i++) - _providers[i]->channelFind(name, spr); - } + if (count > 0) + { + for (int32 i = 0; i < count; i++) + { + transport->ensureData(4); + const int32 cid = payloadBuffer->getInt(); + const String name = SerializeHelper::deserializeString(payloadBuffer, transport.get()); + // no name check here... + + if (allowed) + { + // TODO object pool!!! + int providerCount = _providers.size(); + ServerChannelFindRequesterImpl* pr = new ServerChannelFindRequesterImpl(_context, providerCount); + pr->set(name, searchSequenceId, cid, responseAddress, responseRequired, false); + ChannelFindRequester::shared_pointer spr(pr); + + for (int i = 0; i < providerCount; i++) + _providers[i]->channelFind(name, spr); + } + } + } + else + { + if (allowed) + { + ServerChannelFindRequesterImpl* pr = new ServerChannelFindRequesterImpl(_context, 1); + pr->set("", searchSequenceId, 0, responseAddress, true, true); + ChannelFindRequester::shared_pointer spr(pr); + spr->channelFindResult(Status::Ok, ChannelFind::shared_pointer(), false); + } + } } ServerChannelFindRequesterImpl::ServerChannelFindRequesterImpl(ServerContextImpl::shared_pointer const & context, int32 expectedResponseCount) : - _sendTo(NULL), + _guid(context->getGUID()), + _sendTo(), _wasFound(false), _context(context), _expectedResponseCount(expectedResponseCount), - _responseCount(0) + _responseCount(0), + _serverSearch(false) {} void ServerChannelFindRequesterImpl::clear() { Lock guard(_mutex); - _sendTo = NULL; _wasFound = false; _responseCount = 0; + _serverSearch = false; } -ServerChannelFindRequesterImpl* ServerChannelFindRequesterImpl::set(String name, int32 searchSequenceId, int32 cid, osiSockAddr* sendTo, bool responseRequired) +ServerChannelFindRequesterImpl* ServerChannelFindRequesterImpl::set(String name, int32 searchSequenceId, int32 cid, osiSockAddr const & sendTo, + bool responseRequired, bool serverSearch) { Lock guard(_mutex); _name = name; @@ -237,6 +301,7 @@ ServerChannelFindRequesterImpl* ServerChannelFindRequesterImpl::set(String name, _cid = cid; _sendTo = sendTo; _responseRequired = responseRequired; + _serverSearch = serverSearch; return this; } @@ -288,20 +353,34 @@ void ServerChannelFindRequesterImpl::unlock() void ServerChannelFindRequesterImpl::send(ByteBuffer* buffer, TransportSendControl* control) { - int32 count = 1; - control->startMessage((int8)4, (sizeof(int32)+sizeof(int8)+128+2*sizeof(int16)+count*sizeof(int32))/sizeof(int8)); + control->startMessage((int8)4, 12+4+16+2); Lock guard(_mutex); + buffer->put(_guid.value, 0, sizeof(_guid.value)); buffer->putInt(_searchSequenceId); - buffer->putByte(_wasFound ? (int8)1 : (int8)0); // NOTE: is it possible (very likely) that address is any local address ::ffff:0.0.0.0 encodeAsIPv6Address(buffer, _context->getServerInetAddress()); buffer->putShort((int16)_context->getServerPort()); - buffer->putShort((int16)count); - buffer->putInt(_cid); - control->setRecipient(*_sendTo); + SerializeHelper::serializeString(ServerSearchHandler::SUPPORTED_PROTOCOL, buffer, control); + + control->ensureBuffer(1); + buffer->putByte(_wasFound ? (int8)1 : (int8)0); + + if (!_serverSearch) + { + // TODO for now we do not gather search responses + buffer->putShort((int16)1); + buffer->putInt(_cid); + } + else + { + buffer->putShort((int16)0); + } + + + control->setRecipient(_sendTo); } /****************************************************************************************/ diff --git a/pvAccessApp/server/responseHandlers.h b/pvAccessApp/server/responseHandlers.h index 01d23aa..a509069 100644 --- a/pvAccessApp/server/responseHandlers.h +++ b/pvAccessApp/server/responseHandlers.h @@ -159,7 +159,9 @@ namespace pvAccess { public: // TODO static std::map > s_channelNameToProvider; - + + static std::string SUPPORTED_PROTOCOL; + ServerSearchHandler(ServerContextImpl::shared_pointer const & context); virtual ~ServerSearchHandler(){} @@ -168,7 +170,7 @@ namespace pvAccess { std::size_t payloadSize, epics::pvData::ByteBuffer* payloadBuffer); private: - std::vector _providers; + std::vector _providers; }; @@ -181,22 +183,25 @@ namespace pvAccess { ServerChannelFindRequesterImpl(ServerContextImpl::shared_pointer const & context, epics::pvData::int32 expectedResponseCount); virtual ~ServerChannelFindRequesterImpl(){} void clear(); - ServerChannelFindRequesterImpl* set(epics::pvData::String _name, epics::pvData::int32 searchSequenceId, epics::pvData::int32 cid, osiSockAddr* sendTo, bool responseRequired); + ServerChannelFindRequesterImpl* set(epics::pvData::String _name, epics::pvData::int32 searchSequenceId, + epics::pvData::int32 cid, osiSockAddr const & sendTo, bool responseRequired, bool serverSearch); void channelFindResult(const epics::pvData::Status& status, ChannelFind::shared_pointer const & channelFind, bool wasFound); void lock(); void unlock(); void send(epics::pvData::ByteBuffer* buffer, TransportSendControl* control); private: + GUID _guid; epics::pvData::String _name; epics::pvData::int32 _searchSequenceId; epics::pvData::int32 _cid; - osiSockAddr* _sendTo; + osiSockAddr _sendTo; bool _responseRequired; bool _wasFound; ServerContextImpl::shared_pointer _context; epics::pvData::Mutex _mutex; epics::pvData::int32 _expectedResponseCount; epics::pvData::int32 _responseCount; + bool _serverSearch; }; /****************************************************************************************/ diff --git a/pvAccessApp/server/serverContext.cpp b/pvAccessApp/server/serverContext.cpp index b0bcc57..a028a0a 100644 --- a/pvAccessApp/server/serverContext.cpp +++ b/pvAccessApp/server/serverContext.cpp @@ -74,6 +74,7 @@ const Version& ServerContextImpl::getVersion() void ServerContextImpl::generateGUID() { + // TODO use UUID epics::pvData::TimeStamp startupTime; startupTime.getCurrent(); @@ -84,7 +85,7 @@ void ServerContextImpl::generateGUID() void ServerContextImpl::initializeLogger() { - //createFileLogger("serverContextImpl.log"); + //createFileLogger("serverContextImpl.log"); } struct noop_deleter @@ -265,7 +266,7 @@ void ServerContextImpl::initializeBroadcastTransport() nullTransportClient, responseHandler, listenLocalAddress, PVA_PROTOCOL_REVISION, PVA_DEFAULT_PRIORITY)); - _broadcastTransport->setBroadcastAddresses(broadcastAddresses.get()); + _broadcastTransport->setSendAddresses(broadcastAddresses.get()); // set ignore address list if (!_ignoreAddressList.empty()) @@ -290,7 +291,7 @@ void ServerContextImpl::initializeBroadcastTransport() auto_ptr list(getSocketAddressList(_beaconAddressList, _broadcastPort, appendList)); if (list.get() != NULL && list->size() > 0) { - _broadcastTransport->setBroadcastAddresses(list.get()); + _broadcastTransport->setSendAddresses(list.get()); } } diff --git a/pvAccessApp/utils/inetAddressUtil.cpp b/pvAccessApp/utils/inetAddressUtil.cpp index 31996a3..2408118 100644 --- a/pvAccessApp/utils/inetAddressUtil.cpp +++ b/pvAccessApp/utils/inetAddressUtil.cpp @@ -64,6 +64,12 @@ void encodeAsIPv6Address(ByteBuffer* buffer, const osiSockAddr* address) { buffer->putByte((int8)(ipv4Addr&0xFF)); } +bool isMulticastAddress(const osiSockAddr* address) { + uint32_t ipv4Addr = ntohl(address->ia.sin_addr.s_addr); + uint8_t msB = (uint8_t)((ipv4Addr>>24)&0xFF); + return msB >= 224 && msB <= 239; +} + osiSockAddr* intToIPv4Address(int32 addr) { osiSockAddr* ret = new osiSockAddr; ret->ia.sin_family = AF_INET; diff --git a/pvAccessApp/utils/inetAddressUtil.h b/pvAccessApp/utils/inetAddressUtil.h index c33f086..2c43826 100644 --- a/pvAccessApp/utils/inetAddressUtil.h +++ b/pvAccessApp/utils/inetAddressUtil.h @@ -49,6 +49,13 @@ namespace pvAccess { */ epicsShareFunc void encodeAsIPv6Address(epics::pvData::ByteBuffer* buffer, const osiSockAddr* address); + /** + * Check if an IPv4 address is a multicast address. + * @param address IPv4 address to check. + * @return true if the adress is a multicast address. + */ + epicsShareFunc bool isMulticastAddress(const osiSockAddr* address); + /** * Convert an integer into an IPv4 INET address. * @param addr integer representation of a given address. diff --git a/testApp/utils/testInetAddressUtils.cpp b/testApp/utils/testInetAddressUtils.cpp index c79845a..b2e06a5 100644 --- a/testApp/utils/testInetAddressUtils.cpp +++ b/testApp/utils/testInetAddressUtils.cpp @@ -125,6 +125,22 @@ void test_encodeAsIPv6Address() testOk1(strncmp(buff->getArray(), src, 16) == 0); } +void test_isMulticastAddress() +{ + testDiag("Test test_isMulticastAddress()"); + + auto_ptr vec(getSocketAddressList("127.0.0.1 255.255.255.255 0.0.0.0 224.0.0.0 239.255.255.255 235.3.6.3", 0)); + + testOk1(static_cast(6) == vec->size()); + + testOk1(!isMulticastAddress(&vec->at(0))); + testOk1(!isMulticastAddress(&vec->at(1))); + testOk1(!isMulticastAddress(&vec->at(2))); + testOk1(isMulticastAddress(&vec->at(3))); + testOk1(isMulticastAddress(&vec->at(4))); + testOk1(isMulticastAddress(&vec->at(5))); +} + void test_getBroadcastAddresses() { testDiag("Test getBroadcastAddresses()"); @@ -146,13 +162,14 @@ void test_getBroadcastAddresses() MAIN(testInetAddressUtils) { - testPlan(44); + testPlan(51); testDiag("Tests for InetAddress utils"); test_getSocketAddressList(); test_ipv4AddressToInt(); test_intToIPv4Address(); test_encodeAsIPv6Address(); + test_isMulticastAddress(); test_getBroadcastAddresses(); return testDone();