text:
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.

#define CARES_STATICLIB
#include "ares.h"
#include "async_wrap-inl.h"
#include "env-inl.h"
#include "memory_tracker-inl.h"
#include "node.h"
#include "req_wrap-inl.h"
#include "util-inl.h"
#include "uv.h"

#include <cerrno>
#include <cstring>
#include <memory>
#include <vector>
#include <unordered_set>

#ifdef __POSIX__
# include <netdb.h>
#endif  // __POSIX__

#if defined(__ANDROID__) || \
    defined(__MINGW32__) || \
    defined(__OpenBSD__) || \
    defined(_MSC_VER)

# include <nameser.h>
#else
# include <arpa/nameser.h>
#endif

#if defined(__OpenBSD__)
# define AI_V4MAPPED 0
#endif

namespace node {
namespace cares_wrap {

using v8::Array;
using v8::Context;
using v8::EscapableHandleScope;
using v8::FunctionCallbackInfo;
using v8::FunctionTemplate;
using v8::HandleScope;
using v8::Int32;
using v8::Integer;
using v8::Isolate;
using v8::Local;
using v8::NewStringType;
using v8::Null;
using v8::Object;
using v8::String;
using v8::Value;

namespace {

Mutex ares_library_mutex;

inline uint16_t cares_get_16bit(const unsigned char* p) {
  return static_cast<uint32_t>(p[0] << 8U) | (static_cast<uint32_t>(p[1]));
}

inline uint32_t cares_get_32bit(const unsigned char* p) {
  return static_cast<uint32_t>(p[0] << 24U) |
         static_cast<uint32_t>(p[1] << 16U) |
         static_cast<uint32_t>(p[2] << 8U) |
         static_cast<uint32_t>(p[3]);
}

const int ns_t_cname_or_a = -1;

#define DNS_ESETSRVPENDING -1000
inline const char* ToErrorCodeString(int status) {
  switch (status) {
#define V(code) case ARES_##code: return #code;
    V(EADDRGETNETWORKPARAMS)
    V(EBADFAMILY)
    V(EBADFLAGS)
    V(EBADHINTS)
    V(EBADNAME)
    V(EBADQUERY)
    V(EBADRESP)
    V(EBADSTR)
    V(ECANCELLED)
    V(ECONNREFUSED)
    V(EDESTRUCTION)
    V(EFILE)
    V(EFORMERR)
    V(ELOADIPHLPAPI)
    V(ENODATA)
    V(ENOMEM)
    V(ENONAME)
    V(ENOTFOUND)
    V(ENOTIMP)
    V(ENOTINITIALIZED)
    V(EOF)
    V(EREFUSED)
    V(ESERVFAIL)
    V(ETIMEOUT)
#undef V
  }

  return "UNKNOWN_ARES_ERROR";
}

class ChannelWrap;

struct node_ares_task : public MemoryRetainer {
  ChannelWrap* channel;
  ares_socket_t sock;
  uv_poll_t poll_watcher;

  inline void MemoryInfo(MemoryTracker* tracker) const override;
  SET_MEMORY_INFO_NAME(node_ares_task)
  SET_SELF_SIZE(node_ares_task)
};

struct TaskHash {
  size_t operator()(node_ares_task* a) const {
    return std::hash<ares_socket_t>()(a->sock);
  }
};

struct TaskEqual {
  inline bool operator()(node_ares_task* a, node_ares_task* b) const {
    return a->sock == b->sock;
  }
};

using node_ares_task_list =
    std::unordered_set<node_ares_task*, TaskHash, TaskEqual>;

class ChannelWrap : public AsyncWrap {
 public:
  ChannelWrap(Environment* env, Local<Object> object, int timeout);
  ~ChannelWrap() override;

  static void New(const FunctionCallbackInfo<Value>& args);

  void Setup();
  void EnsureServers();
  void StartTimer();
  void CloseTimer();

  void ModifyActivityQueryCount(int count);

  inline uv_timer_t* timer_handle() { return timer_handle_; }
  inline ares_channel cares_channel() { return channel_; }
  inline void set_query_last_ok(bool ok) { query_last_ok_ = ok; }
  inline void set_is_servers_default(bool is_default) {
    is_servers_default_ = is_default;
  }
  inline int active_query_count() { return active_query_count_; }
  inline node_ares_task_list* task_list() { return &task_list_; }

  void MemoryInfo(MemoryTracker* tracker) const override {
    if (timer_handle_ != nullptr)
      tracker->TrackField("timer_handle", *timer_handle_);
    tracker->TrackField("task_list", task_list_, "node_ares_task_list");
  }

  SET_MEMORY_INFO_NAME(ChannelWrap)
  SET_SELF_SIZE(ChannelWrap)

  static void AresTimeout(uv_timer_t* handle);

 private:
  uv_timer_t* timer_handle_;
  ares_channel channel_;
  bool query_last_ok_;
  bool is_servers_default_;
  bool library_inited_;
  int timeout_;
  int active_query_count_;
  node_ares_task_list task_list_;
};

ChannelWrap::ChannelWrap(Environment* env,
                         Local<Object> object,
                         int timeout)
  : AsyncWrap(env, object, PROVIDER_DNSCHANNEL),
    timer_handle_(nullptr),
    channel_(nullptr),
    query_last_ok_(true),
    is_servers_default_(true),
    library_inited_(false),
    timeout_(timeout),
    active_query_count_(0) {
  MakeWeak();

  Setup();
}

void ChannelWrap::New(const FunctionCallbackInfo<Value>& args) {
  CHECK(args.IsConstructCall());
  CHECK_EQ(args.Length(), 1);
  CHECK(args[0]->IsInt32());
  const int timeout = args[0].As<Int32>()->Value();
  Environment* env = Environment::GetCurrent(args);
  new ChannelWrap(env, args.This(), timeout);
}

class GetAddrInfoReqWrap : public ReqWrap<uv_getaddrinfo_t> {
 public:
  GetAddrInfoReqWrap(Environment* env,
                     Local<Object> req_wrap_obj,
                     bool verbatim);

  SET_NO_MEMORY_INFO()
  SET_MEMORY_INFO_NAME(GetAddrInfoReqWrap)
  SET_SELF_SIZE(GetAddrInfoReqWrap)

  bool verbatim() const { return verbatim_; }

 private:
  const bool verbatim_;
};

GetAddrInfoReqWrap::GetAddrInfoReqWrap(Environment* env,
                                       Local<Object> req_wrap_obj,
                                       bool verbatim)
    : ReqWrap(env, req_wrap_obj, AsyncWrap::PROVIDER_GETADDRINFOREQWRAP)
    , verbatim_(verbatim) {
}

class GetNameInfoReqWrap : public ReqWrap<uv_getnameinfo_t> {
 public:
  GetNameInfoReqWrap(Environment* env, Local<Object> req_wrap_obj);

