Пример #1
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);
}
Пример #2
0
/* Simply decodes a length-encoded character string. The first byte of the
 * input is the length of the string to be returned and the bytes thereafter
 * are the characters of the string. The returned result will be NULL
 * terminated.
 */
int ares_expand_string(const unsigned char *encoded,
                       const unsigned char *abuf,
                       int alen,
                       unsigned char **s,
                       long *enclen)
{
  unsigned char *q;
  union {
    ssize_t sig;
     size_t uns;
  } elen;

  if (encoded == abuf+alen)
    return ARES_EBADSTR;

  elen.uns = *encoded;
  if (encoded+elen.sig+1 > abuf+alen)
    return ARES_EBADSTR;

  encoded++;

  *s = ares_malloc(elen.uns+1);
  if (*s == NULL)
    return ARES_ENOMEM;
  q = *s;
  strncpy((char *)q, (char *)encoded, elen.uns);
  q[elen.uns] = '\0';

  *s = q;

  *enclen = (long)(elen.sig+1);

  return ARES_SUCCESS;
}
Пример #3
0
char *ares_strdup(const char *s1)
{
#ifdef HAVE_STRDUP
  if (ares_malloc == malloc)
    return strdup(s1);
  else
#endif
  {
    size_t sz;
    char * s2;

    if(s1) {
      sz = strlen(s1);
      if(sz < (size_t)-1) {
        sz++;
        if(sz < ((size_t)-1) / sizeof(char)) {
          s2 = ares_malloc(sz * sizeof(char));
          if(s2) {
            memcpy(s2, s1, sz * sizeof(char));
            return s2;
          }
        }
      }
    }
    return (char *)NULL;
  }
}
Пример #4
0
int ares_set_servers_ports(ares_channel channel,
                           struct ares_addr_port_node *servers)
{
  struct ares_addr_port_node *srvr;
  int num_srvrs = 0;
  int i;

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

  if (!channel)
    return ARES_ENODATA;

  if (!ares__is_list_empty(&channel->all_queries))
    return ARES_ENOTIMP;

  ares__destroy_servers_state(channel);

  for (srvr = servers; srvr; srvr = srvr->next)
    {
      num_srvrs++;
    }

  if (num_srvrs > 0)
    {
      /* Allocate storage for servers state */
      channel->servers = ares_malloc(num_srvrs * sizeof(struct server_state));
      if (!channel->servers)
        {
          return ARES_ENOMEM;
        }
      channel->nservers = num_srvrs;
      /* Fill servers state address data */
      for (i = 0, srvr = servers; srvr; i++, srvr = srvr->next)
        {
          channel->servers[i].addr.family = srvr->family;
          channel->servers[i].addr.udp_port = htons((unsigned short)srvr->udp_port);
          channel->servers[i].addr.tcp_port = htons((unsigned short)srvr->tcp_port);
          if (srvr->family == AF_INET)
            memcpy(&channel->servers[i].addr.addrV4, &srvr->addrV4,
                   sizeof(srvr->addrV4));
          else
            memcpy(&channel->servers[i].addr.addrV6, &srvr->addrV6,
                   sizeof(srvr->addrV6));
        }
      /* Initialize servers state remaining data */
      ares__init_servers_state(channel);
    }

  return ARES_SUCCESS;
}
Пример #5
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);
}
Пример #6
0
/* Concatenate two domains. */
static int cat_domain(const char *name, const char *domain, char **s)
{
  size_t nlen = strlen(name);
  size_t dlen = strlen(domain);

  *s = ares_malloc(nlen + 1 + dlen + 1);
  if (!*s)
    return ARES_ENOMEM;
  memcpy(*s, name, nlen);
  (*s)[nlen] = '.';
  memcpy(*s + nlen + 1, domain, dlen);
  (*s)[nlen + 1 + dlen] = 0;
  return ARES_SUCCESS;
}
Пример #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 */);
}
Пример #8
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;
}
Пример #9
0
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;
}
Пример #10
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;
}
Пример #11
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;
            }
        }
    }
}
Пример #12
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);
        }
    }
}
Пример #13
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);
      }
    }
}
Пример #14
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;
}