Exemple #1
0
/* Consume the given number of bytes from the head of the TCP send queue. */
static void advance_tcp_send_queue(ares_channel channel, int whichserver,
                                   ares_ssize_t num_bytes)
{
  struct send_request *sendreq;
  struct server_state *server = &channel->servers[whichserver];
  while (num_bytes > 0) {
    sendreq = server->qhead;
    if ((size_t)num_bytes >= sendreq->len) {
      num_bytes -= sendreq->len;
      server->qhead = sendreq->next;
      if (sendreq->data_storage)
        ares_free(sendreq->data_storage);
      ares_free(sendreq);
      if (server->qhead == NULL) {
        SOCK_STATE_CALLBACK(channel, server->tcp_socket, 1, 0);
        server->qtail = NULL;

        /* qhead is NULL so we cannot continue this loop */
        break;
      }
    }
    else {
      sendreq->data += num_bytes;
      sendreq->len -= num_bytes;
      num_bytes = 0;
    }
  }
}
Exemple #2
0
static void end_squery(struct search_query *squery, int status,
                       unsigned char *abuf, int alen)
{
  squery->callback(squery->arg, status, squery->timeouts, abuf, alen);
  ares_free(squery->name);
  ares_free(squery);
}
Exemple #3
0
static void end_hquery(struct host_query *hquery, int status,
                       struct hostent *host)
{
  hquery->callback(hquery->arg, status, hquery->timeouts, host);
  if (host)
    ares_free_hostent(host);
  ares_free(hquery->name);
  ares_free(hquery);
}
Exemple #4
0
void ares_strsplit_free(char **elms, size_t num_elm)
{
  size_t i;

  if (elms == NULL)
    return;

  for (i=0; i<num_elm; i++)
    ares_free(elms[i]);
  ares_free(elms);
}
Exemple #5
0
void ares_query(ares_channel channel, const char *name, int dnsclass,
                int type, ares_callback callback, void *arg)
{
  struct qquery *qquery;
  unsigned char *qbuf;
  int qlen, rd, status;

  /* Compose the query. */
  rd = !(channel->flags & ARES_FLAG_NORECURSE);
  status = ares_create_query(name, dnsclass, type, channel->next_id, rd, &qbuf,
              &qlen, (channel->flags & ARES_FLAG_EDNS) ? channel->ednspsz : 0);
  if (status != ARES_SUCCESS)
    {
      if (qbuf != NULL) ares_free(qbuf);
      callback(arg, status, 0, NULL, 0);
      return;
    }

  channel->next_id = generate_unique_id(channel);

  /* Allocate and fill in the query structure. */
  qquery = ares_malloc(sizeof(struct qquery));
  if (!qquery)
    {
      ares_free_string(qbuf);
      callback(arg, ARES_ENOMEM, 0, NULL, 0);
      return;
    }
  qquery->callback = callback;
  qquery->arg = arg;

  /* Send it off.  qcallback will be called when we get an answer. */
  ares_send(channel, qbuf, qlen, qcallback, qquery);
  ares_free_string(qbuf);
}
Exemple #6
0
ares_ssize_t ares_writev(ares_socket_t s, const struct iovec *iov, int iovcnt)
{
  char *buffer, *bp;
  int i;
  size_t bytes = 0;
  ares_ssize_t result;

  /* Validate iovcnt */
  if (iovcnt <= 0)
  {
    SET_ERRNO(EINVAL);
    return (-1);
  }

  /* Validate and find the sum of the iov_len values in the iov array */
  for (i = 0; i < iovcnt; i++)
  {
    if (iov[i].iov_len > INT_MAX - bytes)
    {
      SET_ERRNO(EINVAL);
      return (-1);
    }
    bytes += iov[i].iov_len;
  }

  if (bytes == 0)
    return (0);

  /* Allocate a temporary buffer to hold the data */
  buffer = ares_malloc(bytes);
  if (!buffer)
  {
    SET_ERRNO(ENOMEM);
    return (-1);
  }

  /* Copy the data into buffer */
  for (bp = buffer, i = 0; i < iovcnt; ++i)
  {
    memcpy (bp, iov[i].iov_base, iov[i].iov_len);
    bp += iov[i].iov_len;
  }

  /* Send buffer contents */
  result = swrite(s, buffer, bytes);

  ares_free(buffer);

  return (result);
}
Exemple #7
0
void ares_gethostbyname(ares_channel channel, const char *name, int family,
                        ares_host_callback callback, void *arg)
{
  struct host_query *hquery;

  /* Right now we only know how to look up Internet addresses - and unspec
     means try both basically. */
  switch (family) {
  case AF_INET:
  case AF_INET6:
  case AF_UNSPEC:
    break;
  default:
    callback(arg, ARES_ENOTIMP, 0, NULL);
    return;
  }

  if (fake_hostent(name, family, callback, arg))
    return;

  /* Allocate and fill in the host query structure. */
  hquery = ares_malloc(sizeof(struct host_query));
  if (!hquery)
    {
      callback(arg, ARES_ENOMEM, 0, NULL);
      return;
    }
  hquery->channel = channel;
  hquery->name = ares_strdup(name);
  hquery->want_family = family;
  hquery->sent_family = -1; /* nothing is sent yet */
  if (!hquery->name) {
    ares_free(hquery);
    callback(arg, ARES_ENOMEM, 0, NULL);
    return;
  }
  hquery->callback = callback;
  hquery->arg = arg;
  hquery->remaining_lookups = channel->lookups;
  hquery->timeouts = 0;

  /* Start performing lookups according to channel->lookups. */
  next_lookup(hquery, ARES_ECONNREFUSED /* initial error code */);
}
Exemple #8
0
static void qcallback(void *arg, int status, int timeouts, unsigned char *abuf, int alen)
{
  struct qquery *qquery = (struct qquery *) arg;
  unsigned int ancount;
  int rcode;

  if (status != ARES_SUCCESS)
    qquery->callback(qquery->arg, status, timeouts, abuf, alen);
  else
    {
      /* Pull the response code and answer count from the packet. */
      rcode = DNS_HEADER_RCODE(abuf);
      ancount = DNS_HEADER_ANCOUNT(abuf);

      /* Convert errors. */
      switch (rcode)
        {
        case NOERROR:
          status = (ancount > 0) ? ARES_SUCCESS : ARES_ENODATA;
          break;
        case FORMERR:
          status = ARES_EFORMERR;
          break;
        case SERVFAIL:
          status = ARES_ESERVFAIL;
          break;
        case NXDOMAIN:
          status = ARES_ENOTFOUND;
          break;
        case NOTIMP:
          status = ARES_ENOTIMP;
          break;
        case REFUSED:
          status = ARES_EREFUSED;
          break;
        }
      qquery->callback(qquery->arg, status, timeouts, abuf, alen);
    }
  ares_free(qquery);
}
Exemple #9
0
/* Determine if this name only yields one query.  If it does, set *s to
 * the string we should query, in an allocated buffer.  If not, set *s
 * to NULL.
 */