  SET_NO_MEMORY_INFO()
  SET_MEMORY_INFO_NAME(GetNameInfoReqWrap)
  SET_SELF_SIZE(GetNameInfoReqWrap)
};

GetNameInfoReqWrap::GetNameInfoReqWrap(Environment* env,
                                       Local<Object> req_wrap_obj)
    : ReqWrap(env, req_wrap_obj, AsyncWrap::PROVIDER_GETNAMEINFOREQWRAP) {
}

/* This is called once per second by loop->timer. It is used to constantly */
/* call back into c-ares for possibly processing timeouts. */
void ChannelWrap::AresTimeout(uv_timer_t* handle) {
  ChannelWrap* channel = static_cast<ChannelWrap*>(handle->data);
  CHECK_EQ(channel->timer_handle(), handle);
  CHECK_EQ(false, channel->task_list()->empty());
  ares_process_fd(channel->cares_channel(), ARES_SOCKET_BAD, ARES_SOCKET_BAD);
}

void ares_poll_cb(uv_poll_t* watcher, int status, int events) {
  node_ares_task* task = ContainerOf(&node_ares_task::poll_watcher, watcher);
  ChannelWrap* channel = task->channel;

  /* Reset the idle timer */
  uv_timer_again(channel->timer_handle());

  if (status < 0) {
    /* An error happened. Just pretend that the socket is both readable and */
    /* writable. */
    ares_process_fd(channel->cares_channel(), task->sock, task->sock);
    return;
  }

  /* Process DNS responses */
  ares_process_fd(channel->cares_channel(),
                  events & UV_READABLE ? task->sock : ARES_SOCKET_BAD,
                  events & UV_WRITABLE ? task->sock : ARES_SOCKET_BAD);
}

void ares_poll_close_cb(uv_poll_t* watcher) {
  node_ares_task* task = ContainerOf(&node_ares_task::poll_watcher, watcher);
  delete task;
}

void node_ares_task::MemoryInfo(MemoryTracker* tracker) const {
  tracker->TrackField("channel", channel);
}

/* Allocates and returns a new node_ares_task */
node_ares_task* ares_task_create(ChannelWrap* channel, ares_socket_t sock) {
  auto task = new node_ares_task();

  task->channel = channel;
  task->sock = sock;

  if (uv_poll_init_socket(channel->env()->event_loop(),
                          &task->poll_watcher, sock) < 0) {
    /* This should never happen. */
    delete task;
    return nullptr;
  }

  return task;
}

/* Callback from ares when socket operation is started */
void ares_sockstate_cb(void* data,
                       ares_socket_t sock,
                       int read,
                       int write) {
  ChannelWrap* channel = static_cast<ChannelWrap*>(data);
  node_ares_task* task;

  node_ares_task lookup_task;
  lookup_task.sock = sock;
  auto it = channel->task_list()->find(&lookup_task);

  task = (it == channel->task_list()->end()) ? nullptr : *it;

  if (read || write) {
    if (!task) {
      /* New socket */
      channel->StartTimer();

      task = ares_task_create(channel, sock);
      if (task == nullptr) {
        /* This should never happen unless we're out of memory or something */
        /* is seriously wrong. The socket won't be polled, but the query will */
        /* eventually time out. */
        return;
      }

      channel->task_list()->insert(task);
    }

    /* This should never fail. If it fails anyway, the query will eventually */
    /* time out. */
    uv_poll_start(&task->poll_watcher,
                  (read ? UV_READABLE : 0) | (write ? UV_WRITABLE : 0),
                  ares_poll_cb);

  } else {
    /* read == 0 and write == 0 this is c-ares's way of notifying us that */
    /* the socket is now closed. We must free the data associated with */
    /* socket. */
    CHECK(task &&
          "When an ares socket is closed we should have a handle for it");

    channel->task_list()->erase(it);
    channel->env()->CloseHandle(&task->poll_watcher, ares_poll_close_cb);

    if (channel->task_list()->empty()) {
      channel->CloseTimer();
    }
  }
}

Local<Array> HostentToNames(Environment* env,
                            struct hostent* host,
                            Local<Array> append_to = Local<Array>()) {
  EscapableHandleScope scope(env->isolate());
  auto context = env->context();
  bool append = !append_to.IsEmpty();
  Local<Array> names = append ? append_to : Array::New(env->isolate());
  size_t offset = names->Length();

  for (uint32_t i = 0; host->h_aliases[i] != nullptr; ++i) {
    Local<String> address = OneByteString(env->isolate(), host->h_aliases[i]);
    names->Set(context, i + offset, address).Check();
  }

  return append ? names : scope.Escape(names);
}

void safe_free_hostent(struct hostent* host) {
  int idx;

  if (host->h_addr_list != nullptr) {
    idx = 0;
    while (host->h_addr_list[idx]) {
      free(host->h_addr_list[idx++]);
    }
    free(host->h_addr_list);
    host->h_addr_list = nullptr;
  }

  if (host->h_aliases != nullptr) {
    idx = 0;
    while (host->h_aliases[idx]) {
      free(host->h_aliases[idx++]);
    }
    free(host->h_aliases);
    host->h_aliases = nullptr;
  }

  free(host->h_name);
  free(host);
}

void cares_wrap_hostent_cpy(struct hostent* dest, const struct hostent* src) {
  dest->h_addr_list = nullptr;
  dest->h_addrtype = 0;
  dest->h_aliases = nullptr;
  dest->h_length = 0;
  dest->h_name = nullptr;

  /* copy `h_name` */
  size_t name_size = strlen(src->h_name) + 1;
  dest->h_name = node::Malloc<char>(name_size);
  memcpy(dest->h_name, src->h_name, name_size);

  /* copy `h_aliases` */
  size_t alias_count;
  for (alias_count = 0;
      src->h_aliases[alias_count] != nullptr;
      alias_count++) {
  }

  dest->h_aliases = node::Malloc<char*>(alias_count + 1);
  for (size_t i = 0; i < alias_count; i++) {
    const size_t cur_alias_size = strlen(src->h_aliases[i]) + 1;
    dest->h_aliases[i] = node::Malloc(cur_alias_size);
    memcpy(dest->h_aliases[i], src->h_aliases[i], cur_alias_size);
  }
  dest->h_aliases[alias_count] = nullptr;

  /* copy `h_addr_list` */
  size_t list_count;
  for (list_count = 0;
      src->h_addr_list[list_count] != nullptr;
      list_count++) {
  }

  dest->h_addr_list = node::Malloc<char*>(list_count + 1);
  for (size_t i = 0; i < list_count; i++) {
    dest->h_addr_list[i] = node::Malloc(src->h_length);
    memcpy(dest->h_addr_list[i], src->h_addr_list[i], src->h_length);
  }
  dest->h_addr_list[list_count] = nullptr;

  /* work after work */
  dest->h_length = src->h_length;
  dest->h_addrtype = src->h_addrtype;
}

class QueryWrap;

void ChannelWrap::Setup() {
  struct ares_options options;
  memset(&options, 0, sizeof(options));
  options.flags = ARES_FLAG_NOCHECKRESP;
  options.sock_state_cb = ares_sockstate_cb;
  options.sock_state_cb_data = this;
  options.timeout = timeout_;

  int r;
  if (!library_inited_) {
    Mutex::ScopedLock lock(ares_library_mutex);
    // Multiple calls to ares_library_init() increase a reference counter,
    // so this is a no-op except for the first call to it.
    r = ares_library_init(ARES_LIB_INIT_ALL);
    if (r != ARES_SUCCESS)
      return env()->ThrowError(ToErrorCodeString(r));
  }

  /* We do the call to ares_init_option for caller. */
  const int optmask =
      ARES_OPT_FLAGS | ARES_OPT_TIMEOUTMS | ARES_OPT_SOCK_STATE_CB;
  r = ares_init_options(&channel_, &options, optmask);

  if (r != ARES_SUCCESS) {
    Mutex::ScopedLock lock(ares_library_mutex);
    ares_library_cleanup();
    return env()->ThrowError(ToErrorCodeString(r));
  }

  library_inited_ = true;
}

void ChannelWrap::StartTimer() {
  if (timer_handle_ == nullptr) {
    timer_handle_ = new uv_timer_t();
    timer_handle_->data = static_cast<void*>(this);
    uv_timer_init(env()->event_loop(), timer_handle_);
  } else if (uv_is_active(reinterpret_cast<uv_handle_t*>(timer_handle_))) {
    return;
  }
  int timeout = timeout_;
  if (timeout == 0) timeout = 1;
  if (timeout < 0 || timeout > 1000) timeout = 1000;
  uv_timer_start(timer_handle_, AresTimeout, timeout, timeout);
}

void ChannelWrap::CloseTimer() {
  if (timer_handle_ == nullptr)
    return;

  env()->CloseHandle(timer_handle_, [](uv_timer_t* handle) { delete handle; });
  timer_handle_ = nullptr;
}

ChannelWrap::~ChannelWrap() {
  ares_destroy(channel_);

  if (library_inited_) {
    Mutex::ScopedLock lock(ares_library_mutex);
    // This decreases the reference counter increased by ares_library_init().
    ares_library_cleanup();
  }

  CloseTimer();
}

void ChannelWrap::ModifyActivityQueryCount(int count) {
  active_query_count_ += count;
  CHECK_GE(active_query_count_, 0);
}

/**
 * This function is to check whether current servers are fallback servers
 * when cares initialized.
 *
 * The fallback servers of cares is [ "127.0.0.1" ] with no user additional
 * setting.
 */
void ChannelWrap::EnsureServers() {
  /* if last query is OK or servers are set by user self, do not check */
  if (query_last_ok_ || !is_servers_default_) {
    return;
  }

  ares_addr_port_node* servers = nullptr;

  ares_get_servers_ports(channel_, &servers);

  /* if no server or multi-servers, ignore */
  if (servers == nullptr) return;
  if (servers->next != nullptr) {
    ares_free_data(servers);
    is_servers_default_ = false;
    return;
  }

  /* if the only server is not 127.0.0.1, ignore */
  if (servers[0].family != AF_INET ||
      servers[0].addr.addr4.s_addr != htonl(INADDR_LOOPBACK) ||
      servers[0].tcp_port != 0 ||
      servers[0].udp_port != 0) {
    ares_free_data(servers);
    is_servers_default_ = false;
    return;
  }

  ares_free_data(servers);
  servers = nullptr;

  /* destroy channel and reset channel */
  ares_destroy(channel_);

  CloseTimer();
  Setup();
}

class QueryWrap : public AsyncWrap {
 public:
  QueryWrap(ChannelWrap* channel, Local<Object> req_wrap_obj, const char* name)
      : AsyncWrap(channel->env(), req_wrap_obj, AsyncWrap::PROVIDER_QUERYWRAP),
        channel_(channel),
        trace_name_(name) {
  }

  ~QueryWrap() override {
    CHECK_EQ(false, persistent().IsEmpty());

    // Let Callback() know that this object no longer exists.
    if (callback_ptr_ != nullptr)
      *callback_ptr_ = nullptr;
  }

  // Subclasses should implement the appropriate Send method.
  virtual int Send(const char* name) {
    UNREACHABLE();
    return 0;
  }

  virtual int Send(const char* name, int family) {
    UNREACHABLE();
    return 0;
  }

 protected:
  void AresQuery(const char* name,
                 int dnsclass,
                 int type) {
    channel_->EnsureServers();
    TRACE_EVENT_NESTABLE_ASYNC_BEGIN1(
      TRACING_CATEGORY_NODE2(dns, native), trace_name_, this,
      "name", TRACE_STR_COPY(name));
    ares_query(channel_->cares_channel(), name, dnsclass, type, Callback,
               MakeCallbackPointer());
  }

  struct ResponseData {
    int status;
    bool is_host;
    DeleteFnPtr<hostent, safe_free_hostent> host;
    MallocedBuffer<unsigned char> buf;
  };

  void AfterResponse() {
    CHECK(response_data_);

    const int status = response_data_->status;

    if (status != ARES_SUCCESS) {
      ParseError(status);
    } else if (!response_data_->is_host) {
      Parse(response_data_->buf.data, response_data_->buf.size);
    } else {
      Parse(response_data_->host.get());
    }
  }

  void* MakeCallbackPointer() {
    CHECK_NULL(callback_ptr_);
    callback_ptr_ = new QueryWrap*(this);
    return callback_ptr_;
  }

  static QueryWrap* FromCallbackPointer(void* arg) {
    std::unique_ptr<QueryWrap*> wrap_ptr { static_cast<QueryWrap**>(arg) };
    QueryWrap* wrap = *wrap_ptr.get();
    if (wrap == nullptr) return nullptr;
    wrap->callback_ptr_ = nullptr;
    return wrap;
  }

  static void Callback(void* arg, int status, int timeouts,
                       unsigned char* answer_buf, int answer_len) {
    QueryWrap* wrap = FromCallbackPointer(arg);
    if (wrap == nullptr) return;

    unsigned char* buf_copy = nullptr;
    if (status == ARES_SUCCESS) {
      buf_copy = node::Malloc<unsigned char>(answer_len);
      memcpy(buf_copy, answer_buf, answer_len);
    }

    wrap->response_data_ = std::make_unique<ResponseData>();
    ResponseData* data = wrap->response_data_.get();
    data->status = status;
    data->is_host = false;
    data->buf = MallocedBuffer<unsigned char>(buf_copy, answer_len);

    wrap->QueueResponseCallback(status);
  }

  static void Callback(void* arg, int status, int timeouts,
                       struct hostent* host) {
    QueryWrap* wrap = FromCallbackPointer(arg);
    if (wrap == nullptr) return;

    struct hostent* host_copy = nullptr;
    if (status == ARES_SUCCESS) {
      host_copy = node::Malloc<hostent>(1);
      cares_wrap_hostent_cpy(host_copy, host);
    }

    wrap->response_data_ = std::make_unique<ResponseData>();
    ResponseData* data = wrap->response_data_.get();
    data->status = status;
    data->host.reset(host_copy);
    data->is_host = true;

    wrap->QueueResponseCallback(status);
  }

  void QueueResponseCallback(int status) {
    BaseObjectPtr<QueryWrap> strong_ref{this};
    env()->SetImmediate([this, strong_ref](Environment*) {
      AfterResponse();

      // Delete once strong_ref goes out of scope.
      Detach();
    });

    channel_->set_query_last_ok(status != ARES_ECONNREFUSED);
    channel_->ModifyActivityQueryCount(-1);
  }

  void CallOnComplete(Local<Value> answer,
                      Local<Value> extra = Local<Value>()) {
    HandleScope handle_scope(env()->isolate());
    Context::Scope context_scope(env()->context());
    Local<Value> argv[] = {
      Integer::New(env()->isolate(), 0),
      answer,
      extra
    };
    const int argc = arraysize(argv) - extra.IsEmpty();
    TRACE_EVENT_NESTABLE_ASYNC_END0(
        TRACING_CATEGORY_NODE2(dns, native), trace_name_, this);

    MakeCallback(env()->oncomplete_string(), argc, argv);
  }

  void ParseError(int status) {
    CHECK_NE(status, ARES_SUCCESS);
    HandleScope handle_scope(env()->isolate());
    Context::Scope context_scope(env()->context());
    const char* code = ToErrorCodeString(status);
    Local<Value> arg = OneByteString(env()->isolate(), code);
    TRACE_EVENT_NESTABLE_ASYNC_END1(
        TRACING_CATEGORY_NODE2(dns, native), trace_name_, this,
        "error", status);
    MakeCallback(env()->oncomplete_string(), 1, &arg);
  }

  // Subclasses should implement the appropriate Parse method.
  virtual void Parse(unsigned char* buf, int len) {
    UNREACHABLE();
  }

  virtual void Parse(struct hostent* host) {
    UNREACHABLE();
  }

  BaseObjectPtr<ChannelWrap> channel_;

