Esempio n. 1
0
int ares_create_query(const char *name, int dnsclass, int type,
                      unsigned short id, int rd, unsigned char **buf,
                      int *buflen, int max_udp_size)
{
  int len;
  unsigned char *q;
  const char *p;

  /* Set our results early, in case we bail out early with an error. */
  *buflen = 0;
  *buf = NULL;

  /* Compute the length of the encoded name so we can check buflen.
   * Start counting at 1 for the zero-length label at the end. */
  len = 1;
  for (p = name; *p; p++)
    {
      if (*p == '\\' && *(p + 1) != 0)
        p++;
      len++;
    }
  /* If there are n periods in the name, there are n + 1 labels, and
   * thus n + 1 length fields, unless the name is empty or ends with a
   * period.  So add 1 unless name is empty or ends with a period.
   */
  if (*name && *(p - 1) != '.')
    len++;

  /* Immediately reject names that are longer than the maximum of 255
   * bytes that's specified in RFC 1035 ("To simplify implementations,
   * the total length of a domain name (i.e., label octets and label
   * length octets) is restricted to 255 octets or less."). We aren't
   * doing this just to be a stickler about RFCs. For names that are
   * too long, 'dnscache' closes its TCP connection to us immediately
   * (when using TCP) and ignores the request when using UDP, and
   * BIND's named returns ServFail (TCP or UDP). Sending a request
   * that we know will cause 'dnscache' to close the TCP connection is
   * painful, since that makes any other outstanding requests on that
   * connection fail. And sending a UDP request that we know
   * 'dnscache' will ignore is bad because resources will be tied up
   * until we time-out the request.
   */
  if (len > MAXCDNAME)
    return ARES_EBADNAME;

  *buflen = len + HFIXEDSZ + QFIXEDSZ + (max_udp_size ? EDNSFIXEDSZ : 0);
  *buf = malloc(*buflen);
  if (!*buf)
      return ARES_ENOMEM;

  /* Set up the header. */
  q = *buf;
  memset(q, 0, HFIXEDSZ);
  DNS_HEADER_SET_QID(q, id);
  DNS_HEADER_SET_OPCODE(q, QUERY);
  if (rd) {
    DNS_HEADER_SET_RD(q, 1);
  }
  else {
    DNS_HEADER_SET_RD(q, 0);
  }
  DNS_HEADER_SET_QDCOUNT(q, 1);

  if (max_udp_size) {
      DNS_HEADER_SET_ARCOUNT(q, 1);
  }

  /* A name of "." is a screw case for the loop below, so adjust it. */
  if (strcmp(name, ".") == 0)
    name++;

  /* Start writing out the name after the header. */
  q += HFIXEDSZ;
  while (*name)
    {
      if (*name == '.')
        return ARES_EBADNAME;

      /* Count the number of bytes in this label. */
      len = 0;
      for (p = name; *p && *p != '.'; p++)
        {
          if (*p == '\\' && *(p + 1) != 0)
            p++;
          len++;
        }
      if (len > MAXLABEL)
        return ARES_EBADNAME;

      /* Encode the length and copy the data. */
      *q++ = (unsigned char)len;
      for (p = name; *p && *p != '.'; p++)
        {
          if (*p == '\\' && *(p + 1) != 0)
            p++;
          *q++ = *p;
        }

      /* Go to the next label and repeat, unless we hit the end. */
      if (!*p)
        break;
      name = p + 1;
    }

  /* Add the zero-length label at the end. */
  *q++ = 0;

  /* Finish off the question with the type and class. */
  DNS_QUESTION_SET_TYPE(q, type);
  DNS_QUESTION_SET_CLASS(q, dnsclass);

  if (max_udp_size)
  {
      q += QFIXEDSZ;
      memset(q, 0, EDNSFIXEDSZ);
      q++;
      DNS_RR_SET_TYPE(q, T_OPT);
      DNS_RR_SET_CLASS(q, max_udp_size);
  }

  return ARES_SUCCESS;
}
Esempio n. 2
0
/* Handle an answer from a server. */
static void process_answer(ares_channel channel, unsigned char *abuf,
                           int alen, int whichserver, int tcp,
                           struct timeval *now)
{
  int tc, rcode, packetsz;
  unsigned short id;
  struct query *query;
  struct list_node* list_head;
  struct list_node* list_node;

  /* If there's no room in the answer for a header, we can't do much
   * with it. */
  if (alen < HFIXEDSZ)
    return;

  /* Grab the query ID, truncate bit, and response code from the packet. */
  id = DNS_HEADER_QID(abuf);
  tc = DNS_HEADER_TC(abuf);
  rcode = DNS_HEADER_RCODE(abuf);

  /* Find the query corresponding to this packet. The queries are
   * hashed/bucketed by query id, so this lookup should be quick.  Note that
   * both the query id and the questions must be the same; when the query id
   * wraps around we can have multiple outstanding queries with the same query
   * id, so we need to check both the id and question.
   */
  query = NULL;
  list_head = &(channel->queries_by_qid[id % ARES_QID_TABLE_SIZE]);
  for (list_node = list_head->next; list_node != list_head;
       list_node = list_node->next)
    {
      struct query *q = list_node->data;
      if ((q->qid == id) && same_questions(q->qbuf, q->qlen, abuf, alen))
        {
          query = q;
          break;
        }
    }
  if (!query)
    return;

  packetsz = PACKETSZ;
  /* If we use EDNS and server answers with one of these RCODES, the protocol
   * extension is not understood by the responder. We must retry the query
   * without EDNS enabled.
   */
  if (channel->flags & ARES_FLAG_EDNS)
  {
      packetsz = channel->ednspsz;
      if (rcode == NOTIMP || rcode == FORMERR || rcode == SERVFAIL)
      {
          int qlen = alen - EDNSFIXEDSZ;
          channel->flags ^= ARES_FLAG_EDNS;
          query->tcplen -= EDNSFIXEDSZ;
          query->qlen -= EDNSFIXEDSZ;
          query->tcpbuf[0] = (unsigned char)((qlen >> 8) & 0xff);
          query->tcpbuf[1] = (unsigned char)(qlen & 0xff);
          DNS_HEADER_SET_ARCOUNT(query->tcpbuf + 2, 0);
          query->tcpbuf = realloc(query->tcpbuf, query->tcplen);
          ares__send_query(channel, query, now);
          return;
      }