blob: 5442687f90f2c45fb37a78b84d453564809d3ed3 [file] [log] [blame]
// Copyright (c) 2011 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 <stdlib.h>
#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
}
struct addrinfo* CreateAddrInfo(const IPAddressNumber& address,
bool canonicalize_name) {
struct addrinfo* ai = new addrinfo;
memset(ai, 0, sizeof(addrinfo));
ai->ai_socktype = SOCK_STREAM;
switch (address.size()) {
case kIPv4AddressSize: {
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<struct sockaddr_in*>(
new char[sockaddr_in_size]);
memset(addr, 0, sockaddr_in_size);
addr->sin_family = AF_INET;
#if defined(SIN6_LEN)
addr->sin_len = sockaddr_in_size;
#endif
memcpy(&addr->sin_addr, &address[0], kIPv4AddressSize);
ai->ai_addr = reinterpret_cast<struct sockaddr*>(addr);
break;
}
case kIPv6AddressSize: {
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<struct sockaddr_in6*>(
new char[sockaddr_in6_size]);
memset(addr6, 0, sockaddr_in6_size);
addr6->sin6_family = AF_INET6;
#if defined(SIN6_LEN)
addr6->sin6_len = sockaddr_in6_size;
#endif
memcpy(&addr6->sin6_addr, &address[0], kIPv6AddressSize);
ai->ai_addr = reinterpret_cast<struct sockaddr*>(addr6);
break;
}
default: {
NOTREACHED() << "Bad IP address";
break;
}
}
if (canonicalize_name) {
std::string name = NetAddressToString(ai);
ai->ai_canonname = do_strdup(name.c_str());
}
return ai;
}
} // namespace
struct AddressList::Data : public base::RefCountedThreadSafe<Data> {
Data(struct addrinfo* ai, bool is_system_created);
// This variable is const since it should NOT be mutated. Since
// Data is reference counted, this |head| could be shared by multiple
// instances of AddressList, hence we need to be careful not to mutate
// it from any one instance.
const struct addrinfo * const head;
// Indicates which free function to use for |head|.
bool is_system_created;
private:
friend class base::RefCountedThreadSafe<Data>;
~Data();
};
AddressList::AddressList() {
}
AddressList::AddressList(const AddressList& addresslist)
: data_(addresslist.data_) {
}
AddressList::~AddressList() {
}
AddressList& AddressList::operator=(const AddressList& addresslist) {
data_ = addresslist.data_;
return *this;
}
// static
AddressList AddressList::CreateFromIPAddressList(
const IPAddressList& addresses,
uint16 port) {
DCHECK(!addresses.empty());
struct addrinfo* head = NULL;
struct addrinfo* next = NULL;
for (IPAddressList::const_iterator it = addresses.begin();
it != addresses.end(); ++it) {
if (head == NULL) {
head = next = CreateAddrInfo(*it, false);
} else {
next->ai_next = CreateAddrInfo(*it, false);
next = next->ai_next;
}
}
SetPortForAllAddrinfos(head, port);
return AddressList(new Data(head, false));
}
// static
AddressList AddressList::CreateFromIPAddress(
const IPAddressNumber& address,
uint16 port) {
return CreateFromIPAddressWithCname(address, port, false);
}
// static
AddressList AddressList::CreateFromIPAddressWithCname(
const IPAddressNumber& address,
uint16 port,
bool canonicalize_name) {
struct addrinfo* ai = CreateAddrInfo(address, canonicalize_name);
SetPortForAllAddrinfos(ai, port);
return AddressList(new Data(ai, false /*is_system_created*/));
}
// static
AddressList AddressList::CreateByAdoptingFromSystem(struct addrinfo* head) {
return AddressList(new Data(head, true /*is_system_created*/));
}
// static
AddressList AddressList::CreateByCopying(const struct addrinfo* head) {
return AddressList(new Data(CreateCopyOfAddrinfo(head, true /*recursive*/),
false /*is_system_created*/));
}
// static
AddressList AddressList::CreateByCopyingFirstAddress(
const struct addrinfo* head) {
return AddressList(new Data(CreateCopyOfAddrinfo(head, false /*recursive*/),
false /*is_system_created*/));
}
// static
AddressList AddressList::CreateFromSockaddr(
const struct sockaddr* address,
socklen_t address_length,
int socket_type,
int protocol) {
// Do sanity checking on socket_type and protocol.
DCHECK(socket_type == SOCK_DGRAM || socket_type == SOCK_STREAM);
DCHECK(protocol == IPPROTO_TCP || protocol == IPPROTO_UDP);
struct addrinfo* ai = new addrinfo;
memset(ai, 0, sizeof(addrinfo));
switch (address_length) {
case sizeof(struct sockaddr_in):
{
const struct sockaddr_in* sin =
reinterpret_cast<const struct sockaddr_in*>(address);
ai->ai_family = sin->sin_family;
DCHECK_EQ(AF_INET, ai->ai_family);
}
break;
case sizeof(struct sockaddr_in6):
{
const struct sockaddr_in6* sin6 =
reinterpret_cast<const struct sockaddr_in6*>(address);
ai->ai_family = sin6->sin6_family;
DCHECK_EQ(AF_INET6, ai->ai_family);
}
break;
default:
NOTREACHED() << "Bad IP address";
break;
}
ai->ai_socktype = socket_type;
ai->ai_protocol = protocol;
ai->ai_addrlen = address_length;
ai->ai_addr = reinterpret_cast<struct sockaddr*>(new char[address_length]);
memcpy(ai->ai_addr, address, address_length);
return AddressList(new Data(ai, false /*is_system_created*/));
}
void AddressList::Append(const struct addrinfo* head) {
DCHECK(head);
// Always create a copy, since the Data might be shared across instances.
struct addrinfo* new_head = CreateCopyOfAddrinfo(data_->head, true);
data_ = new Data(new_head, false /*is_system_created*/);
// 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;
copy_ptr->ai_next = CreateCopyOfAddrinfo(head, true);
// Only the head of the list should have a canonname. Strip any
// canonical name in the appended data.
copy_ptr = copy_ptr->ai_next;
while (copy_ptr) {
if (copy_ptr->ai_canonname) {
free(copy_ptr->ai_canonname);
copy_ptr->ai_canonname = NULL;
}
copy_ptr = copy_ptr->ai_next;
}
}
void AddressList::SetPort(uint16 port) {
// NOTE: we need to be careful not to mutate the reference-counted data,
// since it might be shared by other AddressLists.
struct addrinfo* head = CreateCopyOfAddrinfo(data_->head, true);
SetPortForAllAddrinfos(head, port);
data_ = new Data(head, false /*is_system_created*/);
}
uint16 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;
}
const struct addrinfo* AddressList::head() const {
if (!data_)
return NULL;
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() {
// Casting away the const is safe, since upon destruction we know that
// no one holds a reference to the data any more.
struct addrinfo* mutable_head = const_cast<struct addrinfo*>(head);
// Call either freeaddrinfo(head), or FreeMyAddrinfo(head), depending who
// created the data.
if (is_system_created)
freeaddrinfo(mutable_head);
else
FreeCopyOfAddrinfo(mutable_head);
}
AddressList CreateAddressListUsingPort(const AddressList& src, int port) {
if (src.GetPort() == port)
return src;
AddressList out = src;
out.SetPort(port);
return out;
}
} // namespace net