a34dda7de6de6d3280d7ec5c952fd72b32e96fe2
[platform/upstream/c-ares.git] / ares_create_query.c
1
2 /* Copyright 1998 by the Massachusetts Institute of Technology.
3  *
4  * Permission to use, copy, modify, and distribute this
5  * software and its documentation for any purpose and without
6  * fee is hereby granted, provided that the above copyright
7  * notice appear in all copies and that both that copyright
8  * notice and this permission notice appear in supporting
9  * documentation, and that the name of M.I.T. not be used in
10  * advertising or publicity pertaining to distribution of the
11  * software without specific, written prior permission.
12  * M.I.T. makes no representations about the suitability of
13  * this software for any purpose.  It is provided "as is"
14  * without express or implied warranty.
15  */
16
17 #include "ares_setup.h"
18
19 #ifdef HAVE_NETINET_IN_H
20 #  include <netinet/in.h>
21 #endif
22 #ifdef HAVE_ARPA_NAMESER_H
23 #  include <arpa/nameser.h>
24 #else
25 #  include "nameser.h"
26 #endif
27 #ifdef HAVE_ARPA_NAMESER_COMPAT_H
28 #  include <arpa/nameser_compat.h>
29 #endif
30
31 #include "ares.h"
32 #include "ares_dns.h"
33 #include "ares_private.h"
34
35 #ifndef T_OPT
36 #  define T_OPT  41 /* EDNS0 option (meta-RR) */
37 #endif
38
39 /* Header format, from RFC 1035:
40  *                                  1  1  1  1  1  1
41  *    0  1  2  3  4  5  6  7  8  9  0  1  2  3  4  5
42  *  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
43  *  |                      ID                       |
44  *  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
45  *  |QR|   Opcode  |AA|TC|RD|RA|   Z    |   RCODE   |
46  *  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
47  *  |                    QDCOUNT                    |
48  *  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
49  *  |                    ANCOUNT                    |
50  *  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
51  *  |                    NSCOUNT                    |
52  *  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
53  *  |                    ARCOUNT                    |
54  *  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
55  *
56  * AA, TC, RA, and RCODE are only set in responses.  Brief description
57  * of the remaining fields:
58  *      ID      Identifier to match responses with queries
59  *      QR      Query (0) or response (1)
60  *      Opcode  For our purposes, always QUERY
61  *      RD      Recursion desired
62  *      Z       Reserved (zero)
63  *      QDCOUNT Number of queries
64  *      ANCOUNT Number of answers
65  *      NSCOUNT Number of name server records
66  *      ARCOUNT Number of additional records
67  *
68  * Question format, from RFC 1035:
69  *                                  1  1  1  1  1  1
70  *    0  1  2  3  4  5  6  7  8  9  0  1  2  3  4  5
71  *  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
72  *  |                                               |
73  *  /                     QNAME                     /
74  *  /                                               /
75  *  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
76  *  |                     QTYPE                     |
77  *  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
78  *  |                     QCLASS                    |
79  *  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
80  *
81  * The query name is encoded as a series of labels, each represented
82  * as a one-byte length (maximum 63) followed by the text of the
83  * label.  The list is terminated by a label of length zero (which can
84  * be thought of as the root domain).
85  */
86
87 int ares_create_query(const char *name, int dnsclass, int type,
88                       unsigned short id, int rd, unsigned char **buf,
89                       int *buflen, int max_udp_size)
90 {
91   int len;
92   unsigned char *q;
93   const char *p;
94
95   /* Set our results early, in case we bail out early with an error. */
96   *buflen = 0;
97   *buf = NULL;
98
99   /* Compute the length of the encoded name so we can check buflen.
100    * Start counting at 1 for the zero-length label at the end. */
101   len = 1;
102   for (p = name; *p; p++)
103     {
104       if (*p == '\\' && *(p + 1) != 0)
105         p++;
106       len++;
107     }
108   /* If there are n periods in the name, there are n + 1 labels, and
109    * thus n + 1 length fields, unless the name is empty or ends with a
110    * period.  So add 1 unless name is empty or ends with a period.
111    */
112   if (*name && *(p - 1) != '.')
113     len++;
114
115   /* Immediately reject names that are longer than the maximum of 255
116    * bytes that's specified in RFC 1035 ("To simplify implementations,
117    * the total length of a domain name (i.e., label octets and label
118    * length octets) is restricted to 255 octets or less."). We aren't
119    * doing this just to be a stickler about RFCs. For names that are
120    * too long, 'dnscache' closes its TCP connection to us immediately
121    * (when using TCP) and ignores the request when using UDP, and
122    * BIND's named returns ServFail (TCP or UDP). Sending a request
123    * that we know will cause 'dnscache' to close the TCP connection is
124    * painful, since that makes any other outstanding requests on that
125    * connection fail. And sending a UDP request that we know
126    * 'dnscache' will ignore is bad because resources will be tied up
127    * until we time-out the request.
128    */
129   if (len > MAXCDNAME)
130     return ARES_EBADNAME;
131
132   *buflen = len + HFIXEDSZ + QFIXEDSZ + (max_udp_size ? EDNSFIXEDSZ : 0);
133   *buf = ares_malloc(*buflen);
134   if (!*buf)
135       return ARES_ENOMEM;
136
137   /* Set up the header. */
138   q = *buf;
139   memset(q, 0, HFIXEDSZ);
140   DNS_HEADER_SET_QID(q, id);
141   DNS_HEADER_SET_OPCODE(q, QUERY);
142   if (rd) {
143     DNS_HEADER_SET_RD(q, 1);
144   }
145   else {
146     DNS_HEADER_SET_RD(q, 0);
147   }
148   DNS_HEADER_SET_QDCOUNT(q, 1);
149
150   if (max_udp_size) {
151       DNS_HEADER_SET_ARCOUNT(q, 1);
152   }
153
154   /* A name of "." is a screw case for the loop below, so adjust it. */
155   if (strcmp(name, ".") == 0)
156     name++;
157
158   /* Start writing out the name after the header. */
159   q += HFIXEDSZ;
160   while (*name)
161     {
162       if (*name == '.')
163         return ARES_EBADNAME;
164
165       /* Count the number of bytes in this label. */
166       len = 0;
167       for (p = name; *p && *p != '.'; p++)
168         {
169           if (*p == '\\' && *(p + 1) != 0)
170             p++;
171           len++;
172         }
173       if (len > MAXLABEL)
174         return ARES_EBADNAME;
175
176       /* Encode the length and copy the data. */
177       *q++ = (unsigned char)len;
178       for (p = name; *p && *p != '.'; p++)
179         {
180           if (*p == '\\' && *(p + 1) != 0)
181             p++;
182           *q++ = *p;
183         }
184
185       /* Go to the next label and repeat, unless we hit the end. */
186       if (!*p)
187         break;
188       name = p + 1;
189     }
190
191   /* Add the zero-length label at the end. */
192   *q++ = 0;
193
194   /* Finish off the question with the type and class. */
195   DNS_QUESTION_SET_TYPE(q, type);
196   DNS_QUESTION_SET_CLASS(q, dnsclass);
197
198   if (max_udp_size)
199   {
200       q += QFIXEDSZ;
201       memset(q, 0, EDNSFIXEDSZ);
202       q++;
203       DNS_RR_SET_TYPE(q, T_OPT);
204       DNS_RR_SET_CLASS(q, max_udp_size);
205   }
206
207   return ARES_SUCCESS;
208 }