 private:
  std::unique_ptr<ResponseData> response_data_;
  const char* trace_name_;
  // Pointer to pointer to 'this' that can be reset from the destructor,
  // in order to let Callback() know that 'this' no longer exists.
  QueryWrap** callback_ptr_ = nullptr;
};

template <typename T>
Local<Array> AddrTTLToArray(Environment* env,
                            const T* addrttls,
                            size_t naddrttls) {
  auto isolate = env->isolate();

  MaybeStackBuffer<Local<Value>, 8> ttls(naddrttls);
  for (size_t i = 0; i < naddrttls; i++)
    ttls[i] = Integer::NewFromUnsigned(isolate, addrttls[i].ttl);

  return Array::New(isolate, ttls.out(), naddrttls);
}

int ParseGeneralReply(Environment* env,
                      const unsigned char* buf,
                      int len,
                      int* type,
                      Local<Array> ret,
                      void* addrttls = nullptr,
                      int* naddrttls = nullptr) {
  HandleScope handle_scope(env->isolate());
  auto context = env->context();
  hostent* host;

  int status;
  switch (*type) {
    case ns_t_a:
    case ns_t_cname:
    case ns_t_cname_or_a:
      status = ares_parse_a_reply(buf,
                                  len,
                                  &host,
                                  static_cast<ares_addrttl*>(addrttls),
                                  naddrttls);
      break;
    case ns_t_aaaa:
      status = ares_parse_aaaa_reply(buf,
                                     len,
                                     &host,
                                     static_cast<ares_addr6ttl*>(addrttls),
                                     naddrttls);
      break;
    case ns_t_ns:
      status = ares_parse_ns_reply(buf, len, &host);
      break;
    case ns_t_ptr:
      status = ares_parse_ptr_reply(buf, len, nullptr, 0, AF_INET, &host);
      break;
    default:
      CHECK(0 && "Bad NS type");
      break;
  }

  if (status != ARES_SUCCESS)
    return status;

  /* If it's `CNAME`, return the CNAME value;
   * And if it's `CNAME_OR_A` and it has value in `h_name` and `h_aliases[0]`,
   * we consider it's a CNAME record, otherwise we consider it's an A record. */
  if ((*type == ns_t_cname_or_a && host->h_name && host->h_aliases[0]) ||
      *type == ns_t_cname) {
    // A cname lookup always returns a single record but we follow the
    // common API here.
    *type = ns_t_cname;
    ret->Set(context,
             ret->Length(),
             OneByteString(env->isolate(), host->h_name)).Check();
    ares_free_hostent(host);
    return ARES_SUCCESS;
  }

  if (*type == ns_t_cname_or_a)
    *type = ns_t_a;

  if (*type == ns_t_ns) {
    HostentToNames(env, host, ret);
  } else if (*type == ns_t_ptr) {
    uint32_t offset = ret->Length();
    for (uint32_t i = 0; host->h_aliases[i] != nullptr; i++) {
      auto alias = OneByteString(env->isolate(), host->h_aliases[i]);
      ret->Set(context, i + offset, alias).Check();
    }
  } else {
    uint32_t offset = ret->Length();
    char ip[INET6_ADDRSTRLEN];
    for (uint32_t i = 0; host->h_addr_list[i] != nullptr; ++i) {
      uv_inet_ntop(host->h_addrtype, host->h_addr_list[i], ip, sizeof(ip));
      auto address = OneByteString(env->isolate(), ip);
      ret->Set(context, i + offset, address).Check();
    }
  }

  ares_free_hostent(host);

  return ARES_SUCCESS;
}

int ParseMxReply(Environment* env,
                 const unsigned char* buf,
                 int len,
                 Local<Array> ret,
                 bool need_type = false) {
  HandleScope handle_scope(env->isolate());
  auto context = env->context();

  struct ares_mx_reply* mx_start;
  int status = ares_parse_mx_reply(buf, len, &mx_start);
  if (status != ARES_SUCCESS) {
    return status;
  }

  uint32_t offset = ret->Length();
  ares_mx_reply* current = mx_start;
  for (uint32_t i = 0; current != nullptr; ++i, current = current->next) {
    Local<Object> mx_record = Object::New(env->isolate());
    mx_record->Set(context,
                   env->exchange_string(),
                   OneByteString(env->isolate(), current->host)).Check();
    mx_record->Set(context,
                   env->priority_string(),
                   Integer::New(env->isolate(), current->priority)).Check();
    if (need_type)
      mx_record->Set(context,
                     env->type_string(),
                     env->dns_mx_string()).Check();

    ret->Set(context, i + offset, mx_record).Check();
  }

  ares_free_data(mx_start);
  return ARES_SUCCESS;
}

int ParseTxtReply(Environment* env,
                  const unsigned char* buf,
                  int len,
                  Local<Array> ret,
                  bool need_type = false) {
  HandleScope handle_scope(env->isolate());
  auto context = env->context();

  struct ares_txt_ext* txt_out;

  int status = ares_parse_txt_reply_ext(buf, len, &txt_out);
  if (status != ARES_SUCCESS) {
    return status;
  }

  Local<Array> txt_chunk;

  struct ares_txt_ext* current = txt_out;
  uint32_t i = 0, j;
  uint32_t offset = ret->Length();
  for (j = 0; current != nullptr; current = current->next) {
    Local<String> txt =
        OneByteString(env->isolate(), current->txt, current->length);

    // New record found - write out the current chunk
    if (current->record_start) {
      if (!txt_chunk.IsEmpty()) {
        if (need_type) {
          Local<Object> elem = Object::New(env->isolate());
          elem->Set(context, env->entries_string(), txt_chunk).Check();
          elem->Set(context,
                    env->type_string(),
                    env->dns_txt_string()).Check();
          ret->Set(context, offset + i++, elem).Check();
        } else {
          ret->Set(context, offset + i++, txt_chunk).Check();
        }
      }

      txt_chunk = Array::New(env->isolate());
      j = 0;
    }

    txt_chunk->Set(context, j++, txt).Check();
  }

  // Push last chunk if it isn't empty
  if (!txt_chunk.IsEmpty()) {
    if (need_type) {
      Local<Object> elem = Object::New(env->isolate());
      elem->Set(context, env->entries_string(), txt_chunk).Check();
      elem->Set(context,
                env->type_string(),
                env->dns_txt_string()).Check();
      ret->Set(context, offset + i, elem).Check();
    } else {
      ret->Set(context, offset + i, txt_chunk).Check();
    }
  }

  ares_free_data(txt_out);
  return ARES_SUCCESS;
}

int ParseSrvReply(Environment* env,
                  const unsigned char* buf,
                  int len,
                  Local<Array> ret,
                  bool need_type = false) {
  HandleScope handle_scope(env->isolate());
  auto context = env->context();

  struct ares_srv_reply* srv_start;
  int status = ares_parse_srv_reply(buf, len, &srv_start);
  if (status != ARES_SUCCESS) {
    return status;
  }

  ares_srv_reply* current = srv_start;
  int offset = ret->Length();
  for (uint32_t i = 0; current != nullptr; ++i, current = current->next) {
    Local<Object> srv_record = Object::New(env->isolate());
    srv_record->Set(context,
                    env->name_string(),
                    OneByteString(env->isolate(), current->host)).Check();
    srv_record->Set(context,
                    env->port_string(),
                    Integer::New(env->isolate(), current->port)).Check();
    srv_record->Set(context,
                    env->priority_string(),
                    Integer::New(env->isolate(), current->priority)).Check();
    srv_record->Set(context,
                    env->weight_string(),
                    Integer::New(env->isolate(), current->weight)).Check();
    if (need_type)
      srv_record->Set(context,
                      env->type_string(),
                      env->dns_srv_string()).Check();

    ret->Set(context, i + offset, srv_record).Check();
  }

  ares_free_data(srv_start);
  return ARES_SUCCESS;
}

int ParseNaptrReply(Environment* env,
                    const unsigned char* buf,
                    int len,
                    Local<Array> ret,
                    bool need_type = false) {
  HandleScope handle_scope(env->isolate());
  auto context = env->context();

  ares_naptr_reply* naptr_start;
  int status = ares_parse_naptr_reply(buf, len, &naptr_start);

  if (status != ARES_SUCCESS) {
    return status;
  }

  ares_naptr_reply* current = naptr_start;
  int offset = ret->Length();
  for (uint32_t i = 0; current != nullptr; ++i, current = current->next) {
    Local<Object> naptr_record = Object::New(env->isolate());
    naptr_record->Set(context,
                      env->flags_string(),
                      OneByteString(env->isolate(), current->flags)).Check();
    naptr_record->Set(context,
                      env->service_string(),
                      OneByteString(env->isolate(),
                                    current->service)).Check();
    naptr_record->Set(context,
                      env->regexp_string(),
                      OneByteString(env->isolate(),
                                    current->regexp)).Check();
    naptr_record->Set(context,
                      env->replacement_string(),
                      OneByteString(env->isolate(),
                                    current->replacement)).Check();
    naptr_record->Set(context,
                      env->order_string(),
                      Integer::New(env->isolate(), current->order)).Check();
    naptr_record->Set(context,
                      env->preference_string(),
                      Integer::New(env->isolate(),
                                   current->preference)).Check();
    if (need_type)
      naptr_record->Set(context,
                        env->type_string(),
                        env->dns_naptr_string()).Check();

    ret->Set(context, i + offset, naptr_record).Check();
  }

  ares_free_data(naptr_start);
  return ARES_SUCCESS;
}

int ParseSoaReply(Environment* env,
                  unsigned char* buf,
                  int len,
                  Local<Object>* ret) {
  EscapableHandleScope handle_scope(env->isolate());
  auto context = env->context();

  // Manage memory using standardard smart pointer std::unique_tr
  struct AresDeleter {
    void operator()(char* ptr) const noexcept { ares_free_string(ptr); }
  };
  using ares_unique_ptr = std::unique_ptr<char[], AresDeleter>;

  // Can't use ares_parse_soa_reply() here which can only parse single record
  const unsigned int ancount = cares_get_16bit(buf + 6);
  unsigned char* ptr = buf + NS_HFIXEDSZ;
  char* name_temp;
  long temp_len;  // NOLINT(runtime/int)
  int status = ares_expand_name(ptr, buf, len, &name_temp, &temp_len);
  const ares_unique_ptr name(name_temp);
  if (status != ARES_SUCCESS) {
    // returns EBADRESP in case of invalid input
    return status == ARES_EBADNAME ? ARES_EBADRESP : status;
  }

  if (ptr + temp_len + NS_QFIXEDSZ > buf + len) {
    return ARES_EBADRESP;
  }
  ptr += temp_len + NS_QFIXEDSZ;

  for (unsigned int i = 0; i < ancount; i++) {
    char* rr_name_temp;
    long rr_temp_len;  // NOLINT(runtime/int)
    int status2 = ares_expand_name(ptr, buf, len, &rr_name_temp, &rr_temp_len);
    const ares_unique_ptr rr_name(rr_name_temp);

    if (status2 != ARES_SUCCESS)
      return status2 == ARES_EBADNAME ? ARES_EBADRESP : status2;

    ptr += rr_temp_len;
    if (ptr + NS_RRFIXEDSZ > buf + len) {
      return ARES_EBADRESP;
    }

    const int rr_type = cares_get_16bit(ptr);
    const int rr_len = cares_get_16bit(ptr + 8);
    ptr += NS_RRFIXEDSZ;

    // only need SOA
    if (rr_type == ns_t_soa) {
      char* nsname_temp;
      long nsname_temp_len;  // NOLINT(runtime/int)

      int status3 = ares_expand_name(ptr, buf, len,
                                     &nsname_temp,
                                     &nsname_temp_len);
      const ares_unique_ptr nsname(nsname_temp);
      if (status3 != ARES_SUCCESS) {
        return status3 == ARES_EBADNAME ? ARES_EBADRESP : status3;
      }
      ptr += nsname_temp_len;

      char* hostmaster_temp;
      long hostmaster_temp_len;  // NOLINT(runtime/int)
      int status4 = ares_expand_name(ptr, buf, len,
                                     &hostmaster_temp,
                                     &hostmaster_temp_len);
      const ares_unique_ptr hostmaster(hostmaster_temp);
      if (status4 != ARES_SUCCESS) {
        return status4 == ARES_EBADNAME ? ARES_EBADRESP : status4;
      }
      ptr += hostmaster_temp_len;

      if (ptr + 5 * 4 > buf + len) {
        return ARES_EBADRESP;
      }

      const unsigned int serial = cares_get_32bit(ptr + 0 * 4);
      const unsigned int refresh = cares_get_32bit(ptr + 1 * 4);
      const unsigned int retry = cares_get_32bit(ptr + 2 * 4);
      const unsigned int expire = cares_get_32bit(ptr + 3 * 4);
      const unsigned int minttl = cares_get_32bit(ptr + 4 * 4);

      Local<Object> soa_record = Object::New(env->isolate());
      soa_record->Set(context,
                      env->nsname_string(),
                      OneByteString(env->isolate(), nsname.get())).Check();
      soa_record->Set(context,
                      env->hostmaster_string(),
                      OneByteString(env->isolate(),
                                    hostmaster.get())).Check();
      soa_record->Set(context,
                      env->serial_string(),
                      Integer::NewFromUnsigned(env->isolate(), serial)).Check();
      soa_record->Set(context,
                      env->refresh_string(),
                      Integer::New(env->isolate(), refresh)).Check();
      soa_record->Set(context,
                      env->retry_string(),
                      Integer::New(env->isolate(), retry)).Check();
      soa_record->Set(context,
                      env->expire_string(),
                      Integer::New(env->isolate(), expire)).Check();
      soa_record->Set(context,
                      env->minttl_string(),
                      Integer::NewFromUnsigned(env->isolate(), minttl)).Check();
      soa_record->Set(context,
                      env->type_string(),
                      env->dns_soa_string()).Check();

      *ret = handle_scope.Escape(soa_record);
      break;
    }

    ptr += rr_len;
  }

  return ARES_SUCCESS;
}

class QueryAnyWrap: public QueryWrap {
 public:
  QueryAnyWrap(ChannelWrap* channel, Local<Object> req_wrap_obj)
    : QueryWrap(channel, req_wrap_obj, "resolveAny") {
  }

