Example #1
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;
      }
Example #2
0
/* If any TCP sockets select true for writing, write out queued data
 * we have for them.
 */
static void write_tcp_data_core(ares_channel channel, int server_idx,
	time_t now)
{
  struct server_state *server;
  struct send_request *sendreq;
#ifdef WIN32
  WSABUF *vec;
#else
  struct iovec *vec;
#endif
  int n, count;

  server = &channel->servers[server_idx];
  if (!server->qhead || server->tcp_socket == -1 )
    return;

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

#ifdef WIN32
  /* Allocate iovecs so we can send all our data at once. */
  vec = malloc(n * sizeof(WSABUF));
  if (vec)
    {
	    int err;
      /* Fill in the iovecs and send. */
      n = 0;
      for (sendreq = server->qhead; sendreq; sendreq = sendreq->next)
	{
	  vec[n].buf = (char *) sendreq->data;
	  vec[n].len = sendreq->len;
	  n++;
	}
      err = WSASend(server->tcp_socket, vec, n, &count,0,0,0 );
      if ( err == SOCKET_ERROR )
      {
	      count =-1;
      }
      free(vec);
#else
	 /* Allocate iovecs so we can send all our data at once. */
  vec = malloc(n * sizeof(struct iovec));
  if (vec)
    {
		    // int err;
      /* 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++;
	}
      count = writev(server->tcp_socket, vec, n);
      free(vec);
#endif

      if (count < 0)
	{
	  handle_error(channel, server_idx, now);
	  return;
	}

      /* Advance the send queue by as many bytes as we sent. */
      while (count)
	{
	  sendreq = server->qhead;
	  if (count >= sendreq->len)
	    {
	      count -= sendreq->len;
	      server->qhead = sendreq->next;
	      free(sendreq);
	      if (server->qhead == NULL)
	      {
		server->qtail = NULL;
		assert(count==0);
		break;
	      }
	    }
	  else
	    {
	      sendreq->data += count;
	      sendreq->len -= count;
	      break;
	    }
	}
    }
  else
    {
      /* Can't allocate iovecs; just send the first request. */
      sendreq = server->qhead;
#ifndef UNDER_CE
      count = write(server->tcp_socket, sendreq->data, sendreq->len);
#else
      count = send(server->tcp_socket, sendreq->data, sendreq->len,0);
#endif
      if (count < 0)
	{
	  handle_error(channel, server_idx, now);
	  return;
	}

      /* Advance the send queue by as many bytes as we sent. */
      if (count == sendreq->len)
	{
	  server->qhead = sendreq->next;
	  if (server->qhead == NULL)
	    server->qtail = NULL;
	  free(sendreq);
	}
      else
	{
	  sendreq->data += count;
	  sendreq->len -= count;
	}
    }
    if ( server->qhead==NULL && channel->poll_cb_func ) {
        (*(channel->poll_cb_func))( channel->poll_cb_data, channel, server_idx,
	  server->tcp_socket, ARES_POLLACTION_WRITEOFF);
    }
}


static void write_tcp_data(ares_channel channel, fd_set *write_fds, time_t now)
{
  struct server_state *server;
  int i;

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

     write_tcp_data_core(channel, i, now);
  }
}

/* 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, int server_idx, fd_set *read_fds, time_t now)
{
  struct server_state *server;
  int i, count;

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

      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).
	   */
#if defined UNDER_CE || defined WIN32
      count = recv(server->tcp_socket,
                       server->tcp_lenbuf + server->tcp_lenbuf_pos,
                       2 - server->tcp_lenbuf_pos,0);
#else
      count = read(server->tcp_socket,
		       server->tcp_lenbuf + server->tcp_lenbuf_pos,
		       2 - server->tcp_lenbuf_pos);
#endif
	  if (count <= 0)
	    {
	      handle_error(channel, i, now);
	      continue;
	    }

	  server->tcp_lenbuf_pos += 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 = malloc(server->tcp_length);
	      if (!server->tcp_buffer)
		handle_error(channel, i, now);
	      server->tcp_buffer_pos = 0;
	    }
	}
      else
	{
	  /* Read data into the allocated buffer. */
#if defined UNDER_CE || defined WIN32
      count = recv(server->tcp_socket,
		       server->tcp_buffer + server->tcp_buffer_pos,
		       server->tcp_length - server->tcp_buffer_pos,0);
#else
      count = read(server->tcp_socket,
		       server->tcp_buffer + server->tcp_buffer_pos,
		       server->tcp_length - server->tcp_buffer_pos);
#endif

	  if (count <= 0)
	    {
	      handle_error(channel, i, now);
	      continue;
	    }

	  server->tcp_buffer_pos += 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);
	      free(server->tcp_buffer);
	      server->tcp_buffer = NULL;
	      server->tcp_lenbuf_pos = 0;
	    }
	}
    }
}

