summaryrefslogtreecommitdiff
path: root/ares_send.c
blob: ab85a2d6984c3d1709362f7e3956360854a304eb (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134

/* Copyright 1998 by the Massachusetts Institute of Technology.
 *
 * Permission to use, copy, modify, and distribute this
 * software and its documentation for any purpose and without
 * fee is hereby granted, provided that the above copyright
 * notice appear in all copies and that both that copyright
 * notice and this permission notice appear in supporting
 * documentation, and that the name of M.I.T. not be used in
 * advertising or publicity pertaining to distribution of the
 * software without specific, written prior permission.
 * M.I.T. makes no representations about the suitability of
 * this software for any purpose.  It is provided "as is"
 * without express or implied warranty.
 */

#include "ares_setup.h"

#ifdef HAVE_SYS_SOCKET_H
#  include <sys/socket.h>
#endif
#ifdef HAVE_NETINET_IN_H
#  include <netinet/in.h>
#endif
#ifdef HAVE_ARPA_NAMESER_H
#  include <arpa/nameser.h>
#else
#  include "nameser.h"
#endif
#ifdef HAVE_ARPA_NAMESER_COMPAT_H
#  include <arpa/nameser_compat.h>
#endif

#include "ares.h"
#include "ares_dns.h"
#include "ares_private.h"

void ares_send(ares_channel channel, const unsigned char *qbuf, int qlen,
               ares_callback callback, void *arg)
{
  struct query *query;
  int i, packetsz;
  struct timeval now;

  /* Verify that the query is at least long enough to hold the header. */
  if (qlen < HFIXEDSZ || qlen >= (1 << 16))
    {
      callback(arg, ARES_EBADQUERY, 0, NULL, 0);
      return;
    }

  /* Allocate space for query and allocated fields. */
  query = malloc(sizeof(struct query));
  if (!query)
    {
      callback(arg, ARES_ENOMEM, 0, NULL, 0);
      return;
    }
  query->tcpbuf = malloc(qlen + 2);
  if (!query->tcpbuf)
    {
      free(query);
      callback(arg, ARES_ENOMEM, 0, NULL, 0);
      return;
    }
  query->server_info = malloc(channel->nservers *
                              sizeof(query->server_info[0]));
  if (!query->server_info)
    {
      free(query->tcpbuf);
      free(query);
      callback(arg, ARES_ENOMEM, 0, NULL, 0);
      return;
    }

  /* Compute the query ID.  Start with no timeout. */
  query->qid = DNS_HEADER_QID(qbuf);
  query->timeout.tv_sec = 0;
  query->timeout.tv_usec = 0;

  /* Form the TCP query buffer by prepending qlen (as two
   * network-order bytes) to qbuf.
   */
  query->tcpbuf[0] = (unsigned char)((qlen >> 8) & 0xff);
  query->tcpbuf[1] = (unsigned char)(qlen & 0xff);
  memcpy(query->tcpbuf + 2, qbuf, qlen);
  query->tcplen = qlen + 2;

  /* Fill in query arguments. */
  query->qbuf = query->tcpbuf + 2;
  query->qlen = qlen;
  query->callback = callback;
  query->arg = arg;

  /* Initialize query status. */
  query->try_count = 0;

  /* Choose the server to send the query to. If rotation is enabled, keep track
   * of the next server we want to use. */
  query->server = channel->last_server;
  if (channel->rotate == 1)
    channel->last_server = (channel->last_server + 1) % channel->nservers;

  for (i = 0; i < channel->nservers; i++)
    {
      query->server_info[i].skip_server = 0;
      query->server_info[i].tcp_connection_generation = 0;
    }

  packetsz = (channel->flags & ARES_FLAG_EDNS) ? channel->ednspsz : PACKETSZ;
  query->using_tcp = (channel->flags & ARES_FLAG_USEVC) || qlen > packetsz;

  query->error_status = ARES_ECONNREFUSED;
  query->timeouts = 0;

  /* Initialize our list nodes. */
  ares__init_list_node(&(query->queries_by_qid),     query);
  ares__init_list_node(&(query->queries_by_timeout), query);
  ares__init_list_node(&(query->queries_to_server),  query);
  ares__init_list_node(&(query->all_queries),        query);

  /* Chain the query into the list of all queries. */
  ares__insert_in_list(&(query->all_queries), &(channel->all_queries));
  /* Keep track of queries bucketed by qid, so we can process DNS
   * responses quickly.
   */
  ares__insert_in_list(
    &(query->queries_by_qid),
    &(channel->queries_by_qid[query->qid % ARES_QID_TABLE_SIZE]));

  /* Perform the first query action. */
  now = ares__tvnow();
  ares__send_query(channel, query, &now);
}