STATIC_TESTABLE int single_domain(ares_channel channel, const char *name, char **s)
{
  size_t len = strlen(name);
  const char *hostaliases;
  FILE *fp;
  char *line = NULL;
  int status;
  size_t linesize;
  const char *p, *q;
  int error;

  /* If the name contains a trailing dot, then the single query is the name
   * sans the trailing dot.
   */
  if ((len > 0) && (name[len - 1] == '.'))
    {
      *s = ares_strdup(name);
      return (*s) ? ARES_SUCCESS : ARES_ENOMEM;
    }

  if (!(channel->flags & ARES_FLAG_NOALIASES) && !strchr(name, '.'))
    {
      /* The name might be a host alias. */
      hostaliases = getenv("HOSTALIASES");
      if (hostaliases)
        {
          fp = fopen(hostaliases, "r");
          if (fp)
            {
              while ((status = ares__read_line(fp, &line, &linesize))
                     == ARES_SUCCESS)
                {
                  if (strncasecmp(line, name, len) != 0 ||
                      !ISSPACE(line[len]))
                    continue;
                  p = line + len;
                  while (ISSPACE(*p))
                    p++;
                  if (*p)
                    {
                      q = p + 1;
                      while (*q && !ISSPACE(*q))
                        q++;
                      *s = ares_malloc(q - p + 1);
                      if (*s)
                        {
                          memcpy(*s, p, q - p);
                          (*s)[q - p] = 0;
                        }
                      ares_free(line);
                      fclose(fp);
                      return (*s) ? ARES_SUCCESS : ARES_ENOMEM;
                    }
                }
              ares_free(line);
              fclose(fp);
              if (status != ARES_SUCCESS && status != ARES_EOF)
                return status;
            }
          else
            {
              error = ERRNO;
              switch(error)
                {
                case ENOENT:
                case ESRCH:
                  break;
                default:
                  DEBUGF(fprintf(stderr, "fopen() failed with error: %d %s\n",
                                 error, strerror(error)));
                  DEBUGF(fprintf(stderr, "Error opening file: %s\n",
                                 hostaliases));
                  *s = NULL;
                  return ARES_EFILE;
                }
            }
        }
    }

  if (channel->flags & ARES_FLAG_NOSEARCH || channel->ndomains == 0)
    {
      /* No domain search to do; just try the name as-is. */
      *s = ares_strdup(name);
      return (*s) ? ARES_SUCCESS : ARES_ENOMEM;
    }

  *s = NULL;
  return ARES_SUCCESS;
}
Exemple #10
0
static void search_callback(void *arg, int status, int timeouts,
                            unsigned char *abuf, int alen)
{
  struct search_query *squery = (struct search_query *) arg;
  ares_channel channel = squery->channel;
  char *s;

  squery->timeouts += timeouts;

  /* Stop searching unless we got a non-fatal error. */
  if (status != ARES_ENODATA && status != ARES_ESERVFAIL
      && status != ARES_ENOTFOUND)
    end_squery(squery, status, abuf, alen);
  else
    {
      /* Save the status if we were trying as-is. */
      if (squery->trying_as_is)
        squery->status_as_is = status;

      /*
       * If we ever get ARES_ENODATA along the way, record that; if the search
       * should run to the very end and we got at least one ARES_ENODATA,
       * then callers like ares_gethostbyname() may want to try a T_A search
       * even if the last domain we queried for T_AAAA resource records
       * returned ARES_ENOTFOUND.
       */
      if (status == ARES_ENODATA)
        squery->ever_got_nodata = 1;

      if (squery->next_domain < channel->ndomains)
        {
          /* Try the next domain. */
          status = cat_domain(squery->name,
                              channel->domains[squery->next_domain], &s);
          if (status != ARES_SUCCESS)
            end_squery(squery, status, NULL, 0);
          else
            {
              squery->trying_as_is = 0;
              squery->next_domain++;
              ares_query(channel, s, squery->dnsclass, squery->type,
                         search_callback, squery);
              ares_free(s);
            }
        }
      else if (squery->status_as_is == -1)
        {
          /* Try the name as-is at the end. */
          squery->trying_as_is = 1;
          ares_query(channel, squery->name, squery->dnsclass, squery->type,
                     search_callback, squery);
        }
      else {
        if (squery->status_as_is == ARES_ENOTFOUND && squery->ever_got_nodata) {
          end_squery(squery, ARES_ENODATA, NULL, 0);
        }
        else
          end_squery(squery, squery->status_as_is, NULL, 0);
      }
    }
}
Exemple #11
0
int
ares_parse_mx_reply (const unsigned char *abuf, int alen,
                     struct ares_mx_reply **mx_out)
{
  unsigned int qdcount, ancount, i;
  const unsigned char *aptr, *vptr;
  int status, rr_type, rr_class, rr_len;
  long len;
  char *hostname = NULL, *rr_name = NULL;
  struct ares_mx_reply *mx_head = NULL;
  struct ares_mx_reply *mx_last = NULL;
  struct ares_mx_reply *mx_curr;

