/** * Copyright - See the COPYRIGHT that is included with this distribution. * pvAccessCPP is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. */ #include #include #include #include #include #include #include #include #include #include #define epicsExportSharedSymbols #include // RTEMS 4.9 doesn't define this, but does implement SIOCGIFNETMASK // and stores under the ifr_addr union member. #ifndef ifr_netmask # define ifr_netmask ifr_addr #endif using namespace std; using namespace epics::pvData; namespace epics { namespace pvAccess { void encodeAsIPv6Address(ByteBuffer* buffer, const osiSockAddr* address) { // IPv4 compatible IPv6 address // first 80-bit are 0 buffer->putLong(0); buffer->putShort(0); // next 16-bits are 1 buffer->putShort(0xFFFF); // following IPv4 address in big-endian (network) byte order uint32_t ipv4Addr = ntohl(address->ia.sin_addr.s_addr); buffer->putByte((int8)((ipv4Addr>>24)&0xFF)); buffer->putByte((int8)((ipv4Addr>>16)&0xFF)); buffer->putByte((int8)((ipv4Addr>>8)&0xFF)); buffer->putByte((int8)(ipv4Addr&0xFF)); } bool decodeAsIPv6Address(ByteBuffer* buffer, osiSockAddr* address) { // IPv4 compatible IPv6 address expected // first 80-bit are 0 if (buffer->getLong() != 0) return false; if (buffer->getShort() != 0) return false; int16 ffff = buffer->getShort(); // allow all zeros address //if (ffff != (int16)0xFFFF) return false; uint32 ipv4Addr = uint8(buffer->getByte()); ipv4Addr <<= 8; ipv4Addr |= uint8(buffer->getByte()); ipv4Addr <<= 8; ipv4Addr |= uint8(buffer->getByte()); ipv4Addr <<= 8; ipv4Addr |= uint8(buffer->getByte()); if (ffff != (int16)0xFFFF && ipv4Addr != (uint32_t)0) return false; address->ia.sin_addr.s_addr = htonl(ipv4Addr); return true; } 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; } void intToIPv4Address(osiSockAddr& ret, int32 addr) { memset(&ret, 0, sizeof(ret)); ret.ia.sin_family = AF_INET; ret.ia.sin_addr.s_addr = htonl(addr); ret.ia.sin_port = 0; } int32 ipv4AddressToInt(const osiSockAddr& addr) { return (int32)ntohl(addr.ia.sin_addr.s_addr); } void getSocketAddressList(InetAddrVector& ret, const std::string & list, int defaultPort, const InetAddrVector* appendList) { ret.clear(); // skip leading spaces size_t len = list.length(); size_t subStart = 0; while (subStart < len && isspace(list[subStart])) subStart++; // parse string size_t subEnd; while((subEnd = list.find(' ', subStart))!=std::string::npos) { string address = list.substr(subStart, (subEnd-subStart)); osiSockAddr addr; if (aToIPAddr(address.c_str(), defaultPort, &addr.ia) == 0) ret.push_back(addr); subStart = list.find_first_not_of(" \t\r\n\v", subEnd); } if(subStart!=std::string::npos && subStartsize(); i++) ret.push_back((*appendList)[i]); } } string inetAddressToString(const osiSockAddr &addr, bool displayPort, bool displayHex) { stringstream saddr; int ipa = ntohl(addr.ia.sin_addr.s_addr); saddr<<((int)(ipa>>24)&0xFF)<<'.'; saddr<<((int)(ipa>>16)&0xFF)<<'.'; saddr<<((int)(ipa>>8)&0xFF)<<'.'; saddr<<((int)ipa&0xFF); if(displayPort) saddr<<":"<ifr_addr.sa_family != AF_INET ) { continue; } /* * if it isnt a wildcarded interface then look for * an exact match */ match = 0; if ( pMatchAddr && pMatchAddr->sa.sa_family != AF_UNSPEC ) { if ( pMatchAddr->sa.sa_family != AF_INET ) { continue; } if ( pMatchAddr->ia.sin_addr.s_addr != htonl (INADDR_ANY) ) { struct sockaddr_in *pInetAddr = (struct sockaddr_in *) &pIfreqList->ifr_addr; if ( pInetAddr->sin_addr.s_addr != pMatchAddr->ia.sin_addr.s_addr ) { continue; } else match = 1; } } ifaceNode node; node.addr.sa = pIfreqList->ifr_addr; status = socket_ioctl ( socket, SIOCGIFFLAGS, pIfreqList ); if ( status ) { errlogPrintf ("discoverInterfaces(): net intf flags fetch for \"%s\" failed\n", pIfreqList->ifr_name); continue; } unsigned short ifflags = pIfreqList->ifr_flags; node.loopback = ifflags & IFF_LOOPBACK; /* * dont bother with interfaces that have been disabled */ if ( ! ( ifflags & IFF_UP ) ) { continue; } /* * dont use the loop back interface, unless it maches pMatchAddr */ if (!match) { if ( ifflags & IFF_LOOPBACK ) { continue; } } /* * If this is an interface that supports * broadcast fetch the broadcast address. * * Otherwise if this is a point to point * interface then use the destination address. * * Otherwise CA will not query through the * interface. */ if ( ifflags & IFF_BROADCAST ) { status = socket_ioctl (socket, SIOCGIFBRDADDR, pIfreqList); if ( status ) { errlogPrintf ("discoverInterfaces(): net intf \"%s\": bcast addr fetch fail\n", pIfreqList->ifr_name); continue; } node.bcast.sa = pIfreqList->ifr_broadaddr; status = socket_ioctl (socket, SIOCGIFNETMASK, pIfreqList); if ( status ) { errlogPrintf ("discoverInterfaces(): net intf \"%s\": netmask fetch fail\n", pIfreqList->ifr_name); continue; } node.mask.sa = pIfreqList->ifr_netmask; checkNode(node); node.validBcast = true; } #if defined (IFF_POINTOPOINT) else if ( ifflags & IFF_POINTOPOINT ) { status = socket_ioctl ( socket, SIOCGIFDSTADDR, pIfreqList); if ( status ) { continue; } node.peer.sa = pIfreqList->ifr_dstaddr; node.validP2P = true; } #endif else { // if it is a match, accept the interface even if it does not support broadcast (i.e. 127.0.0.1 or point to point) if (!match) { continue; } } list.push_back(node); } free ( pIfreqList ); return 0; } #else #define VC_EXTRALEAN #include #include int discoverInterfaces(IfaceNodeVector &list, SOCKET socket, const osiSockAddr *pMatchAddr) { int status; INTERFACE_INFO *pIfinfo; INTERFACE_INFO *pIfinfoList; unsigned nelem; int numifs; DWORD cbBytesReturned; int match; /* only valid for winsock 2 and above TODO resolve dllimport compilation problem and uncomment this check if (wsaMajorVersion() < 2 ) { fprintf(stderr, "Need to set EPICS_CA_AUTO_ADDR_LIST=NO for winsock 1\n"); return -1; } */ nelem = 100; pIfinfoList = (INTERFACE_INFO *) calloc(nelem, sizeof(INTERFACE_INFO)); if(!pIfinfoList) { return -1; } status = WSAIoctl (socket, SIO_GET_INTERFACE_LIST, NULL, 0, (LPVOID)pIfinfoList, nelem*sizeof(INTERFACE_INFO), &cbBytesReturned, NULL, NULL); if (status != 0 || cbBytesReturned == 0) { fprintf(stderr, "WSAIoctl SIO_GET_INTERFACE_LIST failed %d\n",WSAGetLastError()); free(pIfinfoList); return -1; } numifs = cbBytesReturned/sizeof(INTERFACE_INFO); for (pIfinfo = pIfinfoList; pIfinfo < (pIfinfoList+numifs); pIfinfo++) { /* * dont bother with interfaces that have been disabled */ if (!(pIfinfo->iiFlags & IFF_UP)) { continue; } /* * If its not an internet interface then dont use it * + work around WS2 bug */ if (pIfinfo->iiAddress.Address.sa_family != AF_INET) { if (pIfinfo->iiAddress.Address.sa_family == 0) { pIfinfo->iiAddress.Address.sa_family = AF_INET; } else continue; } /* * if it isnt a wildcarded interface then look for * an exact match */ match = 0; if (pMatchAddr && pMatchAddr->sa.sa_family != AF_UNSPEC) { if (pIfinfo->iiAddress.Address.sa_family != pMatchAddr->sa.sa_family) { continue; } if (pIfinfo->iiAddress.Address.sa_family != AF_INET) { continue; } if (pMatchAddr->sa.sa_family != AF_INET) { continue; } if (pMatchAddr->ia.sin_addr.s_addr != htonl(INADDR_ANY)) { if (pIfinfo->iiAddress.AddressIn.sin_addr.s_addr != pMatchAddr->ia.sin_addr.s_addr) { continue; } else match = 1; } } /* * dont use the loop back interface, unless it maches pMatchAddr */ if (!match) { if (pIfinfo->iiFlags & IFF_LOOPBACK) { continue; } } ifaceNode node; node.loopback = pIfinfo->iiFlags & IFF_LOOPBACK; node.addr.ia = pIfinfo->iiAddress.AddressIn; if (pIfinfo->iiFlags & IFF_BROADCAST) { node.mask.ia = pIfinfo->iiNetmask.AddressIn; node.bcast.ia = pIfinfo->iiBroadcastAddress.AddressIn; node.validBcast = true; } else if (pIfinfo->iiFlags & IFF_POINTTOPOINT) { node.peer.ia = pIfinfo->iiNetmask.AddressIn; node.validP2P = true; } checkNode(node); list.push_back(node); } free (pIfinfoList); return 0; } #endif } }