  int Send(const char* name) override {
    AresQuery(name, ns_c_in, ns_t_any);
    return 0;
  }

  SET_NO_MEMORY_INFO()
  SET_MEMORY_INFO_NAME(QueryAnyWrap)
  SET_SELF_SIZE(QueryAnyWrap)

 protected:
  void Parse(unsigned char* buf, int len) override {
    HandleScope handle_scope(env()->isolate());
    auto context = env()->context();
    Context::Scope context_scope(context);

    Local<Array> ret = Array::New(env()->isolate());
    int type, status, old_count;

    /* Parse A records or CNAME records */
    ares_addrttl addrttls[256];
    int naddrttls = arraysize(addrttls);

    type = ns_t_cname_or_a;
    status = ParseGeneralReply(env(),
                               buf,
                               len,
                               &type,
                               ret,
                               addrttls,
                               &naddrttls);
    uint32_t a_count = ret->Length();
    if (status != ARES_SUCCESS && status != ARES_ENODATA) {
      ParseError(status);
      return;
    }

    if (type == ns_t_a) {
      CHECK_EQ(static_cast<uint32_t>(naddrttls), a_count);
      for (uint32_t i = 0; i < a_count; i++) {
        Local<Object> obj = Object::New(env()->isolate());
        obj->Set(context,
                 env()->address_string(),
                 ret->Get(context, i).ToLocalChecked()).Check();
        obj->Set(context,
                 env()->ttl_string(),
                 Integer::NewFromUnsigned(
                   env()->isolate(), addrttls[i].ttl)).Check();
        obj->Set(context,
                 env()->type_string(),
                 env()->dns_a_string()).Check();
        ret->Set(context, i, obj).Check();
      }
    } else {
      for (uint32_t i = 0; i < a_count; i++) {
        Local<Object> obj = Object::New(env()->isolate());
        obj->Set(context,
                 env()->value_string(),
                 ret->Get(context, i).ToLocalChecked()).Check();
        obj->Set(context,
                 env()->type_string(),
                 env()->dns_cname_string()).Check();
        ret->Set(context, i, obj).Check();
      }
    }

    /* Parse AAAA records */
    ares_addr6ttl addr6ttls[256];
    int naddr6ttls = arraysize(addr6ttls);

    type = ns_t_aaaa;
    status = ParseGeneralReply(env(),
                               buf,
                               len,
                               &type,
                               ret,
                               addr6ttls,
                               &naddr6ttls);
    uint32_t aaaa_count = ret->Length() - a_count;
    if (status != ARES_SUCCESS && status != ARES_ENODATA) {
      ParseError(status);
      return;
    }

    CHECK_EQ(aaaa_count, static_cast<uint32_t>(naddr6ttls));
    CHECK_EQ(ret->Length(), a_count + aaaa_count);
    for (uint32_t i = a_count; i < ret->Length(); i++) {
      Local<Object> obj = Object::New(env()->isolate());
      obj->Set(context,
               env()->address_string(),
               ret->Get(context, i).ToLocalChecked()).Check();
      obj->Set(context,
               env()->ttl_string(),
               Integer::NewFromUnsigned(
                 env()->isolate(), addr6ttls[i - a_count].ttl)).Check();
      obj->Set(context,
               env()->type_string(),
               env()->dns_aaaa_string()).Check();
      ret->Set(context, i, obj).Check();
    }

    /* Parse MX records */
    status = ParseMxReply(env(), buf, len, ret, true);
    if (status != ARES_SUCCESS && status != ARES_ENODATA) {
      ParseError(status);
      return;
    }

    /* Parse NS records */
    type = ns_t_ns;
    old_count = ret->Length();
    status = ParseGeneralReply(env(), buf, len, &type, ret);
    if (status != ARES_SUCCESS && status != ARES_ENODATA) {
      ParseError(status);
      return;
    }
    for (uint32_t i = old_count; i < ret->Length(); i++) {
      Local<Object> obj = Object::New(env()->isolate());
      obj->Set(context,
               env()->value_string(),
               ret->Get(context, i).ToLocalChecked()).Check();
      obj->Set(context,
               env()->type_string(),
               env()->dns_ns_string()).Check();
      ret->Set(context, i, obj).Check();
    }

    /* Parse TXT records */
    status = ParseTxtReply(env(), buf, len, ret, true);
    if (status != ARES_SUCCESS && status != ARES_ENODATA) {
      ParseError(status);
      return;
    }

    /* Parse SRV records */
    status = ParseSrvReply(env(), buf, len, ret, true);
    if (status != ARES_SUCCESS && status != ARES_ENODATA) {
      return;
    }

    /* Parse PTR records */
    type = ns_t_ptr;
    old_count = ret->Length();
    status = ParseGeneralReply(env(), buf, len, &type, ret);
    for (uint32_t i = old_count; i < ret->Length(); i++) {
      Local<Object> obj = Object::New(env()->isolate());
      obj->Set(context,
               env()->value_string(),
               ret->Get(context, i).ToLocalChecked()).Check();
      obj->Set(context,
               env()->type_string(),
               env()->dns_ptr_string()).Check();
      ret->Set(context, i, obj).Check();
    }

    /* Parse NAPTR records */
    status = ParseNaptrReply(env(), buf, len, ret, true);
    if (status != ARES_SUCCESS && status != ARES_ENODATA) {
      ParseError(status);
      return;
    }

    /* Parse SOA records */
    Local<Object> soa_record = Local<Object>();
    status = ParseSoaReply(env(), buf, len, &soa_record);
    if (status != ARES_SUCCESS && status != ARES_ENODATA) {
      ParseError(status);
      return;
    }
    if (!soa_record.IsEmpty())
      ret->Set(context, ret->Length(), soa_record).Check();

    CallOnComplete(ret);
  }
};

class QueryAWrap: public QueryWrap {
 public:
  QueryAWrap(ChannelWrap* channel, Local<Object> req_wrap_obj)
      : QueryWrap(channel, req_wrap_obj, "resolve4") {
  }

  int Send(const char* name) override {
    AresQuery(name, ns_c_in, ns_t_a);
    return 0;
  }

  SET_NO_MEMORY_INFO()
  SET_MEMORY_INFO_NAME(QueryAWrap)
  SET_SELF_SIZE(QueryAWrap)

 protected:
  void Parse(unsigned char* buf, int len) override {
    HandleScope handle_scope(env()->isolate());
    Context::Scope context_scope(env()->context());

    ares_addrttl addrttls[256];
    int naddrttls = arraysize(addrttls), status;
    Local<Array> ret = Array::New(env()->isolate());

    int type = ns_t_a;
    status = ParseGeneralReply(env(),
                               buf,
                               len,
                               &type,
                               ret,
                               addrttls,
                               &naddrttls);
    if (status != ARES_SUCCESS) {
      ParseError(status);
      return;
    }

    Local<Array> ttls = AddrTTLToArray<ares_addrttl>(env(),
                                                     addrttls,
                                                     naddrttls);

    CallOnComplete(ret, ttls);
  }
};

class QueryAaaaWrap: public QueryWrap {
 public:
  QueryAaaaWrap(ChannelWrap* channel, Local<Object> req_wrap_obj)
      : QueryWrap(channel, req_wrap_obj, "resolve6") {
  }

  int Send(const char* name) override {
    AresQuery(name, ns_c_in, ns_t_aaaa);
    return 0;
  }

  SET_NO_MEMORY_INFO()
  SET_MEMORY_INFO_NAME(QueryAaaaWrap)
  SET_SELF_SIZE(QueryAaaaWrap)

 protected:
  void Parse(unsigned char* buf, int len) override {
    HandleScope handle_scope(env()->isolate());
    Context::Scope context_scope(env()->context());

    ares_addr6ttl addrttls[256];
    int naddrttls = arraysize(addrttls), status;
    Local<Array> ret = Array::New(env()->isolate());

    int type = ns_t_aaaa;
    status = ParseGeneralReply(env(),
                               buf,
                               len,
                               &type,
                               ret,
                               addrttls,
                               &naddrttls);
    if (status != ARES_SUCCESS) {
      ParseError(status);
      return;
    }

    Local<Array> ttls = AddrTTLToArray<ares_addr6ttl>(env(),
                                                      addrttls,
                                                      naddrttls);

    CallOnComplete(ret, ttls);
  }
};

class QueryCnameWrap: public QueryWrap {
 public:
  QueryCnameWrap(ChannelWrap* channel, Local<Object> req_wrap_obj)
      : QueryWrap(channel, req_wrap_obj, "resolveCname") {
  }

  int Send(const char* name) override {
    AresQuery(name, ns_c_in, ns_t_cname);
    return 0;
  }

  SET_NO_MEMORY_INFO()
  SET_MEMORY_INFO_NAME(QueryCnameWrap)
  SET_SELF_SIZE(QueryCnameWrap)

 protected:
  void Parse(unsigned char* buf, int len) override {
    HandleScope handle_scope(env()->isolate());
    Context::Scope context_scope(env()->context());

    Local<Array> ret = Array::New(env()->isolate());
    int type = ns_t_cname;
    int status = ParseGeneralReply(env(), buf, len, &type, ret);
    if (status != ARES_SUCCESS) {
      ParseError(status);
      return;
    }

    this->CallOnComplete(ret);
  }
};

class QueryMxWrap: public QueryWrap {
 public:
  QueryMxWrap(ChannelWrap* channel, Local<Object> req_wrap_obj)
      : QueryWrap(channel, req_wrap_obj, "resolveMx") {
  }

  int Send(const char* name) override {
    AresQuery(name, ns_c_in, ns_t_mx);
    return 0;
  }

  SET_NO_MEMORY_INFO()
  SET_MEMORY_INFO_NAME(QueryMxWrap)
  SET_SELF_SIZE(QueryMxWrap)

 protected:
  void Parse(unsigned char* buf, int len) override {
    HandleScope handle_scope(env()->isolate());
    Context::Scope context_scope(env()->context());

    Local<Array> mx_records = Array::New(env()->isolate());
    int status = ParseMxReply(env(), buf, len, mx_records);

    if (status != ARES_SUCCESS) {
      ParseError(status);
      return;
    }

    this->CallOnComplete(mx_records);
  }
};

class QueryNsWrap: public QueryWrap {
 public:
  QueryNsWrap(ChannelWrap* channel, Local<Object> req_wrap_obj)
      : QueryWrap(channel, req_wrap_obj, "resolveNs") {
  }

  int Send(const char* name) override {
    AresQuery(name, ns_c_in, ns_t_ns);
    return 0;
  }

  SET_NO_MEMORY_INFO()
  SET_MEMORY_INFO_NAME(QueryNsWrap)
  SET_SELF_SIZE(QueryNsWrap)