  /* Set *mx_out to NULL for all failure cases. */
  *mx_out = NULL;

  /* Give up if abuf doesn't have room for a header. */
  if (alen < HFIXEDSZ)
    return ARES_EBADRESP;

  /* Fetch the question and answer count from the header. */
  qdcount = DNS_HEADER_QDCOUNT (abuf);
  ancount = DNS_HEADER_ANCOUNT (abuf);
  if (qdcount != 1)
    return ARES_EBADRESP;
  if (ancount == 0)
    return ARES_ENODATA;

  /* Expand the name from the question, and skip past the question. */
  aptr = abuf + HFIXEDSZ;
  status = ares_expand_name (aptr, abuf, alen, &hostname, &len);
  if (status != ARES_SUCCESS)
    return status;

  if (aptr + len + QFIXEDSZ > abuf + alen)
    {
      ares_free (hostname);
      return ARES_EBADRESP;
    }
  aptr += len + QFIXEDSZ;

  /* Examine each answer resource record (RR) in turn. */
  for (i = 0; i < ancount; i++)
    {
      /* Decode the RR up to the data field. */
      status = ares_expand_name (aptr, abuf, alen, &rr_name, &len);
      if (status != ARES_SUCCESS)
        {
          break;
        }
      aptr += len;
      if (aptr + RRFIXEDSZ > abuf + alen)
        {
          status = ARES_EBADRESP;
          break;
        }
      rr_type = DNS_RR_TYPE (aptr);
      rr_class = DNS_RR_CLASS (aptr);
      rr_len = DNS_RR_LEN (aptr);
      aptr += RRFIXEDSZ;
      if (aptr + rr_len > abuf + alen)
        {
          status = ARES_EBADRESP;
          break;
        }

      /* Check if we are really looking at a MX record */
      if (rr_class == C_IN && rr_type == T_MX)
        {
          /* parse the MX record itself */
          if (rr_len < 2)
            {
              status = ARES_EBADRESP;
              break;
            }

          /* Allocate storage for this MX answer appending it to the list */
          mx_curr = ares_malloc_data(ARES_DATATYPE_MX_REPLY);
          if (!mx_curr)
            {
              status = ARES_ENOMEM;
              break;
            }
          if (mx_last)
            {
              mx_last->next = mx_curr;
            }
          else
            {
              mx_head = mx_curr;
            }
          mx_last = mx_curr;

          vptr = aptr;
          mx_curr->priority = DNS__16BIT(vptr);
          vptr += sizeof(unsigned short);

          status = ares_expand_name (vptr, abuf, alen, &mx_curr->host, &len);
          if (status != ARES_SUCCESS)
            break;
        }

      /* Don't lose memory in the next iteration */
      ares_free (rr_name);
      rr_name = NULL;

      /* Move on to the next record */
      aptr += rr_len;
    }

  if (hostname)
    ares_free (hostname);
  if (rr_name)
    ares_free (rr_name);

  /* clean up on error */
  if (status != ARES_SUCCESS)
    {
      if (mx_head)
        ares_free_data (mx_head);
      return status;
    }

  /* everything looks fine, return the data */
  *mx_out = mx_head;