/* If any UDP sockets select true for reading, process them. */
static void read_udp_packets(ares_channel channel, int server_idx,
			fd_set *read_fds, time_t now)
{
  struct server_state *server;
  int i, count;
  unsigned char buf[PACKETSZ + 1];

  for (i = 0; i < channel->nservers; i++)
    {
      if ( server_idx>=0 && i != server_idx )
          continue;
      /* Make sure the server has a socket and is selected in read_fds. */
      server = &channel->servers[i];
      if ( (server->udp_socket == -1) )
          continue;
      if ( read_fds && !FD_ISSET(server->udp_socket, read_fds) )
	  continue;

	  assert( server->udp_socket != -1 );
	  
      count = recv(server->udp_socket, buf, sizeof(buf), 0);
      if (count <= 0)
	  {
#if defined(WIN32)
		//int err;
		//err = WSAGetLastError();
		//err = errno;
		switch (getErrno())
		{
		    case WSAEWOULDBLOCK:
			    if ( read_fds ) {
			       // read_fds is only null when using epoll
			       // which shouldn't happen under windows
			       // don't know why CLR is here anyways
			       FD_CLR(server->udp_socket, read_fds);
			    }
			    continue;
		    case WSAECONNABORTED:
			    break;
		    case WSAECONNRESET: // got an ICMP error on a previous send
			    break;
		}
#endif
		handle_error(channel, i, now);
	  }
	  else
	  {
		process_answer(channel, buf, count, i, 0, now);
	  }
    }
}

/* If any queries have timed out, note the timeout and move them on. */
static void process_timeouts(ares_channel channel, time_t now)
{
  struct query *query, *next;

  for (query = channel->queries; query; query = next)
    {
      next = query->next;
      if (query->timeout != 0 && now >= query->timeout)
	{
	  //fprintf(stderr, "kennard:ares:process_timeouts: got timeout\n");
	  query->error_status = ARES_ETIMEOUT;
	  next_server(channel, query, now);
	}
    }
}

/* Handle an answer from a server. */
static void process_answer(ares_channel channel, unsigned char *abuf,
			   int alen, int whichserver, int tcp, time_t now)
{
  int id, tc, rcode;
  struct query *query;

  /* 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. */
  for (query = channel->queries; query; query = query->next)
    {
      if (query->qid == id)
	break;
    }
  if (!query)
    return;

  /* If we got a truncated UDP packet and are not ignoring truncation,
   * don't accept the packet, and switch the query to TCP if we hadn't
   * done so already.
   */
  if ((tc || alen > PACKETSZ) && !tcp && !(channel->flags & ARES_FLAG_IGNTC))
    {
      if (!query->using_tcp)
	{
	  query->using_tcp = 1;
	  ares__send_query(channel, query, now);
	}
      return;
    }

  /* Limit alen to PACKETSZ if we aren't using TCP (only relevant if we
   * are ignoring truncation.
   */
  if (alen > PACKETSZ && !tcp)
    alen = PACKETSZ;

  /* If we aren't passing through all error packets, discard packets
   * with SERVFAIL, NOTIMP, or REFUSED response codes.
   */
  if (!(channel->flags & ARES_FLAG_NOCHECKRESP))
    {
      if (rcode == SERVFAIL || rcode == NOTIMP || rcode == REFUSED)
	{
	  query->skip_server[whichserver] = 1;
	  if (query->server == whichserver)
	    next_server(channel, query, now);
	  return;
	}
      if (!same_questions((unsigned char*)query->qbuf, query->qlen, abuf, alen))
	{
	  if (query->server == whichserver)
	    next_server(channel, query, now);
	  return;
	}

      /* 'No such name' */
      if ((channel->flags & ARES_FLAG_TRY_NEXT_SERVER_ON_RCODE3) && rcode == NXDOMAIN)
        {
          if (query->server == whichserver)
            {
              if (next_server_new_network(channel, query, now))
                return;
            }
        }
    }

  end_query(channel, query, ARES_SUCCESS, abuf, alen);
}