config-win32.h: comments adjustments - followup
[platform/upstream/c-ares.git] / ares_mkquery.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_SYS_SOCKET_H
20 #  include <sys/socket.h>
21 #endif
22 #ifdef HAVE_NETINET_IN_H
23 #  include <netinet/in.h>
24 #endif
25 #ifdef HAVE_ARPA_NAMESER_H
26 #  include <arpa/nameser.h>
27 #else
28 #  include "nameser.h"
29 #endif
30 #ifdef HAVE_ARPA_NAMESER_COMPAT_H
31 #  include <arpa/nameser_compat.h>
32 #endif
33
34 #include <stdlib.h>
35 #include <string.h>
36 #include "ares.h"
37 #include "ares_dns.h"
38 #include "ares_private.h"
39
40 /* Header format, from RFC 1035:
41  *                                  1  1  1  1  1  1
42  *    0  1  2  3  4  5  6  7  8  9  0  1  2  3  4  5
43  *  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
44  *  |                      ID                       |
45  *  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
46  *  |QR|   Opcode  |AA|TC|RD|RA|   Z    |   RCODE   |
47  *  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
48  *  |                    QDCOUNT                    |
49  *  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
50  *  |                    ANCOUNT                    |
51  *  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
52  *  |                    NSCOUNT                    |
53  *  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
54  *  |                    ARCOUNT                    |
55  *  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
56  *
57  * AA, TC, RA, and RCODE are only set in responses.  Brief description
58  * of the remaining fields:
59  *      ID      Identifier to match responses with queries
60  *      QR      Query (0) or response (1)
61  *      Opcode  For our purposes, always QUERY
62  *      RD      Recursion desired
63  *      Z       Reserved (zero)
64  *      QDCOUNT Number of queries
65  *      ANCOUNT Number of answers
66  *      NSCOUNT Number of name server records
67  *      ARCOUNT Number of additional records
68  *
69  * Question format, from RFC 1035:
70  *                                  1  1  1  1  1  1
71  *    0  1  2  3  4  5  6  7  8  9  0  1  2  3  4  5
72  *  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
73  *  |                                               |
74  *  /                     QNAME                     /
75  *  /                                               /
76  *  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
77  *  |                     QTYPE                     |
78  *  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
79  *  |                     QCLASS                    |
80  *  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
81  *
82  * The query name is encoded as a series of labels, each represented
83  * as a one-byte length (maximum 63) followed by the text of the
84  * label.  The list is terminated by a label of length zero (which can
85  * be thought of as the root domain).
86  */
87
88 int ares_mkquery(const char *name, int dnsclass, int type, unsigned short id,
89                  int rd, unsigned char **buf, int *buflen)
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;
133   *buf = 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   /* A name of "." is a screw case for the loop below, so adjust it. */
151   if (strcmp(name, ".") == 0)
152     name++;
153
154   /* Start writing out the name after the header. */
155   q += HFIXEDSZ;
156   while (*name)
157     {
158       if (*name == '.')
159         return ARES_EBADNAME;
160
161       /* Count the number of bytes in this label. */
162       len = 0;
163       for (p = name; *p && *p != '.'; p++)
164         {
165           if (*p == '\\' && *(p + 1) != 0)
166             p++;
167           len++;
168         }
169       if (len > MAXLABEL)
170         return ARES_EBADNAME;
171
172       /* Encode the length and copy the data. */
173       *q++ = (unsigned char)len;
174       for (p = name; *p && *p != '.'; p++)
175         {
176           if (*p == '\\' && *(p + 1) != 0)
177             p++;
178           *q++ = *p;
179         }
180
181       /* Go to the next label and repeat, unless we hit the end. */
182       if (!*p)
183         break;
184       name = p + 1;
185     }
186
187   /* Add the zero-length label at the end. */
188   *q++ = 0;
189
190   /* Finish off the question with the type and class. */
191   DNS_QUESTION_SET_TYPE(q, type);
192   DNS_QUESTION_SET_CLASS(q, dnsclass);
193
194   return ARES_SUCCESS;
195 }