  return ARES_SUCCESS;
}
Exemple #12
0
/* IPv6 addresses with ports require square brackets [fe80::1%lo0]:53 */
static int set_servers_csv(ares_channel channel,
                           const char* _csv, int use_port)
{
  size_t i;
  char* csv = NULL;
  char* ptr;
  char* start_host;
  int cc = 0;
  int rv = ARES_SUCCESS;
  struct ares_addr_port_node *servers = NULL;
  struct ares_addr_port_node *last = NULL;

  if (ares_library_initialized() != ARES_SUCCESS)
    return ARES_ENOTINITIALIZED;  /* LCOV_EXCL_LINE: n/a on non-WinSock */

  if (!channel)
    return ARES_ENODATA;

  ares__destroy_servers_state(channel);

  i = strlen(_csv);
  if (i == 0)
     return ARES_SUCCESS; /* blank all servers */

  csv = ares_malloc(i + 2);
  if (!csv)
    return ARES_ENOMEM;

  strcpy(csv, _csv);
  if (csv[i-1] != ',') { /* make parsing easier by ensuring ending ',' */
    csv[i] = ',';
    csv[i+1] = 0;
  }

  start_host = csv;
  for (ptr = csv; *ptr; ptr++) {
    if (*ptr == ':') {
      /* count colons to determine if we have an IPv6 number or IPv4 with
         port */
      cc++;
    }
    else if (*ptr == '[') {
      /* move start_host if an open square bracket is found wrapping an IPv6
         address */
      start_host = ptr + 1;
    }
    else if (*ptr == ',') {
      char* pp = ptr - 1;
      char* p = ptr;
      int port = 0;
      struct in_addr in4;
      struct ares_in6_addr in6;
      struct ares_addr_port_node *s = NULL;

      *ptr = 0; /* null terminate host:port string */
      /* Got an entry..see if the port was specified. */
      if (cc > 0) {
        while (pp > start_host) {
          /* a single close square bracket followed by a colon, ']:' indicates
             an IPv6 address with port */
          if ((*pp == ']') && (*p == ':'))
            break; /* found port */
          /* a single colon, ':' indicates an IPv4 address with port */
          if ((*pp == ':') && (cc == 1))
            break; /* found port */
          if (!(ISDIGIT(*pp) || (*pp == ':'))) {
            /* Found end of digits before we found :, so wasn't a port */
            /* must allow ':' for IPv6 case of ']:' indicates we found a port */
            pp = p = ptr;
            break;
          }
          pp--;
          p--;
        }
        if ((pp != start_host) && ((pp + 1) < ptr)) {
          /* Found it. Parse over the port number */
          /* when an IPv6 address is wrapped with square brackets the port
             starts at pp + 2 */
          if (*pp == ']')
            p++; /* move p before ':' */
          /* p will point to the start of the port */
          port = (int)strtol(p, NULL, 10);
          *pp = 0; /* null terminate host */
        }
      }
      /* resolve host, try ipv4 first, rslt is in network byte order */
      rv = ares_inet_pton(AF_INET, start_host, &in4);
      if (!rv) {
        /* Ok, try IPv6 then */
        rv = ares_inet_pton(AF_INET6, start_host, &in6);
        if (!rv) {
          rv = ARES_EBADSTR;
          goto out;
        }
        /* was ipv6, add new server */
        s = ares_malloc(sizeof(*s));
        if (!s) {
          rv = ARES_ENOMEM;
          goto out;
        }
        s->family = AF_INET6;
        memcpy(&s->addr, &in6, sizeof(struct ares_in6_addr));
      }
      else {
        /* was ipv4, add new server */
        s = ares_malloc(sizeof(*s));
        if (!s) {
          rv = ARES_ENOMEM;
          goto out;
        }
        s->family = AF_INET;
        memcpy(&s->addr, &in4, sizeof(struct in_addr));
      }
      if (s) {
        s->udp_port = use_port ? port: 0;
        s->tcp_port = s->udp_port;
        s->next = NULL;
        if (last) {
          last->next = s;
          /* need to move last to maintain the linked list */
          last = last->next;
        }
        else {
          servers = s;
          last = s;
        }
      }

      /* Set up for next one */
      start_host = ptr + 1;
      cc = 0;
    }
  }

  rv = ares_set_servers_ports(channel, servers);

  out:
  if (csv)
    ares_free(csv);
  while (servers) {
    struct ares_addr_port_node *s = servers;
    servers = servers->next;
    ares_free(s);
  }

  return rv;
}
Exemple #13
0
/* If the name looks like an IP address, fake up a host entry, end the
 * query immediately, and return true.  Otherwise return false.
 */
static int fake_hostent(const char *name, int family,
                        ares_host_callback callback, void *arg)
{
  struct hostent hostent;
  char *aliases[1] = { NULL };
  char *addrs[2];
  int result = 0;
  struct in_addr in;
  struct ares_in6_addr in6;

  if (family == AF_INET || family == AF_INET6)
    {
      /* It only looks like an IP address if it's all numbers and dots. */
      int numdots = 0, valid = 1;
      const char *p;
      for (p = name; *p; p++)
        {
          if (!ISDIGIT(*p) && *p != '.') {
            valid = 0;
            break;
          } else if (*p == '.') {
            numdots++;
          }
        }

      /* if we don't have 3 dots, it is illegal
       * (although inet_addr doesn't think so).
       */
      if (numdots != 3 || !valid)
        result = 0;
      else
        result = ((in.s_addr = inet_addr(name)) == INADDR_NONE ? 0 : 1);

      if (result)
        family = AF_INET;
    }
  if (family == AF_INET6)
    result = (ares_inet_pton(AF_INET6, name, &in6) < 1 ? 0 : 1);

  if (!result)
    return 0;

  if (family == AF_INET)
    {
      hostent.h_length = (int)sizeof(struct in_addr);
      addrs[0] = (char *)&in;
    }
  else if (family == AF_INET6)
    {
      hostent.h_length = (int)sizeof(struct ares_in6_addr);
      addrs[0] = (char *)&in6;
    }
  /* Duplicate the name, to avoid a constness violation. */
  hostent.h_name = ares_strdup(name);
  if (!hostent.h_name)
    {
      callback(arg, ARES_ENOMEM, 0, NULL);
      return 1;
    }

  /* Fill in the rest of the host structure and terminate the query. */
  addrs[1] = NULL;
  hostent.h_aliases = aliases;
  hostent.h_addrtype = aresx_sitoss(family);
  hostent.h_addr_list = addrs;
  callback(arg, ARES_SUCCESS, 0, &hostent);

  ares_free((char *)(hostent.h_name));
  return 1;
}
Exemple #14
0
void ares_search(ares_channel channel, const char *name, int dnsclass,
                 int type, ares_callback callback, void *arg)
{
  struct search_query *squery;
  char *s;
  const char *p;
  int status, ndots;

  /* If name only yields one domain to search, then we don't have
   * to keep extra state, so just do an ares_query().
   */
  status = single_domain(channel, name, &s);
  if (status != ARES_SUCCESS)
    {
      callback(arg, status, 0, NULL, 0);
      return;
    }
  if (s)
    {
      ares_query(channel, s, dnsclass, type, callback, arg);
      ares_free(s);
      return;
    }

  /* Allocate a search_query structure to hold the state necessary for
   * doing multiple lookups.
   */
  squery = ares_malloc(sizeof(struct search_query));
  if (!squery)
    {
      callback(arg, ARES_ENOMEM, 0, NULL, 0);
      return;
    }
  squery->channel = channel;
  squery->name = ares_strdup(name);
  if (!squery->name)
    {
      ares_free(squery);
      callback(arg, ARES_ENOMEM, 0, NULL, 0);
      return;
    }
  squery->dnsclass = dnsclass;
  squery->type = type;
  squery->status_as_is = -1;
  squery->callback = callback;
  squery->arg = arg;
  squery->timeouts = 0;
  squery->ever_got_nodata = 0;

  /* Count the number of dots in name. */
  ndots = 0;
  for (p = name; *p; p++)
    {
      if (*p == '.')
        ndots++;
    }

  /* If ndots is at least the channel ndots threshold (usually 1),
   * then we try the name as-is first.  Otherwise, we try the name
   * as-is last.
   */
  if (ndots >= channel->ndots)
    {
      /* Try the name as-is first. */
      squery->next_domain = 0;
      squery->trying_as_is = 1;
      ares_query(channel, name, dnsclass, type, search_callback, squery);
    }
  else
    {
      /* Try the name as-is last; start with the first search domain. */
      squery->next_domain = 1;
      squery->trying_as_is = 0;
      status = cat_domain(name, channel->domains[0], &s);
      if (status == ARES_SUCCESS)
        {
          ares_query(channel, s, dnsclass, type, search_callback, squery);
          ares_free(s);
        }
      else
      {
        /* failed, free the malloc()ed memory */
        ares_free(squery->name);
        ares_free(squery);
        callback(arg, status, 0, NULL, 0);
      }
    }
}
Exemple #15
0
/* If any TCP sockets select true for writing, write out queued data
 * we have for them.
 */
