release
[hcoop/zz_old/debian/djbdns.git] / dns_transmit.c
CommitLineData
dc0d77d7
CE
1#include <sys/types.h>
2#include <sys/socket.h>
3#include <unistd.h>
4#include "socket.h"
5#include "alloc.h"
6#include "error.h"
7#include "byte.h"
8#include "uint16.h"
9#include "dns.h"
10
11static int serverwantstcp(const char *buf,unsigned int len)
12{
13 char out[12];
14
15 if (!dns_packet_copy(buf,len,0,out,12)) return 1;
16 if (out[2] & 2) return 1;
17 return 0;
18}
19
20static int serverfailed(const char *buf,unsigned int len)
21{
22 char out[12];
23 unsigned int rcode;
24
25 if (!dns_packet_copy(buf,len,0,out,12)) return 1;
26 rcode = out[3];
27 rcode &= 15;
28 if (rcode && (rcode != 3)) { errno = error_again; return 1; }
29 return 0;
30}
31
32static int irrelevant(const struct dns_transmit *d,const char *buf,unsigned int len)
33{
34 char out[12];
35 char *dn;
36 unsigned int pos;
37
38 pos = dns_packet_copy(buf,len,0,out,12); if (!pos) return 1;
39 if (byte_diff(out,2,d->query + 2)) return 1;
40 if (out[4] != 0) return 1;
41 if (out[5] != 1) return 1;
42
43 dn = 0;
44 pos = dns_packet_getname(buf,len,pos,&dn); if (!pos) return 1;
45 if (!dns_domain_equal(dn,d->query + 14)) { alloc_free(dn); return 1; }
46 alloc_free(dn);
47
48 pos = dns_packet_copy(buf,len,pos,out,4); if (!pos) return 1;
49 if (byte_diff(out,2,d->qtype)) return 1;
50 if (byte_diff(out + 2,2,DNS_C_IN)) return 1;
51
52 return 0;
53}
54
55static void packetfree(struct dns_transmit *d)
56{
57 if (!d->packet) return;
58 alloc_free(d->packet);
59 d->packet = 0;
60}
61
62static void queryfree(struct dns_transmit *d)
63{
64 if (!d->query) return;
65 alloc_free(d->query);
66 d->query = 0;
67}
68
69static void socketfree(struct dns_transmit *d)
70{
71 if (!d->s1) return;
72 close(d->s1 - 1);
73 d->s1 = 0;
74}
75
76void dns_transmit_free(struct dns_transmit *d)
77{
78 queryfree(d);
79 socketfree(d);
80 packetfree(d);
81}
82
83static int randombind(struct dns_transmit *d)
84{
85 int j;
86
87 for (j = 0;j < 10;++j)
88 if (socket_bind4(d->s1 - 1,d->localip,1025 + dns_random(64510)) == 0)
89 return 0;
90 if (socket_bind4(d->s1 - 1,d->localip,0) == 0)
91 return 0;
92 return -1;
93}
94
95static const int timeouts[4] = { 1, 3, 11, 45 };
96
97static int thisudp(struct dns_transmit *d)
98{
99 const char *ip;
100
101 socketfree(d);
102
103 while (d->udploop < 4) {
104 for (;d->curserver < 16;++d->curserver) {
105 ip = d->servers + 4 * d->curserver;
106 if (byte_diff(ip,4,"\0\0\0\0")) {
107 d->query[2] = dns_random(256);
108 d->query[3] = dns_random(256);
109
110 d->s1 = 1 + socket_udp();
111 if (!d->s1) { dns_transmit_free(d); return -1; }
112 if (randombind(d) == -1) { dns_transmit_free(d); return -1; }
113
114 if (socket_connect4(d->s1 - 1,ip,53) == 0)
115 if (send(d->s1 - 1,d->query + 2,d->querylen - 2,0) == d->querylen - 2) {
116 struct taia now;
117 taia_now(&now);
118 taia_uint(&d->deadline,timeouts[d->udploop]);
119 taia_add(&d->deadline,&d->deadline,&now);
120 d->tcpstate = 0;
121 return 0;
122 }
123
124 socketfree(d);
125 }
126 }
127
128 ++d->udploop;
129 d->curserver = 0;
130 }
131
132 dns_transmit_free(d); return -1;
133}
134
135static int firstudp(struct dns_transmit *d)
136{
137 d->curserver = 0;
138 return thisudp(d);
139}
140
141static int nextudp(struct dns_transmit *d)
142{
143 ++d->curserver;
144 return thisudp(d);
145}
146
147static int thistcp(struct dns_transmit *d)
148{
149 struct taia now;
150 const char *ip;
151
152 socketfree(d);
153 packetfree(d);
154
155 for (;d->curserver < 16;++d->curserver) {
156 ip = d->servers + 4 * d->curserver;
157 if (byte_diff(ip,4,"\0\0\0\0")) {
158 d->query[2] = dns_random(256);
159 d->query[3] = dns_random(256);
160
161 d->s1 = 1 + socket_tcp();
162 if (!d->s1) { dns_transmit_free(d); return -1; }
163 if (randombind(d) == -1) { dns_transmit_free(d); return -1; }
164
165 taia_now(&now);
166 taia_uint(&d->deadline,10);
167 taia_add(&d->deadline,&d->deadline,&now);
168 if (socket_connect4(d->s1 - 1,ip,53) == 0) {
169 d->tcpstate = 2;
170 return 0;
171 }
172 if ((errno == error_inprogress) || (errno == error_wouldblock)) {
173 d->tcpstate = 1;
174 return 0;
175 }
176
177 socketfree(d);
178 }
179 }
180
181 dns_transmit_free(d); return -1;
182}
183
184static int firsttcp(struct dns_transmit *d)
185{
186 d->curserver = 0;
187 return thistcp(d);
188}
189
190static int nexttcp(struct dns_transmit *d)
191{
192 ++d->curserver;
193 return thistcp(d);
194}
195
196int dns_transmit_start(struct dns_transmit *d,const char servers[64],int flagrecursive,const char *q,const char qtype[2],const char localip[4])
197{
198 unsigned int len;
199
200 dns_transmit_free(d);
201 errno = error_io;
202
203 len = dns_domain_length(q);
204 d->querylen = len + 18;
205 d->query = alloc(d->querylen);
206 if (!d->query) return -1;
207
208 uint16_pack_big(d->query,len + 16);
209 byte_copy(d->query + 2,12,flagrecursive ? "\0\0\1\0\0\1\0\0\0\0\0\0" : "\0\0\0\0\0\1\0\0\0\0\0\0gcc-bug-workaround");
210 byte_copy(d->query + 14,len,q);
211 byte_copy(d->query + 14 + len,2,qtype);
212 byte_copy(d->query + 16 + len,2,DNS_C_IN);
213
214 byte_copy(d->qtype,2,qtype);
215 d->servers = servers;
216 byte_copy(d->localip,4,localip);
217
218 d->udploop = flagrecursive ? 1 : 0;
219
220 if (len + 16 > 512) return firsttcp(d);
221 return firstudp(d);
222}
223
224void dns_transmit_io(struct dns_transmit *d,iopause_fd *x,struct taia *deadline)
225{
226 x->fd = d->s1 - 1;
227
228 switch(d->tcpstate) {
229 case 0: case 3: case 4: case 5:
230 x->events = IOPAUSE_READ;
231 break;
232 case 1: case 2:
233 x->events = IOPAUSE_WRITE;
234 break;
235 }
236
237 if (taia_less(&d->deadline,deadline))
238 *deadline = d->deadline;
239}
240
241int dns_transmit_get(struct dns_transmit *d,const iopause_fd *x,const struct taia *when)
242{
243 char udpbuf[513];
244 unsigned char ch;
245 int r;
246 int fd;
247
248 errno = error_io;
249 fd = d->s1 - 1;
250
251 if (!x->revents) {
252 if (taia_less(when,&d->deadline)) return 0;
253 errno = error_timeout;
254 if (d->tcpstate == 0) return nextudp(d);
255 return nexttcp(d);
256 }
257
258 if (d->tcpstate == 0) {
259/*
260have attempted to send UDP query to each server udploop times
261have sent query to curserver on UDP socket s
262*/
263 r = recv(fd,udpbuf,sizeof udpbuf,0);
264 if (r <= 0) {
265 if (errno == error_connrefused) if (d->udploop == 2) return 0;
266 return nextudp(d);
267 }
268 if (r + 1 > sizeof udpbuf) return 0;
269
270 if (irrelevant(d,udpbuf,r)) return 0;
271 if (serverwantstcp(udpbuf,r)) return firsttcp(d);
272 if (serverfailed(udpbuf,r)) {
273 if (d->udploop == 2) return 0;
274 return nextudp(d);
275 }
276 socketfree(d);
277
278 d->packetlen = r;
279 d->packet = alloc(d->packetlen);
280 if (!d->packet) { dns_transmit_free(d); return -1; }
281 byte_copy(d->packet,d->packetlen,udpbuf);
282 queryfree(d);
283 return 1;
284 }
285
286 if (d->tcpstate == 1) {
287/*
288have sent connection attempt to curserver on TCP socket s
289pos not defined
290*/
291 if (!socket_connected(fd)) return nexttcp(d);
292 d->pos = 0;
293 d->tcpstate = 2;
294 return 0;
295 }
296
297 if (d->tcpstate == 2) {
298/*
299have connection to curserver on TCP socket s
300have sent pos bytes of query
301*/
302 r = write(fd,d->query + d->pos,d->querylen - d->pos);
303 if (r <= 0) return nexttcp(d);
304 d->pos += r;
305 if (d->pos == d->querylen) {
306 struct taia now;
307 taia_now(&now);
308 taia_uint(&d->deadline,10);
309 taia_add(&d->deadline,&d->deadline,&now);
310 d->tcpstate = 3;
311 }
312 return 0;
313 }
314
315 if (d->tcpstate == 3) {
316/*
317have sent entire query to curserver on TCP socket s
318pos not defined
319*/
320 r = read(fd,&ch,1);
321 if (r <= 0) return nexttcp(d);
322 d->packetlen = ch;
323 d->tcpstate = 4;
324 return 0;
325 }
326
327 if (d->tcpstate == 4) {
328/*
329have sent entire query to curserver on TCP socket s
330pos not defined
331have received one byte of packet length into packetlen
332*/
333 r = read(fd,&ch,1);
334 if (r <= 0) return nexttcp(d);
335 d->packetlen <<= 8;
336 d->packetlen += ch;
337 d->tcpstate = 5;
338 d->pos = 0;
339 d->packet = alloc(d->packetlen);
340 if (!d->packet) { dns_transmit_free(d); return -1; }
341 return 0;
342 }
343
344 if (d->tcpstate == 5) {
345/*
346have sent entire query to curserver on TCP socket s
347have received entire packet length into packetlen
348packet is allocated
349have received pos bytes of packet
350*/
351 r = read(fd,d->packet + d->pos,d->packetlen - d->pos);
352 if (r <= 0) return nexttcp(d);
353 d->pos += r;
354 if (d->pos < d->packetlen) return 0;
355
356 socketfree(d);
357 if (irrelevant(d,d->packet,d->packetlen)) return nexttcp(d);
358 if (serverwantstcp(d->packet,d->packetlen)) return nexttcp(d);
359 if (serverfailed(d->packet,d->packetlen)) return nexttcp(d);
360
361 queryfree(d);
362 return 1;
363 }
364
365 return 0;
366}