/* * * @APPLE_LICENSE_HEADER_START@ * * Copyright (c) 1999-2008 Apple Inc. All Rights Reserved. * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in * compliance with the License. Please obtain a copy of the License at * http://www.opensource.apple.com/apsl/ and read it before using this * file. * * The Original Code and all software distributed under the License are * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. * Please see the License for the specific language governing rights and * limitations under the License. * * @APPLE_LICENSE_HEADER_END@ * */ /* File: SocketUtils.cpp Contains: Implements utility functions defined in SocketUtils.h */ #include #ifndef __Win32__ #include #include #include #include #include #include #if __FreeBSD__ #include #endif #include #include #if __solaris__ #include #endif #endif #include "SocketUtils.h" #ifdef SIOCGIFNUM #define USE_SIOCGIFNUM 1 #endif #ifdef TRUCLUSTER /* Tru64 Cluster Alias support */ #include #include static clua_status_t (*clua_getaliasaddress_vector) (struct sockaddr *, int *); static char *(*clua_error_vector) (clua_status_t); #define clua_getaliasaddress (*clua_getaliasaddress_vector) #define clua_error (*clua_error_vector) struct clucall_vector clua_vectors[] = { { "clua_getaliasaddress", &clua_getaliasaddress_vector }, { "clua_error", &clua_error_vector }, { NULL, NULL } /* END OF LIST */ }; #endif /* TRUCLUSTER */ UInt32 SocketUtils::sNumIPAddrs = 0; SocketUtils::IPAddrInfo* SocketUtils::sIPAddrInfoArray = NULL; OSMutex SocketUtils::sMutex; #if __FreeBSD__ //Complete rewrite for FreeBSD. //The non-FreeBSD version really needs to be rewritten - it's a bit of a mess... void SocketUtils::Initialize(Bool16 lookupDNSName) { struct ifaddrs* ifap; struct ifaddrs* currentifap; struct sockaddr_in* sockaddr; int result = 0; result = getifaddrs(&ifap); //Count them first currentifap = ifap; while( currentifap != NULL ) { sockaddr = (struct sockaddr_in*)currentifap->ifa_addr; if (sockaddr->sin_family == AF_INET) sNumIPAddrs++; currentifap = currentifap->ifa_next; } //allocate the IPAddrInfo array. Unfortunately we can't allocate this //array the proper way due to a GCC bug UInt8* addrInfoMem = new UInt8[sizeof(IPAddrInfo) * sNumIPAddrs]; ::memset(addrInfoMem, 0, sizeof(IPAddrInfo) * sNumIPAddrs); sIPAddrInfoArray = (IPAddrInfo*)addrInfoMem; int addrArrayIndex = 0; currentifap = ifap; while( currentifap != NULL ) { sockaddr = (struct sockaddr_in*)currentifap->ifa_addr; if (sockaddr->sin_family == AF_INET) { char* theAddrStr = ::inet_ntoa(sockaddr->sin_addr); //store the IP addr sIPAddrInfoArray[addrArrayIndex].fIPAddr = ntohl(sockaddr->sin_addr.s_addr); //store the IP addr as a string sIPAddrInfoArray[addrArrayIndex].fIPAddrStr.Len = ::strlen(theAddrStr); sIPAddrInfoArray[addrArrayIndex].fIPAddrStr.Ptr = new char[sIPAddrInfoArray[addrArrayIndex].fIPAddrStr.Len + 2]; ::strcpy(sIPAddrInfoArray[addrArrayIndex].fIPAddrStr.Ptr, theAddrStr); struct hostent* theDNSName = NULL; if (lookupDNSName) //convert this addr to a dns name, and store it { theDNSName = ::gethostbyaddr((char *)&sockaddr->sin_addr, sizeof(sockaddr->sin_addr), AF_INET); } if (theDNSName != NULL) { sIPAddrInfoArray[addrArrayIndex].fDNSNameStr.Len = ::strlen(theDNSName->h_name); sIPAddrInfoArray[addrArrayIndex].fDNSNameStr.Ptr = new char[sIPAddrInfoArray[addrArrayIndex].fDNSNameStr.Len + 2]; ::strcpy(sIPAddrInfoArray[addrArrayIndex].fDNSNameStr.Ptr, theDNSName->h_name); } else { //if we failed to look up the DNS name, just store the IP addr as a string sIPAddrInfoArray[addrArrayIndex].fDNSNameStr.Len = sIPAddrInfoArray[addrArrayIndex].fIPAddrStr.Len; sIPAddrInfoArray[addrArrayIndex].fDNSNameStr.Ptr = new char[sIPAddrInfoArray[addrArrayIndex].fDNSNameStr.Len + 2]; ::strcpy(sIPAddrInfoArray[addrArrayIndex].fDNSNameStr.Ptr, sIPAddrInfoArray[addrArrayIndex].fIPAddrStr.Ptr); } addrArrayIndex++; } currentifap = currentifap->ifa_next; } } #else //__FreeBSD__ //Version for all non-FreeBSD platforms. void SocketUtils::Initialize(Bool16 lookupDNSName) { #if defined(__Win32__) || defined(USE_SIOCGIFNUM) int tempSocket = ::socket(AF_INET, SOCK_DGRAM, 0); if (tempSocket == -1) return; #ifdef __Win32__ static const UInt32 kMaxAddrBufferSize = 2048; char inBuffer[kMaxAddrBufferSize]; char outBuffer[kMaxAddrBufferSize]; UInt32 theReturnedSize = 0; // // Use the WSAIoctl function call to get a list of IP addresses int theErr = ::WSAIoctl( tempSocket, SIO_GET_INTERFACE_LIST, inBuffer, kMaxAddrBufferSize, outBuffer, kMaxAddrBufferSize, &theReturnedSize, NULL, NULL); Assert(theErr == 0); if (theErr != 0) return; Assert((theReturnedSize % sizeof(INTERFACE_INFO)) == 0); LPINTERFACE_INFO addrListP = (LPINTERFACE_INFO)&outBuffer[0]; sNumIPAddrs = theReturnedSize / sizeof(INTERFACE_INFO); #else #if defined(USE_SIOCGIFNUM) if (::ioctl(tempSocket, SIOCGIFNUM, (char*)&sNumIPAddrs) == -1) { #ifdef MAXIFS sNumIPAddrs = MAXIFS; #else sNumIPAddrs = 64; #endif } #else #error #endif struct ifconf ifc; ::memset(&ifc,0,sizeof(ifc)); ifc.ifc_len = sNumIPAddrs * sizeof(struct ifreq); ifc.ifc_buf = (caddr_t)new struct ifreq[sNumIPAddrs]; Assert(ifc.ifc_buf != NULL); ::memset(ifc.ifc_buf, '\0', ifc.ifc_len); int theErr = ::ioctl(tempSocket, SIOCGIFCONF, (char*)&ifc); Assert(theErr == 0); if (theErr != 0) return; struct ifreq* ifr = (struct ifreq*)ifc.ifc_buf; #endif //allocate the IPAddrInfo array. Unfortunately we can't allocate this //array the proper way due to a GCC bug UInt8* addrInfoMem = new UInt8[sizeof(IPAddrInfo) * sNumIPAddrs]; ::memset(addrInfoMem, 0, sizeof(IPAddrInfo) * sNumIPAddrs); sIPAddrInfoArray = (IPAddrInfo*)addrInfoMem; //for (UInt32 addrCount = 0; addrCount < sNumIPAddrs; addrCount++) UInt32 currentIndex = 0; for (UInt32 theIfCount = sNumIPAddrs, addrCount = 0; addrCount < theIfCount; addrCount++) { #ifdef __Win32__ // We *should* count the loopback interface as a valid interface. //if (addrListP[addrCount].iiFlags & IFF_LOOPBACK) //{ // Don't count loopback addrs // sNumIPAddrs--; // continue; //} //if (addrListP[addrCount].iiFlags & IFF_LOOPBACK) // if (lookupDNSName) // The playlist broadcaster doesn't care // Assert(addrCount > 0); // If the loopback interface is interface 0, we've got problems struct sockaddr_in* theAddr = (struct sockaddr_in*)&addrListP[addrCount].iiAddress; #elif defined(USE_SIOCGIFNUM) if (ifr[addrCount].ifr_addr.sa_family != AF_INET) { sNumIPAddrs--; continue; } struct ifreq ifrf; ::memset(&ifrf,0,sizeof(ifrf)); ::strncpy(ifrf.ifr_name, ifr[addrCount].ifr_name, sizeof(ifrf.ifr_name)); theErr = ::ioctl(tempSocket, SIOCGIFFLAGS, (char *) &ifrf); Assert(theErr != -1); #ifndef __solaris__ /* Skip things which aren't interesting */ if ((ifrf.ifr_flags & IFF_UP) == 0 || (ifrf.ifr_flags & (IFF_BROADCAST | IFF_POINTOPOINT)) == 0) { sNumIPAddrs--; continue; } if (ifrf.ifr_flags & IFF_LOOPBACK) { Assert(addrCount > 0); // If the loopback interface is interface 0, we've got problems } #endif struct sockaddr_in* theAddr = (struct sockaddr_in*)&ifr[addrCount].ifr_addr; #if 0 puts(ifr[addrCount].ifr_name); #endif #else #error #endif char* theAddrStr = ::inet_ntoa(theAddr->sin_addr); //store the IP addr sIPAddrInfoArray[currentIndex].fIPAddr = ntohl(theAddr->sin_addr.s_addr); //store the IP addr as a string sIPAddrInfoArray[currentIndex].fIPAddrStr.Len = ::strlen(theAddrStr); sIPAddrInfoArray[currentIndex].fIPAddrStr.Ptr = new char[sIPAddrInfoArray[currentIndex].fIPAddrStr.Len + 2]; ::strcpy(sIPAddrInfoArray[currentIndex].fIPAddrStr.Ptr, theAddrStr); struct hostent* theDNSName = NULL; if (lookupDNSName) //convert this addr to a dns name, and store it { theDNSName = ::gethostbyaddr((char *)&theAddr->sin_addr, sizeof(theAddr->sin_addr), AF_INET); } if (theDNSName != NULL) { sIPAddrInfoArray[currentIndex].fDNSNameStr.Len = ::strlen(theDNSName->h_name); sIPAddrInfoArray[currentIndex].fDNSNameStr.Ptr = new char[sIPAddrInfoArray[currentIndex].fDNSNameStr.Len + 2]; ::strcpy(sIPAddrInfoArray[currentIndex].fDNSNameStr.Ptr, theDNSName->h_name); } else { //if we failed to look up the DNS name, just store the IP addr as a string sIPAddrInfoArray[currentIndex].fDNSNameStr.Len = sIPAddrInfoArray[currentIndex].fIPAddrStr.Len; sIPAddrInfoArray[currentIndex].fDNSNameStr.Ptr = new char[sIPAddrInfoArray[currentIndex].fDNSNameStr.Len + 2]; ::strcpy(sIPAddrInfoArray[currentIndex].fDNSNameStr.Ptr, sIPAddrInfoArray[currentIndex].fIPAddrStr.Ptr); } //move onto the next array index currentIndex++; } #ifdef __Win32__ ::closesocket(tempSocket); #elif defined(USE_SIOCGIFNUM) delete[] ifc.ifc_buf; ::close(tempSocket); #else #error #endif #else // !__Win32__ //Most of this code is similar to the SIOCGIFCONF code presented in Stevens, //Unix Network Programming, section 16.6 //Use the SIOCGIFCONF ioctl call to iterate through the network interfaces static const UInt32 kMaxAddrBufferSize = 2048; struct ifconf ifc; ::memset(&ifc,0,sizeof(ifc)); struct ifreq* ifr; char buffer[kMaxAddrBufferSize]; int tempSocket = ::socket(AF_INET, SOCK_DGRAM, 0); if (tempSocket == -1) return; ifc.ifc_len = kMaxAddrBufferSize; ifc.ifc_buf = buffer; #if __linux__ || __linuxppc__ || __solaris__ || __MacOSX__ || __sgi__ || __osf__ int err = ::ioctl(tempSocket, SIOCGIFCONF, (char*)&ifc); #elif __FreeBSD__ int err = ::ioctl(tempSocket, OSIOCGIFCONF, (char*)&ifc); #else #error #endif if (err == -1) return; #if __FreeBSD__ int netdev1, netdev2; struct ifreq *netdevifr; netdevifr = ifc.ifc_req; netdev1 = ifc.ifc_len / sizeof(struct ifreq); for (netdev2=netdev1-1; netdev2>=0; netdev2--) { if (ioctl(tempSocket, SIOCGIFADDR, &netdevifr[netdev2]) != 0) continue; } #endif ::close(tempSocket); tempSocket = -1; //walk through the list of IP addrs twice. Once to find out how many, //the second time to actually grab their information char* ifReqIter = NULL; sNumIPAddrs = 0; for (ifReqIter = buffer; ifReqIter < (buffer + ifc.ifc_len);) { ifr = (struct ifreq*)ifReqIter; if (!SocketUtils::IncrementIfReqIter(&ifReqIter, ifr)) return; // Some platforms have lo as the first interface, so we have code to // work around this problem below //if (::strncmp(ifr->ifr_name, "lo", 2) == 0) // Assert(sNumIPAddrs > 0); // If the loopback interface is interface 0, we've got problems //Only count interfaces in the AF_INET family. if (ifr->ifr_addr.sa_family == AF_INET) sNumIPAddrs++; } #ifdef TRUCLUSTER int clusterAliases = 0; if (clu_is_member()) { /* loading the vector table */ if (clua_getaliasaddress_vector == NULL) { clucall_stat clustat; struct sockaddr_in sin; clustat = clucall_load("libclua.so", clua_vectors); int context = 0; clua_status_t addr_err; if (clua_getaliasaddress_vector != NULL) while ( (addr_err = clua_getaliasaddress ((struct sockaddr*)&sin, &context)) == CLUA_SUCCESS ) { sNumIPAddrs++; clusterAliases++; } } } #endif // TRUCLUSTER //allocate the IPAddrInfo array. Unfortunately we can't allocate this //array the proper way due to a GCC bug UInt8* addrInfoMem = new UInt8[sizeof(IPAddrInfo) * sNumIPAddrs]; ::memset(addrInfoMem, 0, sizeof(IPAddrInfo) * sNumIPAddrs); sIPAddrInfoArray = (IPAddrInfo*)addrInfoMem; //Now extract all the necessary information about each interface //and put it into the array UInt32 currentIndex = 0; #ifdef TRUCLUSTER // Do these cluster aliases first so they'll be first in the list if (clusterAliases > 0) { int context = 0; struct sockaddr_in sin; clua_status_t addr_err; while ( (addr_err = clua_getaliasaddress ((struct sockaddr*)&sin, &context)) == CLUA_SUCCESS ) { char* theAddrStr = ::inet_ntoa(sin.sin_addr); //store the IP addr sIPAddrInfoArray[currentIndex].fIPAddr = ntohl(sin.sin_addr.s_addr); //store the IP addr as a string sIPAddrInfoArray[currentIndex].fIPAddrStr.Len = ::strlen(theAddrStr); sIPAddrInfoArray[currentIndex].fIPAddrStr.Ptr = new char[sIPAddrInfoArray[currentIndex].fIPAddrStr.Len + 2]; ::strcpy(sIPAddrInfoArray[currentIndex].fIPAddrStr.Ptr, theAddrStr); //convert this addr to a dns name, and store it struct hostent* theDNSName = ::gethostbyaddr((char *)&sin.sin_addr, sizeof(sin.sin_addr), AF_INET); if (theDNSName != NULL) { sIPAddrInfoArray[currentIndex].fDNSNameStr.Len = ::strlen(theDNSName->h_name); sIPAddrInfoArray[currentIndex].fDNSNameStr.Ptr = new char[sIPAddrInfoArray[currentIndex].fDNSNameStr.Len + 2]; ::strcpy(sIPAddrInfoArray[currentIndex].fDNSNameStr.Ptr, theDNSName->h_name); } else { //if we failed to look up the DNS name, just store the IP addr as a string sIPAddrInfoArray[currentIndex].fDNSNameStr.Len = sIPAddrInfoArray[currentIndex].fIPAddrStr.Len; sIPAddrInfoArray[currentIndex].fDNSNameStr.Ptr = new char[sIPAddrInfoArray[currentIndex].fDNSNameStr.Len + 2]; ::strcpy(sIPAddrInfoArray[currentIndex].fDNSNameStr.Ptr, sIPAddrInfoArray[currentIndex].fIPAddrStr.Ptr); } currentIndex++; } } #endif // TRUCLUSTER for (ifReqIter = buffer; ifReqIter < (buffer + ifc.ifc_len);) { ifr = (struct ifreq*)ifReqIter; if (!SocketUtils::IncrementIfReqIter(&ifReqIter, ifr)) { Assert(0);//we should have already detected this error return; } //Only count interfaces in the AF_INET family if (ifr->ifr_addr.sa_family == AF_INET) { struct sockaddr_in* addrPtr = (struct sockaddr_in*)&ifr->ifr_addr; char* theAddrStr = ::inet_ntoa(addrPtr->sin_addr); //store the IP addr sIPAddrInfoArray[currentIndex].fIPAddr = ntohl(addrPtr->sin_addr.s_addr); //store the IP addr as a string sIPAddrInfoArray[currentIndex].fIPAddrStr.Len = ::strlen(theAddrStr); sIPAddrInfoArray[currentIndex].fIPAddrStr.Ptr = new char[sIPAddrInfoArray[currentIndex].fIPAddrStr.Len + 2]; ::strcpy(sIPAddrInfoArray[currentIndex].fIPAddrStr.Ptr, theAddrStr); struct hostent* theDNSName = NULL; if (lookupDNSName) //convert this addr to a dns name, and store it { theDNSName = ::gethostbyaddr((char *)&addrPtr->sin_addr, sizeof(addrPtr->sin_addr), AF_INET); } if (theDNSName != NULL) { sIPAddrInfoArray[currentIndex].fDNSNameStr.Len = ::strlen(theDNSName->h_name); sIPAddrInfoArray[currentIndex].fDNSNameStr.Ptr = new char[sIPAddrInfoArray[currentIndex].fDNSNameStr.Len + 2]; ::strcpy(sIPAddrInfoArray[currentIndex].fDNSNameStr.Ptr, theDNSName->h_name); } else { //if we failed to look up the DNS name, just store the IP addr as a string sIPAddrInfoArray[currentIndex].fDNSNameStr.Len = sIPAddrInfoArray[currentIndex].fIPAddrStr.Len; sIPAddrInfoArray[currentIndex].fDNSNameStr.Ptr = new char[sIPAddrInfoArray[currentIndex].fDNSNameStr.Len + 2]; ::strcpy(sIPAddrInfoArray[currentIndex].fDNSNameStr.Ptr, sIPAddrInfoArray[currentIndex].fIPAddrStr.Ptr); } //move onto the next array index currentIndex++; } } Assert(currentIndex == sNumIPAddrs); #endif // // If LocalHost is the first element in the array, switch it to be the second. // The first element is supposed to be the "default" interface on the machine, // which should really always be en0. if ((sNumIPAddrs > 1) && (::strcmp(sIPAddrInfoArray[0].fIPAddrStr.Ptr, "127.0.0.1") == 0)) { UInt32 tempIP = sIPAddrInfoArray[1].fIPAddr; sIPAddrInfoArray[1].fIPAddr = sIPAddrInfoArray[0].fIPAddr; sIPAddrInfoArray[0].fIPAddr = tempIP; StrPtrLen tempIPStr(sIPAddrInfoArray[1].fIPAddrStr); sIPAddrInfoArray[1].fIPAddrStr = sIPAddrInfoArray[0].fIPAddrStr; sIPAddrInfoArray[0].fIPAddrStr = tempIPStr; StrPtrLen tempDNSStr(sIPAddrInfoArray[1].fDNSNameStr); sIPAddrInfoArray[1].fDNSNameStr = sIPAddrInfoArray[0].fDNSNameStr; sIPAddrInfoArray[0].fDNSNameStr = tempDNSStr; } } #endif //__FreeBSD__ #ifndef __Win32__ Bool16 SocketUtils::IncrementIfReqIter(char** inIfReqIter, ifreq* ifr) { //returns true if successful, false otherwise #if __MacOSX__ *inIfReqIter += sizeof(ifr->ifr_name) + ifr->ifr_addr.sa_len; //if the length of the addr is 0, use the family to determine //what the addr size is if (ifr->ifr_addr.sa_len == 0) #else *inIfReqIter += sizeof(ifr->ifr_name) + 0; #endif { switch (ifr->ifr_addr.sa_family) { case AF_INET: *inIfReqIter += sizeof(struct sockaddr_in); break; default: *inIfReqIter += sizeof(struct sockaddr); // Assert(0); // sNumIPAddrs = 0; // return false; } } return true; } #endif Bool16 SocketUtils::IsMulticastIPAddr(UInt32 inAddress) { return ((inAddress>>8) & 0x00f00000) == 0x00e00000; // multicast addresses == "class D" == 0xExxxxxxx == 1,1,1,0,<28 bits> } Bool16 SocketUtils::IsLocalIPAddr(UInt32 inAddress) { for (UInt32 x = 0; x < sNumIPAddrs; x++) if (sIPAddrInfoArray[x].fIPAddr == inAddress) return true; return false; } void SocketUtils::ConvertAddrToString(const struct in_addr& theAddr, StrPtrLen* ioStr) { //re-entrant version of code below //inet_ntop(AF_INET, &theAddr, ioStr->Ptr, ioStr->Len); //ioStr->Len = ::strlen(ioStr->Ptr); sMutex.Lock(); char* addr = inet_ntoa(theAddr); strcpy(ioStr->Ptr, addr); ioStr->Len = ::strlen(ioStr->Ptr); sMutex.Unlock(); } UInt32 SocketUtils::ConvertStringToAddr(const char* inAddrStr) { if (inAddrStr == NULL) return 0; return ntohl(::inet_addr(inAddrStr)); }