static void write_tcp_data(ares_channel channel,
                           fd_set *write_fds,
                           ares_socket_t write_fd,
                           struct timeval *now)
{
  struct server_state *server;
  struct send_request *sendreq;
  struct iovec *vec;
  int i;
  ares_ssize_t scount;
  ares_ssize_t wcount;
  size_t n;

  if(!write_fds && (write_fd == ARES_SOCKET_BAD))
    /* no possible action */
    return;

  for (i = 0; i < channel->nservers; i++)
    {
      /* Make sure server has data to send and is selected in write_fds or
         write_fd. */
      server = &channel->servers[i];
      if (!server->qhead || server->tcp_socket == ARES_SOCKET_BAD ||
          server->is_broken)
        continue;

      if(write_fds) {
        if(!(FD_ISSET(server->tcp_socket, write_fds)))
          continue;
      }
      else {
        if(server->tcp_socket != write_fd)
          continue;
      }

      if(write_fds)
        /* If there's an error and we close this socket, then open
         * another with the same fd to talk to another server, then we
         * don't want to think that it was the new socket that was
         * ready. This is not disastrous, but is likely to result in
         * extra system calls and confusion. */
        FD_CLR(server->tcp_socket, write_fds);

      /* Count the number of send queue items. */
      n = 0;
      for (sendreq = server->qhead; sendreq; sendreq = sendreq->next)
        n++;

      /* Allocate iovecs so we can send all our data at once. */
      vec = ares_malloc(n * sizeof(struct iovec));
      if (vec)
        {
          /* Fill in the iovecs and send. */
          n = 0;
          for (sendreq = server->qhead; sendreq; sendreq = sendreq->next)
            {
              vec[n].iov_base = (char *) sendreq->data;
              vec[n].iov_len = sendreq->len;
              n++;
            }
          wcount = socket_writev(channel, server->tcp_socket, vec, (int)n);
          ares_free(vec);
          if (wcount < 0)
            {
              if (!try_again(SOCKERRNO))
                handle_error(channel, i, now);
              continue;
            }

          /* Advance the send queue by as many bytes as we sent. */
          advance_tcp_send_queue(channel, i, wcount);
        }
      else
        {
          /* Can't allocate iovecs; just send the first request. */
          sendreq = server->qhead;

          scount = socket_write(channel, server->tcp_socket, sendreq->data, sendreq->len);
          if (scount < 0)
            {
              if (!try_again(SOCKERRNO))
                handle_error(channel, i, now);
              continue;
            }

          /* Advance the send queue by as many bytes as we sent. */
          advance_tcp_send_queue(channel, i, scount);
        }
    }
}
Exemple #16
0
int
ares_parse_soa_reply(const unsigned char *abuf, int alen,
		     struct ares_soa_reply **soa_out)
{
  const unsigned char *aptr;
  long len;
  char *qname = NULL, *rr_name = NULL;
  struct ares_soa_reply *soa = NULL;
  int qdcount, ancount;
  int status;

  if (alen < HFIXEDSZ)
    return ARES_EBADRESP;

  /* parse message header */
  qdcount = DNS_HEADER_QDCOUNT(abuf);
  ancount = DNS_HEADER_ANCOUNT(abuf);
  if (qdcount != 1 || ancount != 1)
    return ARES_EBADRESP;
  aptr = abuf + HFIXEDSZ;

  /* query name */
  status = ares__expand_name_for_response(aptr, abuf, alen, &qname, &len);
  if (status != ARES_SUCCESS)
    goto failed_stat;
  aptr += len;

  /* skip qtype & qclass */
  if (aptr + QFIXEDSZ > abuf + alen)
    goto failed;
  aptr += QFIXEDSZ;

  /* rr_name */
  status = ares__expand_name_for_response(aptr, abuf, alen, &rr_name, &len);
  if (status != ARES_SUCCESS)
    goto failed_stat;
  aptr += len;

