~ubuntu-branches/ubuntu/intrepid/djbdns/intrepid-security

« back to all changes in this revision

Viewing changes to dns_transmit.c

  • Committer: Bazaar Package Importer
  • Author(s): Gerrit Pape
  • Date: 2008-03-02 23:22:04 UTC
  • Revision ID: james.westby@ubuntu.com-20080302232204-wa3owprcpeiyu8kj
Tags: upstream-1.05
ImportĀ upstreamĀ versionĀ 1.05

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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
 
 
11
static 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
 
 
20
static 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
 
 
32
static 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
 
 
55
static void packetfree(struct dns_transmit *d)
 
56
{
 
57
  if (!d->packet) return;
 
58
  alloc_free(d->packet);
 
59
  d->packet = 0;
 
60
}
 
61
 
 
62
static void queryfree(struct dns_transmit *d)
 
63
{
 
64
  if (!d->query) return;
 
65
  alloc_free(d->query);
 
66
  d->query = 0;
 
67
}
 
68
 
 
69
static void socketfree(struct dns_transmit *d)
 
70
{
 
71
  if (!d->s1) return;
 
72
  close(d->s1 - 1);
 
73
  d->s1 = 0;
 
74
}
 
75
 
 
76
void dns_transmit_free(struct dns_transmit *d)
 
77
{
 
78
  queryfree(d);
 
79
  socketfree(d);
 
80
  packetfree(d);
 
81
}
 
82
 
 
83
static 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
 
 
95
static const int timeouts[4] = { 1, 3, 11, 45 };
 
96
 
 
97
static 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
 
 
135
static int firstudp(struct dns_transmit *d)
 
136
{
 
137
  d->curserver = 0;
 
138
  return thisudp(d);
 
139
}
 
140
 
 
141
static int nextudp(struct dns_transmit *d)
 
142
{
 
143
  ++d->curserver;
 
144
  return thisudp(d);
 
145
}
 
146
 
 
147
static 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
 
 
184
static int firsttcp(struct dns_transmit *d)
 
185
{
 
186
  d->curserver = 0;
 
187
  return thistcp(d);
 
188
}
 
189
 
 
190
static int nexttcp(struct dns_transmit *d)
 
191
{
 
192
  ++d->curserver;
 
193
  return thistcp(d);
 
194
}
 
195
 
 
196
int 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
 
 
224
void 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
 
 
241
int 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
/*
 
260
have attempted to send UDP query to each server udploop times
 
261
have 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
/*
 
288
have sent connection attempt to curserver on TCP socket s
 
289
pos 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
/*
 
299
have connection to curserver on TCP socket s
 
300
have 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
/*
 
317
have sent entire query to curserver on TCP socket s
 
318
pos 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
/*
 
329
have sent entire query to curserver on TCP socket s
 
330
pos not defined
 
331
have 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
/*
 
346
have sent entire query to curserver on TCP socket s
 
347
have received entire packet length into packetlen
 
348
packet is allocated
 
349
have 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
}