| /* |
| * libjingle |
| * Copyright 2004--2005, Google Inc. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions are met: |
| * |
| * 1. Redistributions of source code must retain the above copyright notice, |
| * this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright notice, |
| * this list of conditions and the following disclaimer in the documentation |
| * and/or other materials provided with the distribution. |
| * 3. The name of the author may not be used to endorse or promote products |
| * derived from this software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED |
| * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF |
| * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO |
| * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
| * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; |
| * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, |
| * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR |
| * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF |
| * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| #ifdef POSIX |
| #include <sys/types.h> |
| #include <sys/socket.h> |
| #include <netinet/in.h> |
| #include <netinet/ip.h> |
| #include <arpa/inet.h> |
| #include <netdb.h> |
| #include <unistd.h> |
| #endif |
| |
| #include <sstream> |
| |
| #include "bruno/byteorder.h" |
| #include "bruno/common.h" |
| #include "bruno/logging.h" |
| #include "bruno/nethelpers.h" |
| #include "bruno/socketaddress.h" |
| |
| #ifdef WIN32 |
| // Win32 doesn't provide inet_aton, so we add our own version here. |
| // Since inet_addr returns 0xFFFFFFFF on error, if we get this value |
| // we need to test the input to see if the address really was 255.255.255.255. |
| // This is slightly fragile, but better than doing nothing. |
| int inet_aton(const char* cp, struct in_addr* inp) { |
| inp->s_addr = inet_addr(cp); |
| return (inp->s_addr == INADDR_NONE && |
| strcmp(cp, "255.255.255.255") != 0) ? 0 : 1; |
| } |
| #endif // WIN32 |
| |
| namespace bruno_base { |
| |
| SocketAddress::SocketAddress() { |
| Clear(); |
| } |
| |
| SocketAddress::SocketAddress(const std::string& hostname, int port) { |
| SetIP(hostname); |
| SetPort(port); |
| } |
| |
| SocketAddress::SocketAddress(uint32 ip, int port) { |
| SetIP(ip); |
| SetPort(port); |
| } |
| |
| SocketAddress::SocketAddress(const SocketAddress& addr) { |
| this->operator=(addr); |
| } |
| |
| void SocketAddress::Clear() { |
| hostname_.clear(); |
| ip_ = 0; |
| port_ = 0; |
| } |
| |
| bool SocketAddress::IsNil() const { |
| return hostname_.empty() && (0 == ip_) && (0 == port_); |
| } |
| |
| bool SocketAddress::IsComplete() const { |
| return (0 != ip_) && (0 != port_); |
| } |
| |
| SocketAddress& SocketAddress::operator=(const SocketAddress& addr) { |
| hostname_ = addr.hostname_; |
| ip_ = addr.ip_; |
| port_ = addr.port_; |
| return *this; |
| } |
| |
| void SocketAddress::SetIP(uint32 ip) { |
| hostname_.clear(); |
| ip_ = ip; |
| } |
| |
| void SocketAddress::SetIP(const std::string& hostname) { |
| hostname_ = hostname; |
| ip_ = StringToIP(hostname); |
| } |
| |
| void SocketAddress::SetResolvedIP(uint32 ip) { |
| ip_ = ip; |
| } |
| |
| void SocketAddress::SetPort(int port) { |
| ASSERT((0 <= port) && (port < 65536)); |
| port_ = port; |
| } |
| |
| uint32 SocketAddress::ip() const { |
| return ip_; |
| } |
| |
| uint16 SocketAddress::port() const { |
| return port_; |
| } |
| |
| std::string SocketAddress::IPAsString() const { |
| if (!hostname_.empty()) |
| return hostname_; |
| return IPToString(ip_); |
| } |
| |
| std::string SocketAddress::PortAsString() const { |
| std::ostringstream ost; |
| ost << port_; |
| return ost.str(); |
| } |
| |
| std::string SocketAddress::ToString() const { |
| std::ostringstream ost; |
| ost << IPAsString(); |
| ost << ":"; |
| ost << port(); |
| return ost.str(); |
| } |
| |
| bool SocketAddress::FromString(const std::string& str) { |
| std::string::size_type pos = str.find(':'); |
| if (std::string::npos == pos) |
| return false; |
| SetPort(strtoul(str.substr(pos + 1).c_str(), NULL, 10)); |
| SetIP(str.substr(0, pos)); |
| return true; |
| } |
| |
| std::ostream& operator<<(std::ostream& os, const SocketAddress& addr) { |
| os << addr.IPAsString() << ":" << addr.port(); |
| return os; |
| } |
| |
| bool SocketAddress::IsAnyIP() const { |
| return (ip_ == 0); |
| } |
| |
| bool SocketAddress::IsLoopbackIP() const { |
| if (0 == ip_) { |
| return (0 == stricmp(hostname_.c_str(), "localhost")); |
| } else { |
| return ((ip_ >> 24) == 127); |
| } |
| } |
| |
| bool SocketAddress::IsLocalIP() const { |
| if (IsLoopbackIP()) |
| return true; |
| |
| std::vector<uint32> ips; |
| if (0 == ip_) { |
| if (!hostname_.empty() |
| && (0 == stricmp(hostname_.c_str(), GetHostname().c_str()))) { |
| return true; |
| } |
| } else if (GetLocalIPs(ips)) { |
| for (size_t i = 0; i < ips.size(); ++i) { |
| if (ips[i] == ip_) { |
| return true; |
| } |
| } |
| } |
| return false; |
| } |
| |
| bool SocketAddress::IsPrivateIP() const { |
| return ((ip_ >> 24) == 127) || |
| ((ip_ >> 24) == 10) || |
| ((ip_ >> 20) == ((172 << 4) | 1)) || |
| ((ip_ >> 16) == ((192 << 8) | 168)) || |
| ((ip_ >> 16) == ((169 << 8) | 254)); |
| } |
| |
| bool SocketAddress::IsUnresolvedIP() const { |
| return IsAny() && !hostname_.empty(); |
| } |
| |
| bool SocketAddress::ResolveIP(bool force, int* error) { |
| if (hostname_.empty()) { |
| // nothing to resolve |
| } else if (!force && !IsAny()) { |
| // already resolved |
| } else { |
| LOG_F(LS_VERBOSE) << "(" << hostname_ << ")"; |
| int errcode = 0; |
| if (hostent* pHost = SafeGetHostByName(hostname_.c_str(), &errcode)) { |
| ip_ = NetworkToHost32(*reinterpret_cast<uint32*>(pHost->h_addr_list[0])); |
| LOG_F(LS_VERBOSE) << "(" << hostname_ << ") resolved to: " |
| << IPToString(ip_); |
| FreeHostEnt(pHost); |
| } else { |
| LOG_F(LS_ERROR) << "(" << hostname_ << ") err: " << errcode; |
| } |
| if (error) { |
| *error = errcode; |
| } |
| } |
| return (ip_ != 0); |
| } |
| |
| bool SocketAddress::operator==(const SocketAddress& addr) const { |
| return EqualIPs(addr) && EqualPorts(addr); |
| } |
| |
| bool SocketAddress::operator<(const SocketAddress& addr) const { |
| if (ip_ < addr.ip_) |
| return true; |
| else if (addr.ip_ < ip_) |
| return false; |
| |
| // We only check hostnames if both IPs are zero. This matches EqualIPs() |
| if (addr.ip_ == 0) { |
| if (hostname_ < addr.hostname_) |
| return true; |
| else if (addr.hostname_ < hostname_) |
| return false; |
| } |
| |
| return port_ < addr.port_; |
| } |
| |
| bool SocketAddress::EqualIPs(const SocketAddress& addr) const { |
| return (ip_ == addr.ip_) && ((ip_ != 0) || (hostname_ == addr.hostname_)); |
| } |
| |
| bool SocketAddress::EqualPorts(const SocketAddress& addr) const { |
| return (port_ == addr.port_); |
| } |
| |
| size_t SocketAddress::Hash() const { |
| size_t h = 0; |
| h ^= ip_; |
| h ^= port_ | (port_ << 16); |
| return h; |
| } |
| |
| size_t SocketAddress::Size_() const { |
| return sizeof(ip_) + sizeof(port_) + 2; |
| } |
| |
| bool SocketAddress::Write_(char* buf, int len) const { |
| if (len < static_cast<int>(Size_())) |
| return false; |
| buf[0] = 0; |
| buf[1] = AF_INET; |
| SetBE16(buf + 2, port_); |
| SetBE32(buf + 4, ip_); |
| return true; |
| } |
| |
| bool SocketAddress::Read_(const char* buf, int len) { |
| if (len < static_cast<int>(Size_()) || buf[1] != AF_INET) |
| return false; |
| port_ = GetBE16(buf + 2); |
| ip_ = GetBE32(buf + 4); |
| return true; |
| } |
| |
| void SocketAddress::ToSockAddr(sockaddr_in* saddr) const { |
| memset(saddr, 0, sizeof(*saddr)); |
| saddr->sin_family = AF_INET; |
| saddr->sin_port = HostToNetwork16(port_); |
| if (0 == ip_) { |
| saddr->sin_addr.s_addr = INADDR_ANY; |
| } else { |
| saddr->sin_addr.s_addr = HostToNetwork32(ip_); |
| } |
| } |
| |
| bool SocketAddress::FromSockAddr(const sockaddr_in& saddr) { |
| if (saddr.sin_family != AF_INET) |
| return false; |
| SetIP(NetworkToHost32(saddr.sin_addr.s_addr)); |
| SetPort(NetworkToHost16(saddr.sin_port)); |
| return true; |
| } |
| |
| std::string SocketAddress::IPToString(uint32 ip) { |
| std::ostringstream ost; |
| ost << ((ip >> 24) & 0xff); |
| ost << '.'; |
| ost << ((ip >> 16) & 0xff); |
| ost << '.'; |
| ost << ((ip >> 8) & 0xff); |
| ost << '.'; |
| ost << ((ip >> 0) & 0xff); |
| return ost.str(); |
| } |
| |
| bool SocketAddress::StringToIP(const std::string& hostname, uint32* ip) { |
| in_addr addr; |
| if (inet_aton(hostname.c_str(), &addr) == 0) |
| return false; |
| *ip = NetworkToHost32(addr.s_addr); |
| return true; |
| } |
| |
| uint32 SocketAddress::StringToIP(const std::string& hostname) { |
| uint32 ip = 0; |
| StringToIP(hostname, &ip); |
| return ip; |
| } |
| |
| std::string SocketAddress::GetHostname() { |
| char hostname[256]; |
| if (gethostname(hostname, ARRAY_SIZE(hostname)) == 0) |
| return hostname; |
| return ""; |
| } |
| |
| bool SocketAddress::GetLocalIPs(std::vector<uint32>& ips) { |
| ips.clear(); |
| |
| const std::string hostname = GetHostname(); |
| if (hostname.empty()) |
| return false; |
| |
| int errcode; |
| if (hostent* pHost = SafeGetHostByName(hostname.c_str(), &errcode)) { |
| for (size_t i = 0; pHost->h_addr_list[i]; ++i) { |
| uint32 ip = |
| NetworkToHost32(*reinterpret_cast<uint32 *>(pHost->h_addr_list[i])); |
| ips.push_back(ip); |
| } |
| FreeHostEnt(pHost); |
| return !ips.empty(); |
| } |
| LOG(LS_ERROR) << "gethostbyname err: " << errcode; |
| return false; |
| } |
| |
| } // namespace bruno_base |