 protected:
  void Parse(unsigned char* buf, int len) override {
    HandleScope handle_scope(env()->isolate());
    Context::Scope context_scope(env()->context());

    int type = ns_t_ns;
    Local<Array> names = Array::New(env()->isolate());
    int status = ParseGeneralReply(env(), buf, len, &type, names);
    if (status != ARES_SUCCESS) {
      ParseError(status);
      return;
    }

    this->CallOnComplete(names);
  }
};

class QueryTxtWrap: public QueryWrap {
 public:
  QueryTxtWrap(ChannelWrap* channel, Local<Object> req_wrap_obj)
      : QueryWrap(channel, req_wrap_obj, "resolveTxt") {
  }

  int Send(const char* name) override {
    AresQuery(name, ns_c_in, ns_t_txt);
    return 0;
  }

  SET_NO_MEMORY_INFO()
  SET_MEMORY_INFO_NAME(QueryTxtWrap)
  SET_SELF_SIZE(QueryTxtWrap)

 protected:
  void Parse(unsigned char* buf, int len) override {
    HandleScope handle_scope(env()->isolate());
    Context::Scope context_scope(env()->context());

    Local<Array> txt_records = Array::New(env()->isolate());
    int status = ParseTxtReply(env(), buf, len, txt_records);
    if (status != ARES_SUCCESS) {
      ParseError(status);
      return;
    }

    this->CallOnComplete(txt_records);
  }
};

class QuerySrvWrap: public QueryWrap {
 public:
  explicit QuerySrvWrap(ChannelWrap* channel, Local<Object> req_wrap_obj)
      : QueryWrap(channel, req_wrap_obj, "resolveSrv") {
  }

  int Send(const char* name) override {
    AresQuery(name, ns_c_in, ns_t_srv);
    return 0;
  }

  SET_NO_MEMORY_INFO()
  SET_MEMORY_INFO_NAME(QuerySrvWrap)
  SET_SELF_SIZE(QuerySrvWrap)

 protected:
  void Parse(unsigned char* buf, int len) override {
    HandleScope handle_scope(env()->isolate());
    Context::Scope context_scope(env()->context());

    Local<Array> srv_records = Array::New(env()->isolate());
    int status = ParseSrvReply(env(), buf, len, srv_records);
    if (status != ARES_SUCCESS) {
      ParseError(status);
      return;
    }

    this->CallOnComplete(srv_records);
  }
};

class QueryPtrWrap: public QueryWrap {
 public:
  explicit QueryPtrWrap(ChannelWrap* channel, Local<Object> req_wrap_obj)
      : QueryWrap(channel, req_wrap_obj, "resolvePtr") {
  }

  int Send(const char* name) override {
    AresQuery(name, ns_c_in, ns_t_ptr);
    return 0;
  }

  SET_NO_MEMORY_INFO()
  SET_MEMORY_INFO_NAME(QueryPtrWrap)
  SET_SELF_SIZE(QueryPtrWrap)

 protected:
  void Parse(unsigned char* buf, int len) override {
    HandleScope handle_scope(env()->isolate());
    Context::Scope context_scope(env()->context());

    int type = ns_t_ptr;
    Local<Array> aliases = Array::New(env()->isolate());

    int status = ParseGeneralReply(env(), buf, len, &type, aliases);
    if (status != ARES_SUCCESS) {
      ParseError(status);
      return;
    }

    this->CallOnComplete(aliases);
  }
};

class QueryNaptrWrap: public QueryWrap {
 public:
  explicit QueryNaptrWrap(ChannelWrap* channel, Local<Object> req_wrap_obj)
      : QueryWrap(channel, req_wrap_obj, "resolveNaptr") {
  }

  int Send(const char* name) override {
    AresQuery(name, ns_c_in, ns_t_naptr);
    return 0;
  }

  SET_NO_MEMORY_INFO()
  SET_MEMORY_INFO_NAME(QueryNaptrWrap)
  SET_SELF_SIZE(QueryNaptrWrap)

 protected:
  void Parse(unsigned char* buf, int len) override {
    HandleScope handle_scope(env()->isolate());
    Context::Scope context_scope(env()->context());

    Local<Array> naptr_records = Array::New(env()->isolate());
    int status = ParseNaptrReply(env(), buf, len, naptr_records);
    if (status != ARES_SUCCESS) {
      ParseError(status);
      return;
    }

    this->CallOnComplete(naptr_records);
  }
};

class QuerySoaWrap: public QueryWrap {
 public:
  QuerySoaWrap(ChannelWrap* channel, Local<Object> req_wrap_obj)
      : QueryWrap(channel, req_wrap_obj, "resolveSoa") {
  }

  int Send(const char* name) override {
    AresQuery(name, ns_c_in, ns_t_soa);
    return 0;
  }

  SET_NO_MEMORY_INFO()
  SET_MEMORY_INFO_NAME(QuerySoaWrap)
  SET_SELF_SIZE(QuerySoaWrap)

 protected:
  void Parse(unsigned char* buf, int len) override {
    HandleScope handle_scope(env()->isolate());
    auto context = env()->context();
    Context::Scope context_scope(context);

    ares_soa_reply* soa_out;
    int status = ares_parse_soa_reply(buf, len, &soa_out);

    if (status != ARES_SUCCESS) {
      ParseError(status);
      return;
    }

    Local<Object> soa_record = Object::New(env()->isolate());

    soa_record->Set(context,
                    env()->nsname_string(),
                    OneByteString(env()->isolate(),
                                  soa_out->nsname)).Check();
    soa_record->Set(context,
                    env()->hostmaster_string(),
                    OneByteString(env()->isolate(),
                                  soa_out->hostmaster)).Check();
    soa_record->Set(context,
                    env()->serial_string(),
                    Integer::NewFromUnsigned(
                      env()->isolate(), soa_out->serial)).Check();
    soa_record->Set(context,
                    env()->refresh_string(),
                    Integer::New(env()->isolate(),
                                 soa_out->refresh)).Check();
    soa_record->Set(context,
                    env()->retry_string(),
                    Integer::New(env()->isolate(), soa_out->retry)).Check();
    soa_record->Set(context,
                    env()->expire_string(),
                    Integer::New(env()->isolate(), soa_out->expire)).Check();
    soa_record->Set(context,
                    env()->minttl_string(),
                    Integer::NewFromUnsigned(
                      env()->isolate(), soa_out->minttl)).Check();

    ares_free_data(soa_out);

    this->CallOnComplete(soa_record);
  }
};

class GetHostByAddrWrap: public QueryWrap {
 public:
  explicit GetHostByAddrWrap(ChannelWrap* channel, Local<Object> req_wrap_obj)
      : QueryWrap(channel, req_wrap_obj, "reverse") {
  }

  int Send(const char* name) override {
    int length, family;
    char address_buffer[sizeof(struct in6_addr)];

    if (uv_inet_pton(AF_INET, name, &address_buffer) == 0) {
      length = sizeof(struct in_addr);
      family = AF_INET;
    } else if (uv_inet_pton(AF_INET6, name, &address_buffer) == 0) {
      length = sizeof(struct in6_addr);
      family = AF_INET6;
    } else {
      return UV_EINVAL;  // So errnoException() reports a proper error.
    }

    TRACE_EVENT_NESTABLE_ASYNC_BEGIN2(
        TRACING_CATEGORY_NODE2(dns, native), "reverse", this,
        "name", TRACE_STR_COPY(name),
        "family", family == AF_INET ? "ipv4" : "ipv6");

    ares_gethostbyaddr(channel_->cares_channel(),
                       address_buffer,
                       length,
                       family,
                       Callback,
                       MakeCallbackPointer());
    return 0;
  }

  SET_NO_MEMORY_INFO()
  SET_MEMORY_INFO_NAME(GetHostByAddrWrap)
  SET_SELF_SIZE(GetHostByAddrWrap)