  /* allocate result struct */
  soa = ares_malloc_data(ARES_DATATYPE_SOA_REPLY);
  if (!soa)
    {
      status = ARES_ENOMEM;
      goto failed_stat;
    }

  /* skip rr_type, rr_class, rr_ttl, rr_rdlen */
  if (aptr + RRFIXEDSZ > abuf + alen)
    goto failed;
  soa->ttl = DNS_RR_TTL(aptr);
  aptr += RRFIXEDSZ;

  /* nsname */
  status = ares__expand_name_for_response(aptr, abuf, alen, &soa->nsname, &len);
  if (status != ARES_SUCCESS)
    goto failed_stat;
  aptr += len;

  /* hostmaster */
  status = ares__expand_name_for_response(aptr, abuf, alen, &soa->hostmaster, &len);
  if (status != ARES_SUCCESS)
    goto failed_stat;
  aptr += len;

  /* integer fields */
  if (aptr + 5 * 4 > abuf + alen)
    goto failed;
  soa->serial = DNS__32BIT(aptr + 0 * 4);
  soa->refresh = DNS__32BIT(aptr + 1 * 4);
  soa->retry = DNS__32BIT(aptr + 2 * 4);
  soa->expire = DNS__32BIT(aptr + 3 * 4);
  soa->minttl = DNS__32BIT(aptr + 4 * 4);

  ares_free(qname);
  ares_free(rr_name);

  *soa_out = soa;

  return ARES_SUCCESS;

failed:
  status = ARES_EBADRESP;

failed_stat:
  ares_free_data(soa);
  if (qname)
    ares_free(qname);
  if (rr_name)
    ares_free(rr_name);
  return status;
}
Exemple #17
0
/* If any TCP socket selects true for reading, read some data,
 * allocate a buffer if we finish reading the length word, and process
 * a packet if we finish reading one.
 */
