// Copyright (c) 2010 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "net/base/address_list.h" #include #include "base/logging.h" #include "net/base/net_util.h" #include "net/base/sys_addrinfo.h" namespace net { namespace { char* do_strdup(const char* src) { #if defined(OS_WIN) return _strdup(src); #else return strdup(src); #endif } // Make a copy of |info| (the dynamically-allocated parts are copied as well). // If |recursive| is true, chained entries via ai_next are copied too. // Copy returned by this function should be deleted using // DeleteCopyOfAddrinfo(), and NOT freeaddrinfo(). struct addrinfo* CreateCopyOfAddrinfo(const struct addrinfo* info, bool recursive) { DCHECK(info); struct addrinfo* copy = new addrinfo; // Copy all the fields (some of these are pointers, we will fix that next). memcpy(copy, info, sizeof(addrinfo)); // ai_canonname is a NULL-terminated string. if (info->ai_canonname) { copy->ai_canonname = do_strdup(info->ai_canonname); } // ai_addr is a buffer of length ai_addrlen. if (info->ai_addr) { copy->ai_addr = reinterpret_cast(new char[info->ai_addrlen]); memcpy(copy->ai_addr, info->ai_addr, info->ai_addrlen); } // Recursive copy. if (recursive && info->ai_next) copy->ai_next = CreateCopyOfAddrinfo(info->ai_next, recursive); else copy->ai_next = NULL; return copy; } // Free an addrinfo that was created by CreateCopyOfAddrinfo(). void FreeMyAddrinfo(struct addrinfo* info) { DCHECK(info); if (info->ai_canonname) free(info->ai_canonname); // Allocated by strdup. if (info->ai_addr) delete [] reinterpret_cast(info->ai_addr); struct addrinfo* next = info->ai_next; delete info; // Recursive free. if (next) FreeMyAddrinfo(next); } // Assign the port for all addresses in the list. void SetPortRecursive(struct addrinfo* info, int port) { uint16* port_field = GetPortFieldFromAddrinfo(info); if (port_field) *port_field = htons(port); // Assign recursively. if (info->ai_next) SetPortRecursive(info->ai_next, port); } } // namespace struct AddressList::Data : public base::RefCountedThreadSafe { Data(struct addrinfo* ai, bool is_system_created); struct addrinfo* head; // Indicates which free function to use for |head|. bool is_system_created; private: friend class base::RefCountedThreadSafe; ~Data(); }; AddressList::AddressList() { } AddressList::AddressList(const AddressList& addresslist) : data_(addresslist.data_) { } AddressList::~AddressList() { } AddressList& AddressList::operator=(const AddressList& addresslist) { data_ = addresslist.data_; return *this; } AddressList::AddressList(const IPAddressNumber& address, int port, bool canonicalize_name) { struct addrinfo* ai = new addrinfo; memset(ai, 0, sizeof(addrinfo)); ai->ai_socktype = SOCK_STREAM; switch (address.size()) { case 4: { ai->ai_family = AF_INET; const size_t sockaddr_in_size = sizeof(struct sockaddr_in); ai->ai_addrlen = sockaddr_in_size; struct sockaddr_in* addr = reinterpret_cast( new char[sockaddr_in_size]); memset(addr, 0, sockaddr_in_size); addr->sin_family = AF_INET; memcpy(&addr->sin_addr, &address[0], 4); ai->ai_addr = reinterpret_cast(addr); break; } case 16: { ai->ai_family = AF_INET6; const size_t sockaddr_in6_size = sizeof(struct sockaddr_in6); ai->ai_addrlen = sockaddr_in6_size; struct sockaddr_in6* addr6 = reinterpret_cast( new char[sockaddr_in6_size]); memset(addr6, 0, sockaddr_in6_size); addr6->sin6_family = AF_INET6; memcpy(&addr6->sin6_addr, &address[0], 16); ai->ai_addr = reinterpret_cast(addr6); break; } default: { NOTREACHED() << "Bad IP address"; break; } } if (canonicalize_name) { std::string name = NetAddressToString(ai); ai->ai_canonname = do_strdup(name.c_str()); } data_ = new Data(ai, false /*is_system_created*/); SetPort(port); } void AddressList::Adopt(struct addrinfo* head) { data_ = new Data(head, true /*is_system_created*/); } void AddressList::Copy(const struct addrinfo* head, bool recursive) { data_ = new Data(CreateCopyOfAddrinfo(head, recursive), false /*is_system_created*/); } void AddressList::Append(const struct addrinfo* head) { DCHECK(head); struct addrinfo* new_head; if (data_->is_system_created) { new_head = CreateCopyOfAddrinfo(data_->head, true); data_ = new Data(new_head, false /*is_system_created*/); } else { new_head = data_->head; } // Find the end of current linked list and append new data there. struct addrinfo* copy_ptr = new_head; while (copy_ptr->ai_next) copy_ptr = copy_ptr->ai_next; DCHECK(!head->ai_canonname); copy_ptr->ai_next = CreateCopyOfAddrinfo(head, true); } void AddressList::SetPort(int port) { SetPortRecursive(data_->head, port); } int AddressList::GetPort() const { return GetPortFromAddrinfo(data_->head); } bool AddressList::GetCanonicalName(std::string* canonical_name) const { DCHECK(canonical_name); if (!data_ || !data_->head->ai_canonname) return false; canonical_name->assign(data_->head->ai_canonname); return true; } void AddressList::SetFrom(const AddressList& src, int port) { if (src.GetPort() == port) { // We can reference the data from |src| directly. *this = src; } else { // Otherwise we need to make a copy in order to change the port number. Copy(src.head(), true); SetPort(port); } } void AddressList::Reset() { data_ = NULL; } const struct addrinfo* AddressList::head() const { return data_->head; } AddressList::AddressList(Data* data) : data_(data) {} AddressList::Data::Data(struct addrinfo* ai, bool is_system_created) : head(ai), is_system_created(is_system_created) { DCHECK(head); } AddressList::Data::~Data() { // Call either freeaddrinfo(head), or FreeMyAddrinfo(head), depending who // created the data. if (is_system_created) freeaddrinfo(head); else FreeMyAddrinfo(head); } } // namespace net