 protected:
  void Parse(struct hostent* host) override {
    HandleScope handle_scope(env()->isolate());
    Context::Scope context_scope(env()->context());
    this->CallOnComplete(HostentToNames(env(), host));
  }
};

template <class Wrap>
static void Query(const FunctionCallbackInfo<Value>& args) {
  Environment* env = Environment::GetCurrent(args);
  ChannelWrap* channel;
  ASSIGN_OR_RETURN_UNWRAP(&channel, args.Holder());

  CHECK_EQ(false, args.IsConstructCall());
  CHECK(args[0]->IsObject());
  CHECK(args[1]->IsString());

  Local<Object> req_wrap_obj = args[0].As<Object>();
  Local<String> string = args[1].As<String>();
  auto wrap = std::make_unique<Wrap>(channel, req_wrap_obj);

  node::Utf8Value name(env->isolate(), string);
  channel->ModifyActivityQueryCount(1);
  int err = wrap->Send(*name);
  if (err) {
    channel->ModifyActivityQueryCount(-1);
  } else {
    // Release ownership of the pointer allowing the ownership to be transferred
    USE(wrap.release());
  }

  args.GetReturnValue().Set(err);
}

void AfterGetAddrInfo(uv_getaddrinfo_t* req, int status, struct addrinfo* res) {
  std::unique_ptr<GetAddrInfoReqWrap> req_wrap {
      static_cast<GetAddrInfoReqWrap*>(req->data)};
  Environment* env = req_wrap->env();

  HandleScope handle_scope(env->isolate());
  Context::Scope context_scope(env->context());

  Local<Value> argv[] = {
    Integer::New(env->isolate(), status),
    Null(env->isolate())
  };

  uint64_t n = 0;
  const bool verbatim = req_wrap->verbatim();

  if (status == 0) {
    Local<Array> results = Array::New(env->isolate());

    auto add = [&] (bool want_ipv4, bool want_ipv6) {
      for (auto p = res; p != nullptr; p = p->ai_next) {
        CHECK_EQ(p->ai_socktype, SOCK_STREAM);

        const char* addr;
        if (want_ipv4 && p->ai_family == AF_INET) {
          addr = reinterpret_cast<char*>(
              &(reinterpret_cast<struct sockaddr_in*>(p->ai_addr)->sin_addr));
        } else if (want_ipv6 && p->ai_family == AF_INET6) {
          addr = reinterpret_cast<char*>(
              &(reinterpret_cast<struct sockaddr_in6*>(p->ai_addr)->sin6_addr));
        } else {
          continue;
        }

        char ip[INET6_ADDRSTRLEN];
        if (uv_inet_ntop(p->ai_family, addr, ip, sizeof(ip)))
          continue;

        Local<String> s = OneByteString(env->isolate(), ip);
        results->Set(env->context(), n, s).Check();
        n++;
      }
    };

    add(true, verbatim);
    if (verbatim == false)
      add(false, true);

    // No responses were found to return
    if (n == 0) {
      argv[0] = Integer::New(env->isolate(), UV_EAI_NODATA);
    }

    argv[1] = results;
  }

  uv_freeaddrinfo(res);

  TRACE_EVENT_NESTABLE_ASYNC_END2(
      TRACING_CATEGORY_NODE2(dns, native), "lookup", req_wrap.get(),
      "count", n, "verbatim", verbatim);

  // Make the callback into JavaScript
  req_wrap->MakeCallback(env->oncomplete_string(), arraysize(argv), argv);
}

void AfterGetNameInfo(uv_getnameinfo_t* req,
                      int status,
                      const char* hostname,
                      const char* service) {
  std::unique_ptr<GetNameInfoReqWrap> req_wrap {
      static_cast<GetNameInfoReqWrap*>(req->data)};
  Environment* env = req_wrap->env();

  HandleScope handle_scope(env->isolate());
  Context::Scope context_scope(env->context());

  Local<Value> argv[] = {
    Integer::New(env->isolate(), status),
    Null(env->isolate()),
    Null(env->isolate())
  };

  if (status == 0) {
    // Success
    Local<String> js_hostname = OneByteString(env->isolate(), hostname);
    Local<String> js_service = OneByteString(env->isolate(), service);
    argv[1] = js_hostname;
    argv[2] = js_service;
  }

  TRACE_EVENT_NESTABLE_ASYNC_END2(
      TRACING_CATEGORY_NODE2(dns, native), "lookupService", req_wrap.get(),
      "hostname", TRACE_STR_COPY(hostname),
      "service", TRACE_STR_COPY(service));

  // Make the callback into JavaScript
  req_wrap->MakeCallback(env->oncomplete_string(), arraysize(argv), argv);
}

using ParseIPResult =
    decltype(static_cast<ares_addr_port_node*>(nullptr)->addr);

int ParseIP(const char* ip, ParseIPResult* result = nullptr) {
  ParseIPResult tmp;
  if (result == nullptr) result = &tmp;
  if (0 == uv_inet_pton(AF_INET, ip, result)) return 4;
  if (0 == uv_inet_pton(AF_INET6, ip, result)) return 6;
  return 0;
}

void CanonicalizeIP(const FunctionCallbackInfo<Value>& args) {
  Isolate* isolate = args.GetIsolate();
  node::Utf8Value ip(isolate, args[0]);

  ParseIPResult result;
  const int rc = ParseIP(*ip, &result);
  if (rc == 0) return;

  char canonical_ip[INET6_ADDRSTRLEN];
  const int af = (rc == 4 ? AF_INET : AF_INET6);
  CHECK_EQ(0, uv_inet_ntop(af, &result, canonical_ip, sizeof(canonical_ip)));
  Local<String> val = String::NewFromUtf8(isolate, canonical_ip,
      NewStringType::kNormal).ToLocalChecked();
  args.GetReturnValue().Set(val);
}

void GetAddrInfo(const FunctionCallbackInfo<Value>& args) {
  Environment* env = Environment::GetCurrent(args);

  CHECK(args[0]->IsObject());
  CHECK(args[1]->IsString());
  CHECK(args[2]->IsInt32());
  CHECK(args[4]->IsBoolean());
  Local<Object> req_wrap_obj = args[0].As<Object>();
  node::Utf8Value hostname(env->isolate(), args[1]);

  int32_t flags = 0;
  if (args[3]->IsInt32()) {
    flags = args[3].As<Int32>()->Value();
  }

  int family;

  switch (args[2].As<Int32>()->Value()) {
    case 0:
      family = AF_UNSPEC;
      break;
    case 4:
      family = AF_INET;
      break;
    case 6:
      family = AF_INET6;
      break;
    default:
      CHECK(0 && "bad address family");
  }

  auto req_wrap = std::make_unique<GetAddrInfoReqWrap>(env,
                                                       req_wrap_obj,
                                                       args[4]->IsTrue());

  struct addrinfo hints;
  memset(&hints, 0, sizeof(hints));
  hints.ai_family = family;
  hints.ai_socktype = SOCK_STREAM;
  hints.ai_flags = flags;

  TRACE_EVENT_NESTABLE_ASYNC_BEGIN2(
      TRACING_CATEGORY_NODE2(dns, native), "lookup", req_wrap.get(),
      "hostname", TRACE_STR_COPY(*hostname),
      "family",
      family == AF_INET ? "ipv4" : family == AF_INET6 ? "ipv6" : "unspec");

  int err = req_wrap->Dispatch(uv_getaddrinfo,
                               AfterGetAddrInfo,
                               *hostname,
                               nullptr,
                               &hints);
  if (err == 0)
    // Release ownership of the pointer allowing the ownership to be transferred
    USE(req_wrap.release());

  args.GetReturnValue().Set(err);
}

void GetNameInfo(const FunctionCallbackInfo<Value>& args) {
  Environment* env = Environment::GetCurrent(args);

  CHECK(args[0]->IsObject());
  CHECK(args[1]->IsString());
  CHECK(args[2]->IsUint32());
  Local<Object> req_wrap_obj = args[0].As<Object>();
  node::Utf8Value ip(env->isolate(), args[1]);
  const unsigned port = args[2]->Uint32Value(env->context()).FromJust();
  struct sockaddr_storage addr;

  CHECK(uv_ip4_addr(*ip, port, reinterpret_cast<sockaddr_in*>(&addr)) == 0 ||
        uv_ip6_addr(*ip, port, reinterpret_cast<sockaddr_in6*>(&addr)) == 0);

  auto req_wrap = std::make_unique<GetNameInfoReqWrap>(env, req_wrap_obj);

  TRACE_EVENT_NESTABLE_ASYNC_BEGIN2(
      TRACING_CATEGORY_NODE2(dns, native), "lookupService", req_wrap.get(),
      "ip", TRACE_STR_COPY(*ip), "port", port);

  int err = req_wrap->Dispatch(uv_getnameinfo,
                               AfterGetNameInfo,
                               reinterpret_cast<struct sockaddr*>(&addr),
                               NI_NAMEREQD);
  if (err == 0)
    // Release ownership of the pointer allowing the ownership to be transferred
    USE(req_wrap.release());

  args.GetReturnValue().Set(err);
}

void GetServers(const FunctionCallbackInfo<Value>& args) {
  Environment* env = Environment::GetCurrent(args);
  ChannelWrap* channel;
  ASSIGN_OR_RETURN_UNWRAP(&channel, args.Holder());

  Local<Array> server_array = Array::New(env->isolate());

  ares_addr_port_node* servers;

  int r = ares_get_servers_ports(channel->cares_channel(), &servers);
  CHECK_EQ(r, ARES_SUCCESS);
  auto cleanup = OnScopeLeave([&]() { ares_free_data(servers); });

  ares_addr_port_node* cur = servers;

  for (uint32_t i = 0; cur != nullptr; ++i, cur = cur->next) {
    char ip[INET6_ADDRSTRLEN];

    const void* caddr = static_cast<const void*>(&cur->addr);
    int err = uv_inet_ntop(cur->family, caddr, ip, sizeof(ip));
    CHECK_EQ(err, 0);

    Local<Value> ret[] = {
      OneByteString(env->isolate(), ip),
      Integer::New(env->isolate(), cur->udp_port)
    };

    if (server_array->Set(env->context(), i,
                          Array::New(env->isolate(), ret, arraysize(ret)))
          .IsNothing()) {
      return;
    }
  }

  args.GetReturnValue().Set(server_array);
}

void SetServers(const FunctionCallbackInfo<Value>& args) {
  Environment* env = Environment::GetCurrent(args);
  ChannelWrap* channel;
  ASSIGN_OR_RETURN_UNWRAP(&channel, args.Holder());

  if (channel->active_query_count()) {
    return args.GetReturnValue().Set(DNS_ESETSRVPENDING);
  }

  CHECK(args[0]->IsArray());

  Local<Array> arr = Local<Array>::Cast(args[0]);

  uint32_t len = arr->Length();

  if (len == 0) {
    int rv = ares_set_servers(channel->cares_channel(), nullptr);
    return args.GetReturnValue().Set(rv);
  }

  std::vector<ares_addr_port_node> servers(len);
  ares_addr_port_node* last = nullptr;

  int err;

  for (uint32_t i = 0; i < len; i++) {
    CHECK(arr->Get(env->context(), i).ToLocalChecked()->IsArray());

    Local<Array> elm =
        Local<Array>::Cast(arr->Get(env->context(), i).ToLocalChecked());

    CHECK(elm->Get(env->context(),
                   0).ToLocalChecked()->Int32Value(env->context()).FromJust());
    CHECK(elm->Get(env->context(), 1).ToLocalChecked()->IsString());
    CHECK(elm->Get(env->context(),
                   2).ToLocalChecked()->Int32Value(env->context()).FromJust());

    int fam = elm->Get(env->context(), 0)
        .ToLocalChecked()->Int32Value(env->context()).FromJust();
    node::Utf8Value ip(env->isolate(),
                       elm->Get(env->context(), 1).ToLocalChecked());
    int port = elm->Get(env->context(), 2)
        .ToLocalChecked()->Int32Value(env->context()).FromJust();

    ares_addr_port_node* cur = &servers[i];

    cur->tcp_port = cur->udp_port = port;
    switch (fam) {
      case 4:
        cur->family = AF_INET;
        err = uv_inet_pton(AF_INET, *ip, &cur->addr);
        break;
      case 6:
        cur->family = AF_INET6;
        err = uv_inet_pton(AF_INET6, *ip, &cur->addr);
        break;
      default:
        CHECK(0 && "Bad address family.");
    }

    if (err)
      break;

    cur->next = nullptr;

    if (last != nullptr)
      last->next = cur;

    last = cur;
  }

  if (err == 0)
    err = ares_set_servers_ports(channel->cares_channel(), &servers[0]);
  else
    err = ARES_EBADSTR;

  if (err == ARES_SUCCESS)
    channel->set_is_servers_default(false);

  args.GetReturnValue().Set(err);
}

void Cancel(const FunctionCallbackInfo<Value>& args) {
  ChannelWrap* channel;
  ASSIGN_OR_RETURN_UNWRAP(&channel, args.Holder());

  TRACE_EVENT_INSTANT0(TRACING_CATEGORY_NODE2(dns, native),
      "cancel", TRACE_EVENT_SCOPE_THREAD);

  ares_cancel(channel->cares_channel());
}

const char EMSG_ESETSRVPENDING[] = "There are pending queries.";
void StrError(const FunctionCallbackInfo<Value>& args) {
  Environment* env = Environment::GetCurrent(args);
  int code = args[0]->Int32Value(env->context()).FromJust();
  const char* errmsg = (code == DNS_ESETSRVPENDING) ?
    EMSG_ESETSRVPENDING :
    ares_strerror(code);
  args.GetReturnValue().Set(OneByteString(env->isolate(), errmsg));
}

void Initialize(Local<Object> target,
                Local<Value> unused,
                Local<Context> context,
                void* priv) {
  Environment* env = Environment::GetCurrent(context);

  env->SetMethod(target, "getaddrinfo", GetAddrInfo);
  env->SetMethod(target, "getnameinfo", GetNameInfo);
  env->SetMethodNoSideEffect(target, "canonicalizeIP", CanonicalizeIP);

  env->SetMethod(target, "strerror", StrError);

  target->Set(env->context(), FIXED_ONE_BYTE_STRING(env->isolate(), "AF_INET"),
              Integer::New(env->isolate(), AF_INET)).Check();
  target->Set(env->context(), FIXED_ONE_BYTE_STRING(env->isolate(), "AF_INET6"),
              Integer::New(env->isolate(), AF_INET6)).Check();
  target->Set(env->context(), FIXED_ONE_BYTE_STRING(env->isolate(),
                                                    "AF_UNSPEC"),
              Integer::New(env->isolate(), AF_UNSPEC)).Check();
  target->Set(env->context(), FIXED_ONE_BYTE_STRING(env->isolate(),
                                                    "AI_ADDRCONFIG"),
              Integer::New(env->isolate(), AI_ADDRCONFIG)).Check();
  target->Set(env->context(), FIXED_ONE_BYTE_STRING(env->isolate(),
                                                    "AI_ALL"),
              Integer::New(env->isolate(), AI_ALL)).Check();
  target->Set(env->context(), FIXED_ONE_BYTE_STRING(env->isolate(),
                                                    "AI_V4MAPPED"),
              Integer::New(env->isolate(), AI_V4MAPPED)).Check();

  Local<FunctionTemplate> aiw =
      BaseObject::MakeLazilyInitializedJSTemplate(env);
  aiw->Inherit(AsyncWrap::GetConstructorTemplate(env));
  Local<String> addrInfoWrapString =
      FIXED_ONE_BYTE_STRING(env->isolate(), "GetAddrInfoReqWrap");
  aiw->SetClassName(addrInfoWrapString);
  target->Set(env->context(),
              addrInfoWrapString,
              aiw->GetFunction(context).ToLocalChecked()).Check();

  Local<FunctionTemplate> niw =
      BaseObject::MakeLazilyInitializedJSTemplate(env);
  niw->Inherit(AsyncWrap::GetConstructorTemplate(env));
  Local<String> nameInfoWrapString =
      FIXED_ONE_BYTE_STRING(env->isolate(), "GetNameInfoReqWrap");
  niw->SetClassName(nameInfoWrapString);
  target->Set(env->context(),
              nameInfoWrapString,
              niw->GetFunction(context).ToLocalChecked()).Check();

  Local<FunctionTemplate> qrw =
      BaseObject::MakeLazilyInitializedJSTemplate(env);
  qrw->Inherit(AsyncWrap::GetConstructorTemplate(env));
  Local<String> queryWrapString =
      FIXED_ONE_BYTE_STRING(env->isolate(), "QueryReqWrap");
  qrw->SetClassName(queryWrapString);
  target->Set(env->context(),
              queryWrapString,
              qrw->GetFunction(context).ToLocalChecked()).Check();

  Local<FunctionTemplate> channel_wrap =
      env->NewFunctionTemplate(ChannelWrap::New);
  channel_wrap->InstanceTemplate()->SetInternalFieldCount(
      ChannelWrap::kInternalFieldCount);
  channel_wrap->Inherit(AsyncWrap::GetConstructorTemplate(env));

  env->SetProtoMethod(channel_wrap, "queryAny", Query<QueryAnyWrap>);
  env->SetProtoMethod(channel_wrap, "queryA", Query<QueryAWrap>);
  env->SetProtoMethod(channel_wrap, "queryAaaa", Query<QueryAaaaWrap>);
  env->SetProtoMethod(channel_wrap, "queryCname", Query<QueryCnameWrap>);
  env->SetProtoMethod(channel_wrap, "queryMx", Query<QueryMxWrap>);
  env->SetProtoMethod(channel_wrap, "queryNs", Query<QueryNsWrap>);
  env->SetProtoMethod(channel_wrap, "queryTxt", Query<QueryTxtWrap>);
  env->SetProtoMethod(channel_wrap, "querySrv", Query<QuerySrvWrap>);
  env->SetProtoMethod(channel_wrap, "queryPtr", Query<QueryPtrWrap>);
  env->SetProtoMethod(channel_wrap, "queryNaptr", Query<QueryNaptrWrap>);
  env->SetProtoMethod(channel_wrap, "querySoa", Query<QuerySoaWrap>);
  env->SetProtoMethod(channel_wrap, "getHostByAddr", Query<GetHostByAddrWrap>);

  env->SetProtoMethodNoSideEffect(channel_wrap, "getServers", GetServers);
  env->SetProtoMethod(channel_wrap, "setServers", SetServers);
  env->SetProtoMethod(channel_wrap, "cancel", Cancel);

  Local<String> channelWrapString =
      FIXED_ONE_BYTE_STRING(env->isolate(), "ChannelWrap");
  channel_wrap->SetClassName(channelWrapString);
  target->Set(env->context(), channelWrapString,
              channel_wrap->GetFunction(context).ToLocalChecked()).Check();
}

}  // anonymous namespace
}  // namespace cares_wrap
}  // namespace node