static void read_tcp_data(ares_channel channel, fd_set *read_fds,
                          ares_socket_t read_fd, struct timeval *now)
{
  struct server_state *server;
  int i;
  ares_ssize_t count;

  if(!read_fds && (read_fd == ARES_SOCKET_BAD))
    /* no possible action */
    return;

  for (i = 0; i < channel->nservers; i++)
    {
      /* Make sure the server has a socket and is selected in read_fds. */
      server = &channel->servers[i];
      if (server->tcp_socket == ARES_SOCKET_BAD || server->is_broken)
        continue;

      if(read_fds) {
        if(!(FD_ISSET(server->tcp_socket, read_fds)))
          continue;
      }
      else {
        if(server->tcp_socket != read_fd)
          continue;
      }

      if(read_fds)
        /* If there's an error and we close this socket, then open another
         * with the same fd to talk to another server, then we don't want to
         * think that it was the new socket that was ready. This is not
         * disastrous, but is likely to result in extra system calls and
         * confusion. */
        FD_CLR(server->tcp_socket, read_fds);

      if (server->tcp_lenbuf_pos != 2)
        {
          /* We haven't yet read a length word, so read that (or
           * what's left to read of it).
           */
          count = socket_recv(channel, server->tcp_socket,
			      server->tcp_lenbuf + server->tcp_lenbuf_pos,
			      2 - server->tcp_lenbuf_pos);
          if (count <= 0)
            {
              if (!(count == -1 && try_again(SOCKERRNO)))
                handle_error(channel, i, now);
              continue;
            }

          server->tcp_lenbuf_pos += (int)count;
          if (server->tcp_lenbuf_pos == 2)
            {
              /* We finished reading the length word.  Decode the
               * length and allocate a buffer for the data.
               */
              server->tcp_length = server->tcp_lenbuf[0] << 8
                | server->tcp_lenbuf[1];
              server->tcp_buffer = ares_malloc(server->tcp_length);
              if (!server->tcp_buffer) {
                handle_error(channel, i, now);
                return; /* bail out on malloc failure. TODO: make this
                           function return error codes */
              }
              server->tcp_buffer_pos = 0;
            }
        }
      else
        {
          /* Read data into the allocated buffer. */
          count = socket_recv(channel, server->tcp_socket,
			      server->tcp_buffer + server->tcp_buffer_pos,
			      server->tcp_length - server->tcp_buffer_pos);
          if (count <= 0)
            {
              if (!(count == -1 && try_again(SOCKERRNO)))
                handle_error(channel, i, now);
              continue;
            }

          server->tcp_buffer_pos += (int)count;
          if (server->tcp_buffer_pos == server->tcp_length)
            {
              /* We finished reading this answer; process it and
               * prepare to read another length word.
               */
              process_answer(channel, server->tcp_buffer, server->tcp_length,
                             i, 1, now);
              ares_free(server->tcp_buffer);
              server->tcp_buffer = NULL;
              server->tcp_lenbuf_pos = 0;
              server->tcp_buffer_pos = 0;
            }
        }
    }
}
int ares_parse_ptr_reply(const unsigned char *abuf, int alen, const void *addr,
                         int addrlen, int family, struct hostent **host)
{
  unsigned int qdcount, ancount;
  int status, i, rr_type, rr_class, rr_len;
  long len;
  const unsigned char *aptr;
  char *ptrname, *hostname, *rr_name, *rr_data;
  struct hostent *hostent;
  int aliascnt = 0;
  int alias_alloc = 8;
  char ** aliases;

  /* Set *host to NULL for all failure cases. */
  *host = NULL;

  /* Give up if abuf doesn't have room for a header. */
  if (alen < HFIXEDSZ)
    return ARES_EBADRESP;

  /* Fetch the question and answer count from the header. */
  qdcount = DNS_HEADER_QDCOUNT(abuf);
  ancount = DNS_HEADER_ANCOUNT(abuf);
  if (qdcount != 1)
    return ARES_EBADRESP;

  /* Expand the name from the question, and skip past the question. */
  aptr = abuf + HFIXEDSZ;
  status = ares__expand_name_for_response(aptr, abuf, alen, &ptrname, &len);
  if (status != ARES_SUCCESS)
    return status;
  if (aptr + len + QFIXEDSZ > abuf + alen)
    {
      ares_free(ptrname);
      return ARES_EBADRESP;
    }
  aptr += len + QFIXEDSZ;

  /* Examine each answer resource record (RR) in turn. */
  hostname = NULL;
  aliases = ares_malloc(alias_alloc * sizeof(char *));
  if (!aliases)
    {
      ares_free(ptrname);
      return ARES_ENOMEM;
    }
  for (i = 0; i < (int)ancount; i++)
    {
      /* Decode the RR up to the data field. */
      status = ares__expand_name_for_response(aptr, abuf, alen, &rr_name, &len);
      if (status != ARES_SUCCESS)
        break;
      aptr += len;
      if (aptr + RRFIXEDSZ > abuf + alen)
        {
          ares_free(rr_name);
          status = ARES_EBADRESP;
          break;
        }
      rr_type = DNS_RR_TYPE(aptr);
      rr_class = DNS_RR_CLASS(aptr);
      rr_len = DNS_RR_LEN(aptr);
      aptr += RRFIXEDSZ;
      if (aptr + rr_len > abuf + alen)
        {
          ares_free(rr_name);
          status = ARES_EBADRESP;
          break;
        }

      if (rr_class == C_IN && rr_type == T_PTR
          && strcasecmp(rr_name, ptrname) == 0)
        {
          /* Decode the RR data and set hostname to it. */
          status = ares__expand_name_for_response(aptr, abuf, alen, &rr_data,
                                                  &len);
          if (status != ARES_SUCCESS)
            {
              ares_free(rr_name);
              break;
            }
          if (hostname)
            ares_free(hostname);
          hostname = rr_data;
          aliases[aliascnt] = ares_malloc((strlen(rr_data)+1) * sizeof(char));
          if (!aliases[aliascnt])
            {
              ares_free(rr_name);
              status = ARES_ENOMEM;
              break;
            }
          strncpy(aliases[aliascnt], rr_data, strlen(rr_data)+1);
          aliascnt++;
          if (aliascnt >= alias_alloc) {
            char **ptr;
            alias_alloc *= 2;
            ptr = ares_realloc(aliases, alias_alloc * sizeof(char *));
            if(!ptr) {
              ares_free(rr_name);
              status = ARES_ENOMEM;
              break;
            }
            aliases = ptr;
          }
        }

      if (rr_class == C_IN && rr_type == T_CNAME)
        {
          /* Decode the RR data and replace ptrname with it. */
          status = ares__expand_name_for_response(aptr, abuf, alen, &rr_data,
                                                  &len);
          if (status != ARES_SUCCESS)
            {
              ares_free(rr_name);
              break;
            }
          ares_free(ptrname);
          ptrname = rr_data;
        }

      ares_free(rr_name);
      aptr += rr_len;
      if (aptr > abuf + alen)
        {  /* LCOV_EXCL_START: already checked above */
          status = ARES_EBADRESP;
          break;
        }  /* LCOV_EXCL_STOP */
    }

  if (status == ARES_SUCCESS && !hostname)
    status = ARES_ENODATA;
  if (status == ARES_SUCCESS)
    {
      /* We got our answer.  Allocate memory to build the host entry. */
      hostent = ares_malloc(sizeof(struct hostent));
      if (hostent)
        {
          hostent->h_addr_list = ares_malloc(2 * sizeof(char *));
          if (hostent->h_addr_list)
            {
              hostent->h_addr_list[0] = ares_malloc(addrlen);
              if (hostent->h_addr_list[0])
                {
                  hostent->h_aliases = ares_malloc((aliascnt+1) * sizeof (char *));
                  if (hostent->h_aliases)
                    {
                      /* Fill in the hostent and return successfully. */
                      hostent->h_name = hostname;
                      for (i=0 ; i<aliascnt ; i++)
                        hostent->h_aliases[i] = aliases[i];
                      hostent->h_aliases[aliascnt] = NULL;
                      hostent->h_addrtype = aresx_sitoss(family);
                      hostent->h_length = aresx_sitoss(addrlen);
                      memcpy(hostent->h_addr_list[0], addr, addrlen);
                      hostent->h_addr_list[1] = NULL;
                      *host = hostent;
                      ares_free(aliases);
                      ares_free(ptrname);
                      return ARES_SUCCESS;
                    }
                  ares_free(hostent->h_addr_list[0]);
                }
              ares_free(hostent->h_addr_list);
            }
          ares_free(hostent);
        }
      status = ARES_ENOMEM;
    }
  for (i=0 ; i<aliascnt ; i++)
    if (aliases[i])
      ares_free(aliases[i]);
  ares_free(aliases);
  if (hostname)
    ares_free(hostname);
  ares_free(ptrname);
  return status;
}
int ares__get_hostent(FILE *fp, int family, struct hostent **host)
{
  char *line = NULL, *p, *q, **alias;
  char *txtaddr, *txthost, *txtalias;
  int status;
  size_t addrlen, linesize, naliases;
  struct ares_addr addr;
  struct hostent *hostent = NULL;

  *host = NULL; /* Assume failure */

  /* Validate family */
  switch (family) {
    case AF_INET:
    case AF_INET6:
    case AF_UNSPEC:
      break;
    default:
      return ARES_EBADFAMILY;
  }

  while ((status = ares__read_line(fp, &line, &linesize)) == ARES_SUCCESS)
    {

      /* Trim line comment. */
      p = line;
      while (*p && (*p != '#'))
        p++;
      *p = '\0';

      /* Trim trailing whitespace. */
      q = p - 1;
      while ((q >= line) && ISSPACE(*q))
        q--;
      *++q = '\0';

      /* Skip leading whitespace. */
      p = line;
      while (*p && ISSPACE(*p))
        p++;
      if (!*p)
        /* Ignore line if empty. */
        continue;

      /* Pointer to start of IPv4 or IPv6 address part. */
      txtaddr = p;

      /* Advance past address part. */
      while (*p && !ISSPACE(*p))
        p++;
      if (!*p)
        /* Ignore line if reached end of line. */
        continue;

      /* Null terminate address part. */
      *p = '\0';

      /* Advance to host name */
      p++;
      while (*p && ISSPACE(*p))
        p++;
      if (!*p)
        /* Ignore line if reached end of line. */
        continue;  /* LCOV_EXCL_LINE: trailing whitespace already stripped */

      /* Pointer to start of host name. */
      txthost = p;

      /* Advance past host name. */
      while (*p && !ISSPACE(*p))
        p++;

      /* Pointer to start of first alias. */
      txtalias = NULL;
      if (*p)
        {
          q = p + 1;
          while (*q && ISSPACE(*q))
            q++;
          if (*q)
            txtalias = q;
        }

      /* Null terminate host name. */
      *p = '\0';

      /* find out number of aliases. */
      naliases = 0;
      if (txtalias)
        {
          p = txtalias;
          while (*p)
            {
              while (*p && !ISSPACE(*p))
                p++;
              while (*p && ISSPACE(*p))
                p++;
              naliases++;
            }
        }

      /* Convert address string to network address for the requested family. */
      addrlen = 0;
      addr.family = AF_UNSPEC;
      addr.addrV4.s_addr = INADDR_NONE;
      if ((family == AF_INET) || (family == AF_UNSPEC))
        {
          addr.addrV4.s_addr = inet_addr(txtaddr);
          if (addr.addrV4.s_addr != INADDR_NONE)
            {
              /* Actual network address family and length. */
              addr.family = AF_INET;
              addrlen = sizeof(addr.addrV4);
            }
        }
      if ((family == AF_INET6) || ((family == AF_UNSPEC) && (!addrlen)))
        {
          if (ares_inet_pton(AF_INET6, txtaddr, &addr.addrV6) > 0)
            {
              /* Actual network address family and length. */
              addr.family = AF_INET6;
              addrlen = sizeof(addr.addrV6);
            }
        }
      if (!addrlen)
        /* Ignore line if invalid address string for the requested family. */
        continue;

      /*
      ** Actual address family possible values are AF_INET and AF_INET6 only.
      */

      /* Allocate memory for the hostent structure. */
      hostent = ares_malloc(sizeof(struct hostent));
      if (!hostent)
        break;

      /* Initialize fields for out of memory condition. */
      hostent->h_aliases = NULL;
      hostent->h_addr_list = NULL;

      /* Copy official host name. */
      hostent->h_name = ares_strdup(txthost);
      if (!hostent->h_name)
        break;

      /* Copy network address. */
      hostent->h_addr_list = ares_malloc(2 * sizeof(char *));
      if (!hostent->h_addr_list)
        break;
      hostent->h_addr_list[1] = NULL;
      hostent->h_addr_list[0] = ares_malloc(addrlen);
      if (!hostent->h_addr_list[0])
        break;
      if (addr.family == AF_INET)
        memcpy(hostent->h_addr_list[0], &addr.addrV4, sizeof(addr.addrV4));
      else
        memcpy(hostent->h_addr_list[0], &addr.addrV6, sizeof(addr.addrV6));

      /* Copy aliases. */
      hostent->h_aliases = ares_malloc((naliases + 1) * sizeof(char *));
      if (!hostent->h_aliases)
        break;
      alias = hostent->h_aliases;
      while (naliases)
        *(alias + naliases--) = NULL;
      *alias = NULL;
      while (txtalias)
        {
          p = txtalias;
          while (*p && !ISSPACE(*p))
            p++;
          q = p;
          while (*q && ISSPACE(*q))
            q++;
          *p = '\0';
          if ((*alias = ares_strdup(txtalias)) == NULL)
            break;
          alias++;
          txtalias = *q ? q : NULL;
        }
      if (txtalias)
        /* Alias memory allocation failure. */
        break;

      /* Copy actual network address family and length. */
      hostent->h_addrtype = aresx_sitoss(addr.family);
      hostent->h_length = aresx_uztoss(addrlen);

      /* Free line buffer. */
      ares_free(line);

      /* Return hostent successfully */
      *host = hostent;
      return ARES_SUCCESS;

    }

  /* If allocated, free line buffer. */
  if (line)
    ares_free(line);

  if (status == ARES_SUCCESS)
    {
      /* Memory allocation failure; clean up. */
      if (hostent)
        {
          if (hostent->h_name)
            ares_free((char *) hostent->h_name);
          if (hostent->h_aliases)
            {
              for (alias = hostent->h_aliases; *alias; alias++)
                ares_free(*alias);
              ares_free(hostent->h_aliases);
            }
          if (hostent->h_addr_list)
            {
              if (hostent->h_addr_list[0])
                ares_free(hostent->h_addr_list[0]);
              ares_free(hostent->h_addr_list);
            }
          ares_free(hostent);
        }
      return ARES_ENOMEM;
    }

  return status;
}