/* * 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. */ #include "talk/base/socketaddress.h" #ifdef POSIX #include #include #include #if defined(OPENBSD) #include #endif #include #include #include #include #endif #include #include "talk/base/byteorder.h" #include "talk/base/common.h" #include "talk/base/logging.h" #include "talk/base/nethelpers.h" #ifdef WIN32 #include "talk/base/win32.h" #endif namespace talk_base { // Address family constants for STUN (see RFC 5389). static const int kStunFamilyIPv4 = 1; static const int kStunFamilyIPv6 = 2; SocketAddress::SocketAddress() { Clear(); } SocketAddress::SocketAddress(const std::string& hostname, int port) { SetIP(hostname); SetPort(port); } SocketAddress::SocketAddress(uint32 ip_as_host_order_integer, int port) { SetIP(IPAddress(ip_as_host_order_integer)); SetPort(port); } SocketAddress::SocketAddress(const IPAddress& ip, int port) { SetIP(ip); SetPort(port); } SocketAddress::SocketAddress(const SocketAddress& addr) { this->operator=(addr); } void SocketAddress::Clear() { hostname_.clear(); literal_ = false; ip_ = IPAddress(INADDR_ANY); port_ = 0; } bool SocketAddress::IsNil() const { return hostname_.empty() && IPIsAny(ip_) && 0 == port_; } bool SocketAddress::IsComplete() const { return (!IPIsAny(ip_)) && (0 != port_); } SocketAddress& SocketAddress::operator=(const SocketAddress& addr) { hostname_ = addr.hostname_; ip_ = addr.ip_; port_ = addr.port_; literal_ = addr.literal_; return *this; } void SocketAddress::SetIP(uint32 ip_as_host_order_integer) { hostname_.clear(); literal_ = false; ip_ = IPAddress(ip_as_host_order_integer); } void SocketAddress::SetIP(const IPAddress& ip) { hostname_.clear(); literal_ = false; ip_ = ip; } void SocketAddress::SetIP(const std::string& hostname) { hostname_ = hostname; literal_ = IPFromString(hostname, &ip_); if (!literal_) { ip_ = IPAddress(INADDR_ANY); } } void SocketAddress::SetResolvedIP(uint32 ip_as_host_order_integer) { ip_ = IPAddress(ip_as_host_order_integer); } void SocketAddress::SetResolvedIP(const IPAddress& ip) { ip_ = ip; } void SocketAddress::SetPort(int port) { ASSERT((0 <= port) && (port < 65536)); port_ = port; } uint32 SocketAddress::ip() const { return ip_.v4AddressAsHostOrderInteger(); } const IPAddress& SocketAddress::ipaddr() const { return ip_; } uint16 SocketAddress::port() const { return port_; } std::string SocketAddress::IPAsString() const { // If the hostname was a literal IP string, it may need to have square // brackets added (for SocketAddress::ToString()). if (!literal_ && !hostname_.empty()) return hostname_; if (ip_.family() == AF_INET6) { return "[" + ip_.ToString() + "]"; } else { return ip_.ToString(); } } 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) { if (str.at(0) == '[') { std::string::size_type closebracket = str.rfind(']'); if (closebracket != std::string::npos) { std::string::size_type colon = str.find(':', closebracket); if (colon != std::string::npos && colon > closebracket) { SetPort(strtoul(str.substr(colon + 1).c_str(), NULL, 10)); SetIP(str.substr(1, closebracket - 1)); } else { return false; } } } else { 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 IPIsAny(ip_); } bool SocketAddress::IsLoopbackIP() const { return IPIsLoopback(ip_) || (IPIsAny(ip_) && 0 == strcmp(hostname_.c_str(), "localhost")); } bool SocketAddress::IsLocalIP() const { if (IsLoopbackIP()) return true; std::vector ips; if (IPIsAny(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 IPIsPrivate(ip_); } bool SocketAddress::IsUnresolvedIP() const { return IsAny() && !literal_ && !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)) { if (IPFromHostEnt(pHost, &ip_)) { LOG_F(LS_VERBOSE) << "(" << hostname_ << ") resolved to: " << ip_.ToString(); } FreeHostEnt(pHost); } else { LOG_F(LS_ERROR) << "(" << hostname_ << ") err: " << errcode; } if (error) { *error = errcode; } } return (!IPIsAny(ip_)); } 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.IsAnyIP()) { 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_) && ((!IPIsAny(ip_)) || (hostname_ == addr.hostname_)); } bool SocketAddress::EqualPorts(const SocketAddress& addr) const { return (port_ == addr.port_); } size_t SocketAddress::Hash() const { size_t h = 0; h ^= HashIP(ip_); h ^= port_ | (port_ << 16); return h; } void SocketAddress::ToSockAddr(sockaddr_in* saddr) const { memset(saddr, 0, sizeof(*saddr)); if (ip_.family() != AF_INET) { saddr->sin_family = AF_UNSPEC; return; } saddr->sin_family = AF_INET; saddr->sin_port = HostToNetwork16(port_); if (IPIsAny(ip_)) { saddr->sin_addr.s_addr = INADDR_ANY; } else { saddr->sin_addr = ip_.ipv4_address(); } } 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)); literal_ = false; return true; } static size_t ToSockAddrStorageHelper(sockaddr_storage* addr, IPAddress ip, int port, int scope_id) { memset(addr, 0, sizeof(sockaddr_storage)); addr->ss_family = ip.family(); if (addr->ss_family == AF_INET6) { sockaddr_in6* saddr = reinterpret_cast(addr); saddr->sin6_addr = ip.ipv6_address(); saddr->sin6_port = HostToNetwork16(port); saddr->sin6_scope_id = scope_id; return sizeof(sockaddr_in6); } else if (addr->ss_family == AF_INET) { sockaddr_in* saddr = reinterpret_cast(addr); saddr->sin_addr = ip.ipv4_address(); saddr->sin_port = HostToNetwork16(port); return sizeof(sockaddr_in); } return 0; } size_t SocketAddress::ToDualStackSockAddrStorage(sockaddr_storage *addr) const { return ToSockAddrStorageHelper(addr, ip_.AsIPv6Address(), port_, scope_id_); } size_t SocketAddress::ToSockAddrStorage(sockaddr_storage* addr) const { return ToSockAddrStorageHelper(addr, ip_, port_, scope_id_); } std::string SocketAddress::IPToString(uint32 ip_as_host_order_integer) { std::ostringstream ost; ost << ((ip_as_host_order_integer >> 24) & 0xff); ost << '.'; ost << ((ip_as_host_order_integer >> 16) & 0xff); ost << '.'; ost << ((ip_as_host_order_integer >> 8) & 0xff); ost << '.'; ost << ((ip_as_host_order_integer >> 0) & 0xff); return ost.str(); } bool SocketAddress::StringToIP(const std::string& hostname, uint32* ip) { in_addr addr; if (talk_base::inet_pton(AF_INET, hostname.c_str(), &addr) == 0) return false; *ip = NetworkToHost32(addr.s_addr); return true; } bool SocketAddress::StringToIP(const std::string& hostname, IPAddress* ip) { in_addr addr4; if (talk_base::inet_pton(AF_INET, hostname.c_str(), &addr4) > 0) { if (ip) { *ip = IPAddress(addr4); } return true; } in6_addr addr6; if (talk_base::inet_pton(AF_INET6, hostname.c_str(), &addr6) > 0) { if (ip) { *ip = IPAddress(addr6); } return true; } return false; } 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* ips) { if (!ips) { return false; } ips->clear(); const std::string hostname = SocketAddress::GetHostname(); if (hostname.empty()) return false; int errcode; if (hostent* pHost = SafeGetHostByName(hostname.c_str(), &errcode)) { for (int i = 0; pHost->h_addr_list[i]; ++i) { IPAddress ip; if (IPFromHostEnt(pHost, i, &ip)) { ips->push_back(ip); } } FreeHostEnt(pHost); return !ips->empty(); } LOG(LS_ERROR) << "gethostbyname err: " << errcode; return false; } bool SocketAddressFromSockAddrStorage(const sockaddr_storage& addr, SocketAddress* out) { if (!out) { return false; } if (addr.ss_family == AF_INET) { const sockaddr_in* saddr = reinterpret_cast(&addr); *out = SocketAddress(IPAddress(saddr->sin_addr), NetworkToHost16(saddr->sin_port)); return true; } else if (addr.ss_family == AF_INET6) { const sockaddr_in6* saddr = reinterpret_cast(&addr); *out = SocketAddress(IPAddress(saddr->sin6_addr), NetworkToHost16(saddr->sin6_port)); out->SetScopeID(saddr->sin6_scope_id); return true; } return false; } } // namespace talk_base