]> git.ipfire.org Git - thirdparty/squid.git/blob - src/dns/rfc3596.cc
720861b6bce3216ad4a26b7fc41b2f5e8ba6476c
[thirdparty/squid.git] / src / dns / rfc3596.cc
1 /*
2 * Copyright (C) 1996-2016 The Squid Software Foundation and contributors
3 *
4 * Squid software is distributed under GPLv2+ license and includes
5 * contributions from numerous individuals and organizations.
6 * Please see the COPYING and CONTRIBUTORS files for details.
7 */
8
9 #include "squid.h"
10 #include "dns/rfc2671.h"
11 #include "dns/rfc3596.h"
12 #include "util.h"
13
14 #if HAVE_UNISTD_H
15 #include <unistd.h>
16 #endif
17 #if HAVE_MEMORY_H
18 #include <memory.h>
19 #endif
20 #if HAVE_ASSERT_H
21 #include <assert.h>
22 #endif
23 #if HAVE_NETINET_IN_H
24 #include <netinet/in.h>
25 #endif
26 #if HAVE_STRINGS_H
27 #include <strings.h>
28 #endif
29
30 #ifndef SQUID_RFC1035_H
31 #error RFC3596 Library depends on RFC1035
32 #endif
33
34 /*
35 * Low level DNS protocol routines
36 *
37 * Provides RFC3596 functions to handle purely IPv6 DNS.
38 * Adds AAAA and IPv6 PTR records.
39 * Other IPv6 records are not mentioned by this RFC.
40 *
41 * IPv4 equivalents are taken care of by the RFC1035 library.
42 * Where one protocol lookup must be followed by another, the caller
43 * is resposible for the order and handling of the lookups.
44 *
45 * KNOWN BUGS:
46 *
47 * UDP replies with TC set should be retried via TCP
48 */
49
50 /**
51 * Builds a message buffer with a QUESTION to lookup records
52 * for a hostname. Caller must allocate 'buf' which should
53 * probably be at least 512 octets. The 'szp' initially
54 * specifies the size of the buffer, on return it contains
55 * the size of the message (i.e. how much to write).
56 * Returns the size of the query
57 */
58 ssize_t
59 rfc3596BuildHostQuery(const char *hostname, char *buf, size_t sz, unsigned short qid, rfc1035_query * query, int qtype, ssize_t edns_sz)
60 {
61 static rfc1035_message h;
62 size_t offset = 0;
63 memset(&h, '\0', sizeof(h));
64 h.id = qid;
65 h.qr = 0;
66 h.rd = 1;
67 h.opcode = 0; /* QUERY */
68 h.qdcount = (unsigned int) 1;
69 h.arcount = (edns_sz > 0 ? 1 : 0);
70 offset += rfc1035HeaderPack(buf + offset, sz - offset, &h);
71 offset += rfc1035QuestionPack(buf + offset,
72 sz - offset,
73 hostname,
74 qtype,
75 RFC1035_CLASS_IN);
76 if (edns_sz > 0)
77 offset += rfc2671RROptPack(buf + offset, sz - offset, edns_sz);
78
79 if (query) {
80 query->qtype = qtype;
81 query->qclass = RFC1035_CLASS_IN;
82 xstrncpy(query->name, hostname, sizeof(query->name));
83 }
84
85 assert(offset <= sz);
86 return offset;
87 }
88
89 /**
90 * Builds a message buffer with a QUESTION to lookup A records
91 * for a hostname. Caller must allocate 'buf' which should
92 * probably be at least 512 octets. The 'szp' initially
93 * specifies the size of the buffer, on return it contains
94 * the size of the message (i.e. how much to write).
95 * \return the size of the query
96 */
97 ssize_t
98 rfc3596BuildAQuery(const char *hostname, char *buf, size_t sz, unsigned short qid, rfc1035_query * query, ssize_t edns_sz)
99 {
100 return rfc3596BuildHostQuery(hostname, buf, sz, qid, query, RFC1035_TYPE_A, edns_sz);
101 }
102
103 /**
104 * Builds a message buffer with a QUESTION to lookup AAAA records
105 * for a hostname. Caller must allocate 'buf' which should
106 * probably be at least 512 octets. The 'szp' initially
107 * specifies the size of the buffer, on return it contains
108 * the size of the message (i.e. how much to write).
109 * \return the size of the query
110 */
111 ssize_t
112 rfc3596BuildAAAAQuery(const char *hostname, char *buf, size_t sz, unsigned short qid, rfc1035_query * query, ssize_t edns_sz)
113 {
114 return rfc3596BuildHostQuery(hostname, buf, sz, qid, query, RFC1035_TYPE_AAAA, edns_sz);
115 }
116
117 /**
118 * Builds a message buffer with a QUESTION to lookup PTR records
119 * for an address. Caller must allocate 'buf' which should
120 * probably be at least 512 octets. The 'szp' initially
121 * specifies the size of the buffer, on return it contains
122 * the size of the message (i.e. how much to write).
123 * \return the size of the query
124 */
125 ssize_t
126 rfc3596BuildPTRQuery4(const struct in_addr addr, char *buf, size_t sz, unsigned short qid, rfc1035_query * query, ssize_t edns_sz)
127 {
128 static char rev[RFC1035_MAXHOSTNAMESZ];
129 unsigned int i;
130
131 i = (unsigned int) ntohl(addr.s_addr);
132 snprintf(rev, RFC1035_MAXHOSTNAMESZ, "%u.%u.%u.%u.in-addr.arpa.",
133 i & 255,
134 (i >> 8) & 255,
135 (i >> 16) & 255,
136 (i >> 24) & 255);
137
138 return rfc3596BuildHostQuery(rev, buf, sz, qid, query, RFC1035_TYPE_PTR, edns_sz);
139 }
140
141 ssize_t
142 rfc3596BuildPTRQuery6(const struct in6_addr addr, char *buf, size_t sz, unsigned short qid, rfc1035_query * query, ssize_t edns_sz)
143 {
144 static char rev[RFC1035_MAXHOSTNAMESZ];
145 const uint8_t* r = addr.s6_addr;
146 char* p = rev;
147 int i; /* NP: MUST allow signed for loop termination. */
148
149 /* work from the raw addr field. anything else may have representation changes. */
150 /* The sin6_port and sin6_addr members shall be in network byte order. */
151 for (i = 15; i >= 0; i--, p+=4) {
152 snprintf(p, 5, "%1x.%1x.", ((r[i])&0xf), (r[i]>>4)&0xf );
153 }
154
155 snprintf(p,10,"ip6.arpa.");
156
157 return rfc3596BuildHostQuery(rev, buf, sz, qid, query, RFC1035_TYPE_PTR, edns_sz);
158 }
159
160 #if DRIVER
161
162 /* driver needs the rfc1035 code _without_ the main() */
163 # define main(a,b) rfc1035_main(a,b)
164 # include "rfc1035.c"
165 # undef main(a,b)
166
167 #include <sys/socket.h>
168
169 int
170 main(int argc, char *argv[])
171 {
172 #define PACKET_BUFSZ 1024
173 char input[PACKET_BUFSZ];
174 char buf[PACKET_BUFSZ];
175 char rbuf[PACKET_BUFSZ];
176 size_t sz = PACKET_BUFSZ;
177 unsigned short sid, sidb;
178 int s;
179 int rl;
180 ssize_t edns_max = -1;
181
182 struct sockaddr* S;
183 int var = 1;
184
185 if ( argc < 3 || argc > 4) {
186 fprintf(stderr, "usage: %s [-6|-4] ip port\n", argv[0]);
187 return 1;
188 }
189
190 setbuf(stdout, NULL);
191 setbuf(stderr, NULL);
192
193 if (argv[var][0] == '-') {
194 if (argv[var][1] == '4')
195 prefer = AF_INET;
196 else if (argv[var][1] == '6')
197 prefer = AF_INET6;
198 else if (argv[var][1] == 'E')
199 edns_max = atoi(argv[var++]);
200 else {
201 fprintf(stderr, "usage: %s [-6|-4] [-E packet-size] ip port\n", argv[0]);
202 fprintf(stderr, " EDNS packets my be up to %d\n", PACKET_BUFSZ);
203 return 1;
204 }
205
206 var++;
207 }
208
209 s = socket(PF_INET, SOCK_DGRAM, 0);
210
211 if (s < 0) {
212 perror("socket");
213 return 1;
214 }
215
216 memset(&S, '\0', sizeof(S));
217
218 if (prefer == 6) {
219 S = (struct sockaddr *) new sockaddr_in6;
220 memset(S,0,sizeof(struct sockaddr_in6));
221
222 ((struct sockaddr_in6 *)S)->sin6_family = AF_INET6;
223 ((struct sockaddr_in6 *)S)->sin6_port = htons(atoi(argv[var+1]));
224
225 if ( ! inet_pton(AF_INET6, argv[var], &((struct sockaddr_in6 *)S)->sin6_addr.s_addr) ) {
226 perror("listen address");
227 return 1;
228 }
229
230 s = socket(PF_INET6, SOCK_DGRAM, 0);
231 } else {
232 S = (struct sockaddr *) new sockaddr_in;
233 memset(S,0,sizeof(struct sockaddr_in));
234
235 ((struct sockaddr_in *)S)->sin_family = AF_INET;
236 ((struct sockaddr_in *)S)->sin_port = htons(atoi(argv[var+1]));
237
238 if ( ! inet_pton(AF_INET, argv[var], &((struct sockaddr_in *)S)->sin_addr.s_addr) )
239 perror("listen address");
240 return 1;
241 }
242 }
243
244 while (fgets(input, PACKET_BUFSZ, stdin))
245 {
246
247 struct in6_addr junk6;
248
249 struct in_addr junk4;
250 strtok(input, "\r\n");
251 memset(buf, '\0', PACKET_BUFSZ);
252 sz = PACKET_BUFSZ;
253
254 if (inet_pton(AF_INET6, input, &junk6)) {
255 sid = rfc1035BuildPTRQuery6(junk6, buf, &sz, edns_max);
256 sidb=0;
257 } else if (inet_pton(AF_INET, input, &junk4)) {
258 sid = rfc1035BuildPTRQuery4(junk4, buf, &sz, edns_max);
259 sidb=0;
260 } else {
261 sid = rfc1035BuildAAAAQuery(input, buf, &sz, edns_max);
262 sidb = rfc1035BuildAQuery(input, buf, &sz, edns_max);
263 }
264
265 sendto(s, buf, sz, 0, S, sizeof(*S));
266
267 do {
268 fd_set R;
269
270 struct timeval to;
271 FD_ZERO(&R);
272 FD_SET(s, &R);
273 to.tv_sec = 10;
274 to.tv_usec = 0;
275 rl = select(s + 1, &R, NULL, NULL, &to);
276 } while (0);
277
278 if (rl < 1) {
279 printf("TIMEOUT\n");
280 continue;
281 }
282
283 memset(rbuf, '\0', PACKET_BUFSZ);
284 rl = recv(s, rbuf, PACKET_BUFSZ, 0);
285 {
286 unsigned short rid = 0;
287 int i;
288 int n;
289 rfc1035_rr *answers = NULL;
290 n = rfc1035AnswersUnpack(rbuf,
291 rl,
292 &answers,
293 &rid);
294
295 if (n < 0) {
296 printf("ERROR %d\n", -n);
297 } else if (rid != sid && rid != sidb) {
298 printf("ERROR, ID mismatch (%#hx, %#hx)\n", sid, rid);
299 printf("ERROR, ID mismatch (%#hx, %#hx)\n", sidb, rid);
300 } else {
301 printf("%d answers\n", n);
302
303 for (i = 0; i < n; i++) {
304 if (answers[i].type == RFC1035_TYPE_A) {
305
306 struct in_addr a;
307 char tmp[16];
308 memcpy(&a, answers[i].rdata, 4);
309 printf("A\t%d\t%s\n", answers[i].ttl, inet_ntop(AF_INET,&a,tmp,16));
310 } else if (answers[i].type == RFC1035_TYPE_AAAA) {
311
312 struct in6_addr a;
313 char tmp[INET6_ADDRSTRLEN];
314 memcpy(&a, answers[i].rdata, 16);
315 printf("AAAA\t%d\t%s\n", answers[i].ttl, inet_ntop(AF_INET6,&a,tmp,sizeof(tmp)));
316 } else if (answers[i].type == RFC1035_TYPE_PTR) {
317 char ptr[RFC1035_MAXHOSTNAMESZ];
318 strncpy(ptr, answers[i].rdata, answers[i].rdlength);
319 printf("PTR\t%d\t%s\n", answers[i].ttl, ptr);
320 } else if (answers[i].type == RFC1035_TYPE_CNAME) {
321 char ptr[RFC1035_MAXHOSTNAMESZ];
322 strncpy(ptr, answers[i].rdata, answers[i].rdlength);
323 printf("CNAME\t%d\t%s\n", answers[i].ttl, ptr);
324 } else {
325 fprintf(stderr, "can't print answer type %d\n",
326 (int) answers[i].type);
327 }
328 }
329 }
330 }
331 }
332
333 return 0;
334 }
335
336 #endif
337