NODE_MODULE_CONTEXT_AWARE_INTERNAL(cares_wrap, node::cares_wrap::Initialize)


text:
Roseanne Reboot Cancelled After Racist Tweet
Walt Disney Co's ABC network has cancelled the popular US television comedy Roseanne after star Roseanne Barr sparked outrage by comparing a black former Obama administration official to an ape in remarks on Twitter.
"Roseanne's Twitter statement is abhorrent, repugnant and inconsistent with our values, and we have decided to cancel her show," ABC Entertainment president Channing Dungey said in a statement on Tuesday.
Disney chief executive Bob Iger added on Twitter: "There was only one thing to do here, and that was the right thing."
In a since deleted comment on Twitter, Barr compared former Obama adviser Valerie Jarrett, 61, to an ape.
She wrote that if the Islamist political movement "muslim brotherhood & planet of the apes had a baby = vj."
Barr, 65, apologised "for making a bad joke" about Jarrett, who is black and was born in Iran to American parents. In the fallout, her agents, ICM Partners, dropped Barr as a client.
I apologize to Valerie Jarrett and to all Americans. I am truly sorry for making a bad joke about her politics and her looks. I should have known better. Forgive me-my joke was in bad taste.
— Roseanne Barr (@therealroseanne) May 29, 2018
"We are all greatly distressed by the disgraceful and unacceptable tweet from Roseanne Barr this morning," ICM Partners said in a statement on Tuesday.
"What she wrote is antithetical to our core values, both as individuals and as an agency. Consequently, we have notified her that we will not represent her. Effective immediately, Roseanne Barr is no longer a client."
Barr signed with ICM last August, just as the reboot of Roseanne went into production.
Roseanne was ABC's biggest hit of the 2017-2018 season.
The show drew an average of 18.7 million viewers, second only to CBS sitcom The Big Bang Theory, according to Nielsen data through May 20. Jarrett, through spokesman Jordan Finkelstein, declined to comment.
The original Roseanne aired from 1988 to 1997. It featured a blue-collar family, the Conners, with overweight parents struggling to get by and was praised for its realistic portrayal of working-class life.
President Donald Trump has latched on to the show's huge viewership as evidence that his supporters, which include Barr, want shows that speak to their concerns.
The Man Who Saved A Baby From A Ledge Reminded Us Of Someone
Cornelia Frances Passes Away After Long Cancer Battle
Prince Harry & Meghan Markle Might Be Buying A House In Oz

redpajama_set_name: RedPajamaCommonCrawl


text:
Aztec Baseball Looks to Continue Remarkable Turnaround
April 17, 2017 Brandon Battaglia
The Aztecs came into this season after a disappointing 2016 campaign in which they finished 21-38. This year's Aztec squad has returned with renewed vigor and a determination to reclaim their place at the top of the Mountain West. It is in stark contrast to the poor play of the 2016 season, as the Aztecs find themselves at 24-10 heading into Thursday night's game at UNLV. This turnaround cannot be attributed to one player, for it has been the success of players in all three phases of the game: pitching, defense, and offense.
The starting pitching has been the start of it all, with Brett Seeburger enjoying a bounceback season (6-1, 2.65 ERA) along with Dominic Purpura (5-0, 3.06 ERA). This excellent starting pitching has given a key bridge to what has been a very effective bullpen. The SDSU bullpen this season has featured breakout performances from junior Jacob Erickson (1.50 ERA), freshman Logan Boyer (0.79 ERA), as well as Adrian Mardueno (2.77 ERA). These bullpen pieces have provided solid relief pitching appearances, giving way to standout closer CJ Saylor (1.80 ERA), who has 9 saves on the season. Despite these impressive players, the pitching has only been the start of the Aztecs' success.
The Aztec defense has also contributed to the team's success, especially on the infield. Last season Alan Trejo struggled at shortstop, perhaps contributing to his offensive dip; however, with the move to second base this season, he is enjoying a breakout year. The addition of Danny Sheehan at shortstop, who the Aztecs dearly missed last season, has provided great defense and important veteran leadership. This formidable combination up the middle has given the Aztecs' pitching staff a reliable defense to count on. Third baseman Andrew Brown and first baseman Jordan Verdon have also contributed solid play at both corners. The outfield remains solid, as Chase Calabuig, Tyler Adkison, and Julian Escobedo have proved to be a speedy outfield unit. The catching platoon which includes Dean Nevarez and Hunter Stratton have provided good pitch calling and defense behind the plate. Even freshman catcher Ryan Orr has had a breakout performance against UCLA, throwing out 3 would-be base stealers.
Perhaps the biggest component to the Aztecs' turnaround has been the offense. The SDSU lineup has become an offensive juggernaut, with standouts Adkison (.402 BA), Brown (.340 BA), Trejo (.315), and Sheehan (.328 BA) leading the way. While these players catch the eye, the lineup has been productive one through nine. Trejo has been the catalyst at the top of the order, getting on base for the likes of Brown and Sheehan. Adkison has provided the power with a team-leading nine home runs on the season. Even the bottom of the order has produced, as DH Andrew Martinez has contributed four home runs to go along with a .320 BA. Nevarez has contributed to the hit parade as well, adding .273 BA as well as three home runs.
We are witnessing a truly great college baseball team with fantastic performances at every position. The Aztecs are looking to continue this exciting turnaround as we head towards the close of the regular season.
KCR Blog, KCR Sports Alan Trejo, Andrew Brown, Andrew Martinez, Aztec Baseball, Brett Seeburger, Chase Calabuig, Dean Nevarez, Dominic Purpura, Hunter Stratton, Jacob Erickson, Jordan Verdon, Logan Boyer, Ryan Orr, Tyler Adkison


We've all used software that would be oh-so-perfect if that one nasty bug got squashed, and Lance Gilmore, Product Operations Manager at T-Mobile, is no exception. Apparently, I'm not the only person with a shiny new G2 that has been having problems with Google Listen asking for a Gmail account after I've already entered it, nor are the other folks reporting the bug over at the Google Listen discussion list. But lo and behold, Lance gets results. In other words -- Lance Does. Google Listen received an update to fix this particular bug mere hours after someone with a better title than "Hillbilly Blogger" chimed in on the issue. So if you were seeing this problem, update and give it a try.
And p.s.: If Google would like to go ahead and update its podcast index to add the World's Greatest Android Podcast Ever, that would be nice, too.


text:
Faces on the Radio
WE LISTEN SO YOU DON'T HAVE TO
About FOTR
Late Night Dispatch
Category Archives: Late Night Dispatch
LATE NIGHT DISPATCHES: The Replacements / The National
The Replacements // Photo Credit: Colin Sanders
https://facesontheradio.files.wordpress.com/2013/09/mats-recap.mp3
Here are our Late Night Dispatches from this weekend! The first is our newest correspondent, Colin Sanders, reporting after Riot Fest in Denver, CO – and it's a LONG one. You can download that one HERE.
The National // Photo Credit: Colin McLaughlin
https://facesontheradio.files.wordpress.com/2013/09/late-night-dispatch_-the-national-w.mp3
The second is Hollister, Arya, and friend-of-the-show Colin McLaughlin, after catching the transcendent National set at Edgefield Amphitheater. That can be downloaded HERE.
We hope you enjoy these!
Tagged Colin McLaughlin, Colin Sanders, Faces on the Radio, Frightened Rabbit, Live Music, Oregon, Podcast, Portland, Reviews, Riot Fest, The National, The Replacements
Late Night Dispatch, Podcast
BONUS EPISODE: Faces On The Road-io vs. Jeff Mangum
Here's a bonus episode of Faces on the Radio, part of which was recorded in Eugene, OR, during Jeff Mangum's performance. We (Hollister, Kelly, and Cannon Riggs) recorded a 13 minute late-night dispatch to accompany these recordings, but technical difficulties ruined this recording. Included instead is a 1o-minutes-shorter recap of the performance, recorded live in Hollister's living room.
Kelly Dixon managed to record the entire set, and included in the episode is three of Mangum's songs, "Holland, 1945" / "Two-Headed Boy Part 2" and "Gardenhead." These recordings were captured on the fly, and the quality is not pristine, but it is still an incredible memento of an incredible performance.
In addition to that, many thanks to New York's Tall Firs for providing us with an intro for this episode!
Enjoy the show! You can listen above, or right-click HERE to download.
Tagged Eugene, Faces on the Radio, Jeff Mangum, Late Night Dispatch, Live Music, Oregon, Podcast, Reviews, Tall Firs
LATE-NIGHT DISPATCH: Psychic TV
Here's the late-night dispatch that Yousef recorded after seeing a very rare performance by Psychic TV at Rotture! Click to listen/right-click to download!
Tagged Late Night Dispatch, Podcast, Portland, Psychic TV, Rotture
LATE NIGHT DISPATCHES
We've decided to record a small segment after every show we go to, while the experience is fresh in our heads. We intended to put them up immediately, but we didn't get the chance. We have three, two of which are packaged together. The first one is for the Built to Spill secret show at Bunk Bar in October, combined with a review of Snow Patrol and Noel Gallagher's co-headline set at the Arlene Schnitzer Concert Hall. Click here to listen/right-click to download.
The second is from the homecoming show by Menomena, at the Crystal Ballroom, with special guest and friend of the show, Anna Mackay. Click here to listen/right-click to download.
Tagged Arlene Schnitzer Concert Hall, Built To Spill, Bunk Bar, Crystal Ballroom, Late Night Dispatch, Live Music, Menomena, Noel Gallagher, Oregon, ortland, Portland, Snow Patrol
Arya on Facebook
Arya on Tumblr
Arya on Twitter
FotR on Facebook
Hollister on Facebook
Hollister on Twitter
This Temporary Life
Yousef on Facebook
Archives Select Month August 2017 July 2017 June 2017 March 2017 February 2017 November 2016 September 2016 August 2016 July 2016 June 2016 May 2016 April 2016 March 2016 February 2016 January 2016 December 2015 November 2015 October 2015 September 2015 August 2015 July 2015 June 2015 May 2015 April 2015 March 2015 February 2015 January 2015 December 2014 November 2014 October 2014 September 2014 August 2014 July 2014 June 2014 May 2014 April 2014 March 2014 February 2014 January 2014 December 2013 November 2013 October 2013 September 2013 August 2013 July 2013 June 2013 May 2013 April 2013 March 2013 February 2013 January 2013 December 2012 November 2012 October 2012 September 2012 July 2012 June 2012 May 2012 April 2012 March 2012 January 2012 December 2011 November 2011 October 2011 September 2011 August 2011 June 2011 May 2011 March 2011 December 2010 October 2010 September 2010 August 2010 June 2010 April 2010 March 2010 January 2010 November 2009 July 2009 May 2009 September 2008 August 2008 July 2008 May 2008 March 2008
Faces on the Radio RSS


Alongside a robust condemnation of antisemitic discourse, it is imperative that we celebrate the extraordinary contribution of the Jewish community to British society over the centuries through science (Chain on penicillin), ethical finance (the Rothschilds) and the arts (Menuhin), to name but a few. To write a book on Jewish contributions to British life would require multiple volumes, not merely these few names at the front of my mind.
The Most Reverend Justin Welby is the 105th Archbishop of Canterbury. Before his ordination he worked in the oil industry for eleven years. After serving as a parish priest he ran the international reconciliation ministry at Coventry Cathedral, later becoming Dean of Liverpool Cathedral and then Bishop of Durham.


Moving and looking for a school.
Grandmaster Ashida Kim - Is this a joke??? This in MABS - Is this a joke???
How do you get out of here ??????
Help me out here. Ninjutsu spawned Judo?
Know any good judo clubs in Northen NJ/Manhattan?
Best thing ever on ebay!
Hello, happy welcome to me!


text:
The Competition Authority imposes a 500 m. ISK fine on Valitor, a firm operating in the payment card market, for serious breaches of competition law.
In the decision of the Competition Authority, no. 8/2013, which is published to day, the authority concludes that Valitor, a firm operating in the payment card market, has abused its dominant position on the market by actions aimed at the competitors of Valitor in the payment card acquiring services market in Iceland. Valitor has also breached the conditions of a settlement between Valitor and the Competition Authority which Valitor was obliged to honour.
Valitor operates in the market for payment card acquiring services. Payment card acquiring services consist in authorising vendors to accept payments by payment cards, collecting their data and disbursing the proceeds when card holders pay their bills. Valitor has a dominant position on this market in Iceland, but two other undertakings operate on this market, Borgun hf. and Kortaþjónustan (Teller).
In the end of 2007 Valitor admitted extensive breaches of the competition act and agreed to pay an administrative fine to the amount of 385 m. ISK. Valitor also agreed to abide by conditions which were meant to prevent the company in abusing its dominant position on the market by actions that would cause competitive harm to its competitors. This case was concluded in the beginning of 2008 with the decision of the Competition Authority no. 4/2008.
In the decision of the Competition Authority, published today, the Competition Authority states that Valitor breached against two conditions which the undertaking had agreed to abide by by the settlement from 2007. Amongst other things, Valitor abused confidential information about its competitors in the market for acquiring services which the undertaking had access to because of its operations as the issuer of VISA cards here in Iceland. These conditions were a response to serious breaches of Valitor and were set to prevent further breaches by the undertaking. The Competition Authority deems it very serious when undertakings breach conditions of this kind, which they have agreed to abide by.
Valitor is also guilty of predatory pricing, and it priced its services in acquiring services for debit cards under variable cost in the years 2007 and 2008. By this pricing, the undertaking was likely to gain contracts with vendors in acquiring services of credit cards, which are considered to be a more profitable service. The scale of the predatory pricing was considerable.
Valitor has been found guilty of repeated offence and the Competition Authority deems these offences to have been committed with intent. These are very serious offences and the Competition Authority deems it fitting that Valitor pay 500 m. ISK in administrative fines.


text:
High tech, Small business, Verticals
Founder vs CEO: What's the difference?
Tom Angelucci
Business terminology can get a little complicated. Between founder, CEO, Vice President, Managing Director, CTO, and CFO, there's a lot to unpack.
For startups especially, you need to understand these positions to organize your company's hierarchy. That is, where you and your employees fit within the company.
You'll also need to establish when and how they work. For example, will you rent an office base, or does everyone work from home as part of a distributed team? If it's the latter, you'll need to consider how—as either a CEO or founder—you can stay accessible to your team.
We'll expand later on how to use the right tools to help you with that. In this post, we'll look at:
What is a founder?
What is a CEO?
How both founders and CEOs can stay accessible to the team
Founder vs CEO: how are they different?
Characteristics of a founder vs CEO
First, though, let's look in detail at the "founder vs CEO" difference to give you a better understanding of how to structure a startup.
🤝 Is your tech startup's team collaborating as effectively as it could be? What should you look out for before you start scaling in earnest?
🌟 Download the checklist to find out
👀 Get this free checklist to evaluate if your startup's current approach is working for you.
To understand the difference between a founder and a CEO, let's first look at the definition for each.
A founder is the person who starts their own company. They're the one who came up with the business idea and acted on it. For example, Jeff Bezos of Amazon is probably the founder who comes most readily to mind. Amazon distributes goods worldwide, but once upon a time, it was a bookstore operating from a garage.
A founder needs to secure funding, bring resources, and market the brand. Unlike a CEO, the founder of the business will always remain the same, even if they leave.
In cases where there is more than one founder, they are co-founders. And, usually, the founder of a startup is also referred to as an entrepreneur.
What are the 4 key components of a successful startup?
🌟 Find out here
Learn to bake success into the DNA of your startup
Happy launching!
A Chief Executive Officer (CEO) is the highest-ranking executive in the business. Typically, they work with the founder to carry out the business's strategy and vision.
A CEO needs to delegate tasks, communicate with board members, and liaise with investors. Even though it comes with great responsibility, the title of CEO is often a coveted one because they take up the top position in the business and reap the benefits.
Some founders are also CEOs. For example, Steve Jobs was a co-founder of Apple, but also a CEO. Now, after his passing, Tim Cook has been appointed to the CEO role.
However, although every business has a founder, not every founder needs to be the CEO, too.
One of the biggest responsibilities of both the founder and CEO is to stay accessible to their team. It's also one of the biggest challenges, because founders and CEOs tend to be…
In important meetings at all hours of the day (and night)
Traveling somewhere for important meetings
Deep in work planning for the next important meeting
Basically, they're busy.
So how can they remain accessible to their teams and make employees feel as though they can talk to their CEO about any concerns or issues?
Traditionally, this is often referred to as the "open door" policy. But, as workplace dynamics change and more people are working remotely in large companies, how can those in charge make sure they maintain the same level of communication? By investing in smart software, of course.
A good communication platform, for example, is almost always the best way to solve this problem. It removes the communication barrier some CEOs and their team may experience, no matter how big or distributed the team is.
For instance, RingCentral's desktop and mobile app is essentially an all-in-one solution that includes HD video, messaging, and phone calling—all in one interface:
We'll discuss what this means for businesses later in this section.
Using a platform like RingCentral is key to making it easier for a business to adapt to a modern working environment. How?
Does the CEO / founder need to make phone or conference calls with an investor or board? ✅
Do they need to have a face-to-face check-in with their senior leadership team? ✅
What if they just need a fast and simple way to stay up to date with employees in group chats and give employees a way to message them with thoughts? ✅
With RingCentral, all of these functions—integrated phone calling, video conferencing, and team messaging—are all in one convenient place. This means that a CEO can stay in touch with managers and the rest of the executive team through pretty much any communication channel they prefer.
Need to make a company-wide announcement or update? With RingCentral, you can send a video conference invite straight to employees' email addresses thanks to the Gmail and Outlook calendar integrations to let them know the time and place of the meeting.
During the meeting, you can also send files and screen share:
A virtual open door
By equipping all team members with this type of communication software, you're also letting them know they can reach you when they need to. Today, a virtual open door is entirely possible, no matter where in the world you work.
Managers can message the CEO and execs with just a few clicks or jump on a call in just one tap. You no longer need to be in the office to be able to communicate effectively.
What's more, for founders and CEOs who are budget-conscious, it's a great way to cut down costs by using just one efficient piece of software. Versatile apps that can do multiple things well are especially important for smaller businesses and startups.
For instance, RingCentral integrates with apps like Gmail and Salesforce to allow your team to work seamlessly with existing tools:
And, with everything in one place, you're streamlining your tech stack, helping the team stay productive, and saving them time because they don't have to toggle between so many apps.
Although both pivotal roles in a great company, founder and CEO are not the same thing. The founder is the creator of the business, who can then hire a CEO further down the line.
One of the main differences between the founder and CEO positions is their responsibilities. When a business owner begins creating plans for their company, one of the first things they need to decide is what title to give themselves and any other employees. They also need to create a business plan and set goals. On the other hand, the CEO needs to manage the employee talent pool and help to scale the business.
Here are some of the other main responsibilities you'd find in the job description of a founder and CEO:
Responsibilities of a founder
Getting the business off the ground
Determining the company's vision
Setting a vision for the company
Managing finances, along with the Chief Financial Officer
Assembling a strong team
Maintain a good relationship with board of directors
Making the final decision
Responsibilities of a CEO
Implement the company's business model
Coach the team
Help to scale the business
Communicate with internal and external parties
Set the company culture
From Bill Gates to Mark Zuckerberg, there are certain characteristics associated with founders and CEOs. In fact, there are many who believe all CEOs should have the same traits.
Characteristics of a founder
Willing to work unsociable hours to achieve their dream
Strong networking skills
Courage to take calculated risks
Characteristics of a professional CEO
Willingness to learn
Good team management skills
A good listener
Founder vs CEO, or founder and CEO?
Although business structures differ, there's always a founder or co-founders. From there, the business hierarchy may change as new job titles come into play as the business grows.
Now you know the ins and outs of founder vs CEO, it's up to you to decide whether you think it's crucial for your small business to have separate roles at this early-stage or whether you think you can take on both.
At some point in the future, you may choose to hire a new CEO, and when this happens, ensure they are passionate about your business and its goals. Because although the positions differ, together they can completely change a business's course—hopefully towards even greater success.
Originally published Feb 15, 2021, updated Feb 22, 2021
Small business, SMB communication, SMB productivity
The 10 key benefits of cloud computing
The cloud computing revolution has had truly transformative effects over the last decade or so. More and more firms are increasingly reliant on cloud services and data centers, simply to operate from day to day. This has made life easier and more convenient for consumers, too, even those who don't appreciate what an impact the ...
3 Ways Free File Sharing Makes Your Team More Productive
4 cool ways to use file sharing in your RingCentral app
Small business, SMB communication, SMB mobility
The best collaboration software for efficient remote teams