~ubuntu-branches/ubuntu/raring/maradns/raring

« back to all changes in this revision

Viewing changes to deadwood-2.4.10/src/DwUdpSocket.c

  • Committer: Bazaar Package Importer
  • Author(s): Kai Hendry
  • Date: 2010-01-24 12:17:40 UTC
  • mfrom: (1.1.13 upstream) (10.1.4 sid)
  • Revision ID: james.westby@ubuntu.com-20100124121740-a4e1fjobwaouz443
Tags: 1.4.02-1
New upstream release

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* Copyright (c) 2007-2009 Sam Trenholme and others
 
2
 *
 
3
 * TERMS
 
4
 *
 
5
 * Redistribution and use in source and binary forms, with or without
 
6
 * modification, are permitted provided that the following conditions
 
7
 * are met:
 
8
 *
 
9
 * 1. Redistributions of source code must retain the above copyright
 
10
 *    notice, this list of conditions and the following disclaimer.
 
11
 * 2. Redistributions in binary form must reproduce the above copyright
 
12
 *    notice, this list of conditions and the following disclaimer in the
 
13
 *    documentation and/or other materials provided with the distribution.
 
14
 *
 
15
 * This software is provided 'as is' with no guarantees of correctness or
 
16
 * fitness for purpose.
 
17
 */
 
18
 
 
19
#include "DwSocket.h"
 
20
#include "DwTcpSocket.h"
 
21
#include "DwCompress.h"
 
22
#include "DwDnsStr.h"
 
23
 
 
24
/* Mararc parameters that are set in DwMararc.c */
 
25
extern dw_str *key_s[];
 
26
extern dw_str *key_d[];
 
27
extern int32_t key_n[];
 
28
 
 
29
/* Parameters set in DwSys.c */
 
30
extern int64_t the_time;
 
31
extern dwr_rg *rng_seed;
 
32
extern dw_hash *cache;
 
33
 
 
34
/* List of addresses we will bind to */
 
35
extern ip_addr_T bind_address[];
 
36
extern ip_addr_T upstream_address[];
 
37
 
 
38
/* List of active sockets */
 
39
extern SOCKET b_local[];
 
40
extern SOCKET *b_remote;
 
41
 
 
42
/* The list of pending remote connections */
 
43
extern remote_T *rem;
 
44
 
 
45
/* The list of active in-flight connections */
 
46
dw_hash *inflight;
 
47
 
 
48
/* The numeric mararc parameters */
 
49
extern int maxprocs;
 
50
extern int timeout_seconds;
 
51
extern int dns_port;
 
52
extern int upstream_port;
 
53
extern int handle_overload;
 
54
extern int resurrections;
 
55
extern int min_bind;
 
56
extern int num_ports;
 
57
extern int num_retries;
 
58
extern int deliver_all;
 
59
 
 
60
/* Other mararc parameters */
 
61
dwd_dict *blacklist_dict;
 
62
 
 
63
#ifdef MINGW
 
64
/* Needed for the Windows way of making a socket non-blocking */
 
65
extern u_long dont_block;
 
66
#endif /*MINGW*/
 
67
 
 
68
/* Initialize the inflight hash */
 
69
void init_inflight_hash() {
 
70
        inflight = 0;
 
71
        if(key_n[DWM_N_max_inflights] < 2) {
 
72
                return; /* No inflight merge desired */
 
73
        }
 
74
        inflight = dwh_hash_init(key_n[DWM_N_maxprocs] + 10);
 
75
}
 
76
 
 
77
/* The upstream server we will connect to (round robin rotated) */
 
78
 
 
79
/* Bind to all IP addresses we are to bind to and return the number of
 
80
 * IP addresses we got or INVALID_SOCKET on error */
 
81
int bind_all_udp() {
 
82
        int a = 0;
 
83
        int count = 0;
 
84
        for(a = 0; a < DW_MAXIPS; a++) {
 
85
                if(bind_address[a].len != 0) {
 
86
                        b_local[a] = do_bind(&bind_address[a],SOCK_DGRAM);
 
87
                        if(b_local[a] != INVALID_SOCKET) {
 
88
                                count++;
 
89
                        }
 
90
                } else {
 
91
                        b_local[a] = INVALID_SOCKET;
 
92
                }
 
93
        }
 
94
        return count;
 
95
}
 
96
 
 
97
/* Create a sockaddr_in that will be bound to a given port; this is
 
98
 * used by the code that binds to a randomly chosen port */
 
99
void setup_bind(sockaddr_all_T *dns_udp, uint16_t port, int len) {
 
100
        if(dns_udp == 0) {
 
101
                return;
 
102
        }
 
103
        memset(dns_udp,0,sizeof(dns_udp));
 
104
        if(len == 4) {
 
105
                dns_udp->V4.sin_family = AF_INET;
 
106
                dns_udp->V4.sin_addr.s_addr = htonl(INADDR_ANY);
 
107
                dns_udp->V4.sin_port = htons(port);
 
108
#ifdef IPV6
 
109
        } else if(len == 16) {
 
110
                dns_udp->V6.sin6_family = AF_INET6;
 
111
                dns_udp->V6.sin6_addr = in6addr_any;
 
112
                dns_udp->V6.sin6_port = htons(port);
 
113
#endif
 
114
        } else { /* Bad ip */
 
115
                return;
 
116
        }
 
117
        return;
 
118
}
 
119
 
 
120
/* This tries to bind to a random port up to 10 times; should it fail
 
121
 * after 10 times, it returns a -1 */
 
122
int do_random_bind(SOCKET s, int len) {
 
123
        sockaddr_all_T dns_udp;
 
124
        int a = 0;
 
125
        int success = 0;
 
126
        socklen_t len_inet = sizeof(struct sockaddr_in);
 
127
 
 
128
        for(a = 0; a < 10; a++) {
 
129
                /* Set up random source port to bind to */
 
130
                setup_bind(&dns_udp,
 
131
                           min_bind + (dwr_rng(rng_seed) & num_ports), len);
 
132
#ifdef IPV6
 
133
                if (dns_udp.Family == AF_INET6)
 
134
                        len_inet = sizeof(struct sockaddr_in6);
 
135
#endif
 
136
                /* Try to bind to that port */
 
137
                if(bind(s, (struct sockaddr *)&dns_udp,
 
138
                   len_inet) != -1) {
 
139
                        success = 1;
 
140
                        break;
 
141
                }
 
142
        }
 
143
        if(success == 0) { /* Bind to random port failed */
 
144
                return -1;
 
145
        }
 
146
        return 1;
 
147
}
 
148
 
 
149
/* Create a sockaddr_in that will connect to a given address; -1 on fail;
 
150
 * this is used by the code that "connects" to the remote DNS server */
 
151
SOCKET setup_server(sockaddr_all_T *server, ip_addr_T *addr) {
 
152
        int s = INVALID_SOCKET;
 
153
        if(server == 0 || addr == 0) {
 
154
                return INVALID_SOCKET;
 
155
        }
 
156
 
 
157
        memset(server,0,sizeof(server));
 
158
        if ( addr->len == 4 ) {
 
159
                server->V4.sin_family = AF_INET;
 
160
                server->V4.sin_port = htons(upstream_port);
 
161
                memcpy(&(server->V4.sin_addr),addr->ip,4);
 
162
                s = socket(AF_INET,SOCK_DGRAM,0);
 
163
#ifdef IPV6
 
164
        } else if ( addr->len == 16 ) {
 
165
                server->V6.sin6_family = AF_INET6;
 
166
                server->V6.sin6_port = htons(upstream_port);
 
167
                memcpy(&(server->V6.sin6_addr),addr->ip,16);
 
168
                s = socket(AF_INET6,SOCK_DGRAM,0);
 
169
#endif
 
170
        } else {
 
171
                return INVALID_SOCKET;
 
172
        }
 
173
        return s;
 
174
}
 
175
 
 
176
/* Given a remote number, a C-string with the packet to send them, and
 
177
 * the length of that string, make a connection to a remote server */
 
178
void make_remote_connection(int32_t n, unsigned char *packet, int len,
 
179
     dw_str *query) {
 
180
        sockaddr_all_T server;
 
181
        SOCKET s = 0;
 
182
        int32_t rnum = -1;
 
183
        ip_addr_T addr = {0,{0,0}};
 
184
        socklen_t inet_len = sizeof(struct sockaddr_in);
 
185
 
 
186
        if(rem[n].socket != INVALID_SOCKET) { /* Already used (sanity check) */
 
187
                return;
 
188
        }
 
189
 
 
190
        /* Get a random query ID to send to the remote server */
 
191
        rnum = set_dns_qid(packet,len,dwr_rng(rng_seed));
 
192
        if(rnum == -1) {
 
193
                return;
 
194
        }
 
195
 
 
196
        /* Get IP of remote server and open a socket to them */
 
197
        addr = get_upstream_ip(query);
 
198
        s = setup_server(&server,&addr);
 
199
        if(s == INVALID_SOCKET) { /* Failed to make socket */
 
200
                return;
 
201
        }
 
202
 
 
203
#ifdef IPV6
 
204
        if (server.Family == AF_INET6)
 
205
                 inet_len = sizeof(struct sockaddr_in6);
 
206
#endif
 
207
 
 
208
        make_socket_nonblock(s); /* Linux kernel bug */
 
209
 
 
210
        /* Bind to source port; "connect" to remote server; send packet */
 
211
        if ((do_random_bind(s,addr.len) == -1) ||
 
212
            (connect(s, (struct sockaddr *)&server, inet_len) == -1) ||
 
213
            (send(s,packet,len,0) < 0)) {
 
214
                closesocket(s);
 
215
                return;
 
216
        }
 
217
 
 
218
        /* OK, now note the pending connection */
 
219
        b_remote[n] = s;
 
220
        rem[n].socket = s;
 
221
        rem[n].remote_id = rnum;
 
222
}
 
223
 
 
224
/* Send a server failure back to the client when the server is overloaded.
 
225
 * This is a bit of a hack; we just take the client's DNS packet, and send
 
226
 * it as-is with QR set to 1 (a response) and RCODE set to 2 (server
 
227
 * failure) */
 
228
void send_server_fail(sockaddr_all_T *client,unsigned char *a, int len,
 
229
                      SOCKET sock, int tcp_num) {
 
230
        socklen_t c_len = sizeof(struct sockaddr_in);
 
231
 
 
232
        if(len < 12) {
 
233
                return;
 
234
        }
 
235
 
 
236
#ifdef IPV6
 
237
        if (client->Family == AF_INET6)
 
238
                c_len = sizeof(struct sockaddr_in6);
 
239
#endif
 
240
        a[2] |= 0x80; /* Set QR (reply, not question) */
 
241
        a[3] &= 0xf0; a[3] |= 0x02; /* Set RCODE */
 
242
 
 
243
        if(tcp_num == -1) {
 
244
                sendto(sock,(void *)a,len,0,(struct sockaddr *)client, c_len);
 
245
        }
 
246
 
 
247
}
 
248
 
 
249
/* See if there is a "inflight" query already handling the query we
 
250
 * are processing.  If not (or if we don't merge inflights), return -1.
 
251
 * If so, return the UDP connection number of the already-inflight query
 
252
 */
 
253
 
 
254
int find_inflight_query(unsigned char *a, int len) {
 
255
        dw_str *query = 0, *answer = 0;
 
256
        int ret = -1;
 
257
 
 
258
        if(len < 12 || a == 0 || key_n[DWM_N_max_inflights] < 2) {
 
259
                goto catch_find_inflight_query;
 
260
        }
 
261
 
 
262
        query = dw_get_dname_type(a,12,len);
 
263
        if(query == 0) {
 
264
                goto catch_find_inflight_query;
 
265
        }
 
266
 
 
267
        answer = dwh_get(inflight, query, 0, 1);
 
268
        if(answer == 0) { /* Nothing in-flight */
 
269
                goto catch_find_inflight_query;
 
270
        }
 
271
 
 
272
        ret = dw_fetch_u16(answer,0);
 
273
 
 
274
        if(rem[ret].socket == INVALID_SOCKET) { /* Not valid query */
 
275
                ret = -1;
 
276
                goto catch_find_inflight_query;
 
277
        }
 
278
 
 
279
        if(dw_issame(query,rem[ret].query) != 1) { /* Actually not same */
 
280
                dwh_zap(inflight,query, 0, 1); /* Remove corrupt data */
 
281
                ret = -1;
 
282
        }
 
283
 
 
284
catch_find_inflight_query:
 
285
        if(query != 0) {
 
286
                dw_destroy(query);
 
287
        }
 
288
        if(answer != 0) {
 
289
                dw_destroy(answer);
 
290
        }
 
291
        return ret;
 
292
}
 
293
 
 
294
void zap_inflight(dw_str *query) {
 
295
        if(key_n[DWM_N_max_inflights] > 1) {
 
296
                dwh_zap(inflight,query, 0, 1);
 
297
        }
 
298
}
 
299
 
 
300
/* Make a new UDP connection on remote pending connection b */
 
301
void make_new_udp_connect(int b, unsigned char *a, int len, int num_alloc) {
 
302
        dw_str *answer = 0, *check = 0;
 
303
        int counter = 0;
 
304
 
 
305
        /* Make a new remote connection */
 
306
        if(rem[b].socket != INVALID_SOCKET) { /* Sanity check */
 
307
                return;
 
308
        }
 
309
        rem[b].query = dw_get_dname_type(a,12,len);
 
310
        make_remote_connection(b,a,len,rem[b].query);
 
311
 
 
312
        rem[b].local = malloc(num_alloc * sizeof(local_T *));
 
313
        for(counter = 0; counter < num_alloc; counter++) {
 
314
                rem[b].local[counter] = 0;
 
315
        }
 
316
        rem[b].num_locals = 1;
 
317
        if(key_n[DWM_N_max_inflights] > 1) {
 
318
                answer = dw_create(3);
 
319
                check = dwh_get(inflight, rem[b].query, 0, 1);
 
320
                if(check == 0) { /* Mark query in inflight hash */
 
321
                        dw_put_u16(answer,b,0);
 
322
                        dwh_add(inflight, rem[b].query, answer, 120, 1);
 
323
                } else { /* This query is already inflight */
 
324
                        dw_destroy(check);
 
325
                }
 
326
                dw_destroy(answer);
 
327
        }
 
328
}
 
329
 
 
330
/* Send a local DNS request to the upstream (remote) server; this
 
331
 * requires a few parameters having to do with the connection
 
332
 * to be passed to the function. */
 
333
int forward_local_udp_packet(SOCKET sock, int32_t local_id,
 
334
     ip_addr_T *from_ip, uint16_t from_port, unsigned char *a,
 
335
     int len, int tcp_num) {
 
336
        int32_t b = 0;
 
337
        int num_alloc = 0;
 
338
 
 
339
        num_alloc = key_n[DWM_N_max_inflights];
 
340
        if(num_alloc < 1) {
 
341
                num_alloc = 1;
 
342
        } else if(num_alloc > 32000) {
 
343
                num_alloc = 32000;
 
344
        }
 
345
        num_alloc++; /* Stop off-by-one attacks */
 
346
 
 
347
        b = find_inflight_query(a,len);
 
348
        if(b == -1) {
 
349
                b = find_free_remote();
 
350
                if(b == -1) { /* We're out of remote pending connections */
 
351
                        return -1;
 
352
                } else { /* Create new connection */
 
353
                        reset_rem(b);
 
354
                        make_new_udp_connect(b,a,len,num_alloc);
 
355
                }
 
356
        } else { /* Add new local to already inflight connection */
 
357
                if(rem[b].num_locals >= num_alloc - 2) {
 
358
                        return -1; /* No more inflights for this query */
 
359
                }
 
360
                rem[b].num_locals++;
 
361
#ifdef INFLIGHT_VERBOSE
 
362
                printf("Connection %d has %d locals\n",b,rem[b].num_locals);
 
363
                fflush(stdout);
 
364
#endif /* INFLIGHT_VERBOSE */
 
365
        }
 
366
        if(rem[b].socket == INVALID_SOCKET) {
 
367
                reset_rem(b);
 
368
                return -1;
 
369
        }
 
370
 
 
371
        rem[b].local[rem[b].num_locals - 1] = malloc(sizeof(local_T));
 
372
        rem[b].local[rem[b].num_locals - 1]->from_socket = sock;
 
373
        rem[b].local[rem[b].num_locals - 1]->tcp_num = tcp_num;
 
374
        if(from_ip != 0) {
 
375
                rem[b].local[rem[b].num_locals - 1]->ip = *from_ip;
 
376
        }
 
377
        rem[b].local[rem[b].num_locals - 1]->port = from_port;
 
378
        rem[b].local[rem[b].num_locals - 1]->local_id = local_id;
 
379
        rem[b].die = get_time() + ((int64_t)timeout_seconds << 8);
 
380
        return 0;
 
381
}
 
382
 
 
383
/* Create a DNS header suitable for giving back to the client */
 
384
dw_str *make_dns_header(int32_t id, int16_t flags, int32_t ancount,
 
385
                        int32_t nscount, int32_t arcount) {
 
386
        dw_str *out = 0;
 
387
 
 
388
        if(id < 0 || ancount < 0 || nscount < 0 || arcount < 0) {
 
389
                return 0; /* Sanity check */
 
390
        }
 
391
 
 
392
        /* Destroyed after giving the client the reply */
 
393
        out = dw_create(515);
 
394
 
 
395
        /* Query ID; echo from client */
 
396
        if(dw_put_u16(out, id, -1) == -1) {
 
397
                goto catch_make_dns_header;
 
398
        }
 
399
 
 
400
        /* QR; Opcode; AA; TC; RD; RA; Z; RCODE */
 
401
        if(dw_put_u16(out, flags, -1) == -1) {
 
402
                goto catch_make_dns_header;
 
403
        }
 
404
 
 
405
        /* QDCOUNT = 1 */
 
406
        if(dw_put_u16(out,1,-1) == -1) {
 
407
                goto catch_make_dns_header;
 
408
        }
 
409
 
 
410
        /* And, finally, ANCOUNT, NSCOUNT, and ARCOUNT */
 
411
        if(dw_put_u16(out,ancount,-1) == -1 ||
 
412
           dw_put_u16(out,nscount,-1) == -1 ||
 
413
           dw_put_u16(out,arcount,-1) == -1) {
 
414
                goto catch_make_dns_header;
 
415
        }
 
416
 
 
417
        return out;
 
418
 
 
419
catch_make_dns_header:
 
420
        if(out != 0) {
 
421
                dw_destroy(out);
 
422
                out = 0;
 
423
        }
 
424
        return 0;
 
425
}
 
426
 
 
427
/* Given two dw_strings with the question and answer, make a DNS packet
 
428
 * we can give back to the client; this will multilate answer so be
 
429
 * careful */
 
430
dw_str *make_dns_packet(dw_str *question, dw_str *answer, int32_t id) {
 
431
        int32_t ancount = 0, nscount = 0, arcount = 0;
 
432
        int is_nxdomain = 0;
 
433
        dw_str *out = 0;
 
434
 
 
435
        is_nxdomain = dw_pop_u8(answer);
 
436
        arcount = dw_pop_u16(answer);
 
437
        nscount = dw_pop_u16(answer);
 
438
        ancount = dw_pop_u16(answer);
 
439
 
 
440
        if(is_nxdomain == 0) {
 
441
                /* 0x8180: QR = 1; Opcode = 0; AA = 0; TC = 0; RD = 1; RA = 1;
 
442
                 * Z = 0; RCODE = 0 */
 
443
                out = make_dns_header(id,0x8180,ancount,nscount,arcount);
 
444
        } else if(is_nxdomain == 1) {
 
445
                /* Same header as before, but with RCODE of "name error" */
 
446
                out = make_dns_header(id,0x8183,ancount,nscount,arcount);
 
447
        } else {
 
448
                goto catch_make_dns_packet;
 
449
        }
 
450
 
 
451
        if(dw_append(question,out) == -1 ||
 
452
           dw_put_u16(out,1,-1) == -1 || /* QCLASS: 1 */
 
453
           dw_append(answer,out) == -1) {
 
454
                goto catch_make_dns_packet;
 
455
        }
 
456
 
 
457
        return out;
 
458
 
 
459
catch_make_dns_packet:
 
460
        if(out != 0) {
 
461
                dw_destroy(out);
 
462
                out = 0;
 
463
        }
 
464
        return 0;
 
465
}
 
466
 
 
467
/* See if a given reply is in the cache; if so, send them the reply
 
468
 * from the cache */
 
469
int get_reply_from_cache(dw_str *query, sockaddr_all_T *client,
 
470
                         SOCKET sock, int32_t id, int resurrect,
 
471
                         int tcp_num) {
 
472
        dw_str *value = 0; /* Element in cache */
 
473
        dw_str *comp = 0; /* Compressed DNS packet */
 
474
        dw_str *packet = 0;
 
475
        socklen_t c_len = sizeof(struct sockaddr_in);
 
476
        int ret = -1;
 
477
 
 
478
#ifdef IPV6
 
479
        if (client->Family == AF_INET6) {
 
480
               c_len = sizeof(struct sockaddr_in6);
 
481
        }
 
482
#endif
 
483
 
 
484
        dwc_process(cache,query,3); /* RR rotation, TTL aging, etc. */
 
485
        value = dwh_get(cache,query,resurrect,1);
 
486
        comp = dwc_compress(query,value);
 
487
        if(comp == 0) {
 
488
                goto catch_get_reply_from_cache;
 
489
        }
 
490
 
 
491
        if(comp->len == 7) { /* Empty packet; workaround */
 
492
                dw_log_string("Warning: Removing empty packet from cache",11);
 
493
                dwh_zap(cache,query,0,1);
 
494
                goto catch_get_reply_from_cache;
 
495
        }
 
496
 
 
497
        dw_log_dwstr_str("Fetching ",query," from cache",100);
 
498
        packet = make_dns_packet(query,comp,id);
 
499
        if(packet == 0) {
 
500
                goto catch_get_reply_from_cache;
 
501
        }
 
502
 
 
503
        if(tcp_num == -1) {
 
504
                sendto(sock,(void *)packet->str,packet->len,0,
 
505
                       (struct sockaddr *)client, c_len);
 
506
        } else if(key_n[DWM_N_tcp_listen] == 1) {
 
507
                tcp_return_reply(tcp_num,(void *)packet->str,packet->len);
 
508
        }
 
509
 
 
510
        ret = 1;
 
511
 
 
512
catch_get_reply_from_cache:
 
513
        if(value != 0) {
 
514
                dw_destroy(value);
 
515
        }
 
516
        if(packet != 0) {
 
517
                dw_destroy(packet);
 
518
        }
 
519
        if(comp != 0) {
 
520
                dw_destroy(comp);
 
521
        }
 
522
        return ret;
 
523
}
 
524
 
 
525
/* Given a connection we will send on, try and send the connection on.
 
526
   If we're unable to send the connection on, see if we have an
 
527
   expired element with the data we want. */
 
528
void try_forward_local_udp_packet(SOCKET sock, int32_t local_id,
 
529
     ip_addr_T *from_ip, uint16_t from_port, unsigned char *packet,
 
530
     int len, sockaddr_all_T *client,dw_str *query, int tcp_num) {
 
531
 
 
532
        /* If not cached, get a reply that we will cache and send back to
 
533
         * the client */
 
534
        if(forward_local_udp_packet(sock,local_id,from_ip,from_port,
 
535
                                    packet,len,tcp_num) != -1) {
 
536
                return; /* Success! */
 
537
        }
 
538
 
 
539
        /* OK, at this point it failed so we'll see if we get a
 
540
         * "resurrected" cache entry */
 
541
        if(resurrections == 1 &&
 
542
           get_reply_from_cache(query,client,sock,local_id,1,tcp_num)
 
543
             == 1) {
 
544
                dw_log_string("Resurrected from cache",11);
 
545
                return; /* Resurrected entry; we're done */
 
546
        }
 
547
 
 
548
        if(handle_overload == 1) {
 
549
                send_server_fail(client,packet,len,sock,tcp_num);
 
550
        }
 
551
}
 
552
 
 
553
/* Get and process a local DNS request */
 
554
void get_local_udp_packet(SOCKET sock) {
 
555
        unsigned char packet[522];
 
556
        int len = 0;
 
557
        sockaddr_all_T client;
 
558
        socklen_t c_len = 0;
 
559
        ip_addr_T from_ip = {0,{0,0}};
 
560
        uint16_t from_port = 0;
 
561
        int32_t local_id = -1;
 
562
        dw_str *query = 0;
 
563
#ifdef VALGRIND_NOERRORS
 
564
        memset(packet,0,522);
 
565
#endif /* VALGRIND_NOERRORS */
 
566
 
 
567
        c_len = sizeof(client);
 
568
        make_socket_nonblock(sock); /* Linux bug workaround */
 
569
        len = recvfrom(sock,(void *)packet,520,0,(struct sockaddr *)&client,
 
570
                       &c_len);
 
571
 
 
572
        from_port = get_from_ip_port(&from_ip,&client);
 
573
 
 
574
        if(check_ip_acl(&from_ip) != 1) { /* Drop unauthorized packets */
 
575
                goto catch_get_local_udp_packet;
 
576
        }
 
577
 
 
578
        local_id = get_dns_qid(packet, len, 2);
 
579
        if(local_id < 0 || len < 13) { /* Invalid remote packet */
 
580
                goto catch_get_local_udp_packet;
 
581
        }
 
582
 
 
583
        /* See if we have something in the cache; destroyed at end of
 
584
         * function */
 
585
        query = dw_get_dname_type(packet,12,len);
 
586
        dw_log_dwstr("Got DNS query for ",query,100); /* Log it */
 
587
        if(query == 0) {
 
588
                goto catch_get_local_udp_packet;
 
589
        }
 
590
        if(get_reply_from_cache(query,&client,sock,local_id,0,-1)
 
591
                == 1) {
 
592
                goto catch_get_local_udp_packet; /* In cache; we're done */
 
593
        }
 
594
 
 
595
        /* Nothing in cache; lets try sending the packet upstream */
 
596
        try_forward_local_udp_packet(sock,local_id,&from_ip,from_port,
 
597
                        packet,len,&client,query,INVALID_SOCKET);
 
598
 
 
599
catch_get_local_udp_packet:
 
600
        if(query != 0) {
 
601
                dw_destroy(query);
 
602
                query = 0;
 
603
        }
 
604
}
 
605
 
 
606
/* Forward a remote reply back to the client */
 
607
void forward_remote_reply(unsigned char *packet, size_t len, remote_T *r_ip,
 
608
                int local_num) {
 
609
        sockaddr_all_T client;
 
610
        socklen_t len_inet = 0;
 
611
 
 
612
        if(r_ip == 0) {
 
613
                return;
 
614
        }
 
615
        if(r_ip->local[local_num]->from_socket == INVALID_SOCKET) {
 
616
                return;
 
617
        }
 
618
        memset(&client,0,sizeof(client));
 
619
        len_inet = sizeof(client);
 
620
 
 
621
        if (r_ip->local[local_num]->ip.len == 4) {
 
622
                client.V4.sin_family = AF_INET;
 
623
                client.V4.sin_port = htons(r_ip->local[local_num]->port);
 
624
                memcpy(&client.V4.sin_addr, r_ip->local[local_num]->ip.ip, 4);
 
625
                len_inet = sizeof(struct sockaddr_in);
 
626
#ifdef IPV6
 
627
        } else if(r_ip->local[local_num]->ip.len == 16) {
 
628
                client.V6.sin6_family = AF_INET6;
 
629
                client.V6.sin6_port = htons(r_ip->local[local_num]->port);
 
630
                memcpy(&client.V6.sin6_addr, r_ip->local[local_num]->ip.ip,
 
631
                        16);
 
632
                len_inet = sizeof(struct sockaddr_in6);
 
633
#endif
 
634
        } else {
 
635
                return;
 
636
        }
 
637
        sendto(r_ip->local[local_num]->from_socket,(void *)packet,len,0,
 
638
               (struct sockaddr *)&client,len_inet);
 
639
 
 
640
        /* Sometimes, especially if we use DNS-over-TCP, we will have a case
 
641
         * where some local IPs have been handled and others haven't.
 
642
         * "forward_remote_reply" finishes up a local connection to UDP and
 
643
         * is only sent once, so we close the socket to make it invalid */
 
644
        r_ip->local[local_num]->from_socket = INVALID_SOCKET;
 
645
}
 
646
 
 
647
/* Add a reply we have received from the remote (upstream) DNS server to
 
648
 * the cache */
 
649
int cache_dns_reply(unsigned char *packet, int count) {
 
650
        int32_t ttl = 60;
 
651
        int32_t ancount = 0;
 
652
        int is_nxdomain = 0;
 
653
        dw_str *question = 0, *answer = 0;
 
654
        dw_str *decomp = 0;
 
655
        int ret = -1;
 
656
 
 
657
        question = dw_get_dname_type(packet,12,count);
 
658
        dw_log_dwstr("Caching reply for ",question,100);
 
659
        if((packet[3] & 0x0f) == 3) {
 
660
                is_nxdomain = 1;
 
661
        }
 
662
        answer = dw_packet_to_cache(packet,count,is_nxdomain);
 
663
        decomp = dwc_decompress(question,answer);
 
664
        if(blacklist_dict != 0 && has_bad_ip(decomp,blacklist_dict)) {
 
665
                ret = -2; /* Tell caller we need synth "not there" */
 
666
                goto catch_cache_dns_reply;
 
667
        }
 
668
        ancount = dw_cachepacket_to_ancount(answer);
 
669
        if(ancount == 0) {
 
670
                ancount = 32; /* So we can correctly cache negative answers */
 
671
        }
 
672
 
 
673
        if(question == 0 || answer == 0 || ancount == -1) {
 
674
                goto catch_cache_dns_reply;
 
675
        }
 
676
 
 
677
        ttl = dw_get_a_dnsttl(answer,0,31536000,ancount);
 
678
 
 
679
        if(ttl == -1) {
 
680
                goto catch_cache_dns_reply;
 
681
        }
 
682
 
 
683
        /* Physically put the data in the cache */
 
684
        dwh_add(cache,question,decomp,ttl,1);
 
685
        ret = 1;
 
686
 
 
687
catch_cache_dns_reply:
 
688
        if(question != 0) {
 
689
                dw_destroy(question);
 
690
        }
 
691
        if(answer != 0) {
 
692
                dw_destroy(answer);
 
693
        }
 
694
        if(decomp != 0) {
 
695
                dw_destroy(decomp);
 
696
        }
 
697
        return ret;
 
698
}
 
699
 
 
700
/* Verify that a given DNS packet is good (The Query ID is correct, the
 
701
   query in the "question" section of the DNS header is good) */
 
702
int verify_dns_packet(int b, unsigned char *packet, int len) {
 
703
        int ret = 0;
 
704
        dw_str *question = 0;
 
705
 
 
706
        /* Make sure the ID we got is the same as the one we originally
 
707
         * sent them */
 
708
        if(get_dns_qid(packet,len,0) != rem[b].remote_id) {
 
709
                goto catch_verify_dns_packet;
 
710
        }
 
711
 
 
712
        question = dw_get_dname_type(packet,12,len);
 
713
        if(question == 0) {
 
714
                goto catch_verify_dns_packet;
 
715
        }
 
716
 
 
717
        /* Should we make this case-insensitive? Probably not. */
 
718
        if(dw_issame(question,rem[b].query) != 1) {
 
719
                goto catch_verify_dns_packet;
 
720
        }
 
721
 
 
722
        ret = 1;
 
723
 
 
724
catch_verify_dns_packet:
 
725
        if(question != 0) {
 
726
                dw_destroy(question);
 
727
                question = 0;
 
728
        }
 
729
        return ret;
 
730
}
 
731
 
 
732
/* Make the actual answer for a synthetic "not there" reply */
 
733
unsigned char *make_synth_not_there_answer(unsigned char *a, int count) {
 
734
        /* This is the answer for a "not there" reply */
 
735
        unsigned char not_there[41] =
 
736
        "\xc0\x0c" /* Name */
 
737
        "\0\x06" /* Type */
 
738
        "\0\x01" /* Class */
 
739
        "\0\0\0\0" /* TTL (don't cache) */
 
740
        "\0\x1c" /* RDLENGTH */
 
741
        "\x01\x7a\xc0\x0c" /* Origin */
 
742
        "\x01\x79\xc0\x0c" /* Email */
 
743
        "\0\0\0\x01\0\0\0\x01\0\0\0\x01\0\0\0\x01\0\0\0\x01" /* 5 numbers */;
 
744
        unsigned char *answer = 0;
 
745
        int counter = 0;
 
746
 
 
747
        answer = malloc(count + 43);
 
748
        /* Copy the header they sent us to our reply */
 
749
        for(counter = 0; counter < 12 && counter < count; counter++) {
 
750
                answer[counter] = a[counter];
 
751
        }
 
752
 
 
753
        /* Copy the question over to the reply */
 
754
        for(;counter < 520 && counter < count; counter++) {
 
755
                if(a[counter] == 0) {
 
756
                        break; /* Quick and dirty "end of name"; yes, I
 
757
                                * check in dw_get_dname_type() to make sure
 
758
                                * there is no ASCII NULL in names */
 
759
                }
 
760
                answer[counter] = a[counter];
 
761
        }
 
762
        if(count < counter + 5 || counter > 512) { /* Sanity check */
 
763
                free(answer);
 
764
                return 0;
 
765
        }
 
766
 
 
767
        /* Add the rest of the question */
 
768
        count = counter + 5;
 
769
        for(;counter < count; counter++) {
 
770
                answer[counter] = a[counter];
 
771
        }
 
772
 
 
773
        /* Add the SOA reply to the answer */
 
774
        for(counter = 0; counter < 40; counter++) {
 
775
                answer[count + counter] = not_there[counter];
 
776
        }
 
777
 
 
778
        /* Return the answer */
 
779
        return answer;
 
780
}
 
781
 
 
782
/* Make a synthetic "not there" reply */
 
783
void make_synth_not_there(int b, SOCKET sock, unsigned char *a, int count) {
 
784
        unsigned char *answer = 0;
 
785
        int local_num = 0;
 
786
 
 
787
        if(a == 0 || count < 12 || rem[b].local == 0) {
 
788
                return;
 
789
        }
 
790
 
 
791
        /* Copy original header and question in to answer */
 
792
        answer = make_synth_not_there_answer(a,count);
 
793
        if(answer == 0) {
 
794
                return;
 
795
        }
 
796
 
 
797
        /* Flag this as an answer */
 
798
        answer[2] |= 0x80;
 
799
 
 
800
        /* One "NS" record; no other records */
 
801
        answer[9] = 1;
 
802
        answer[6] = answer[7] = answer[8] = answer[10] = answer[11] = 0;
 
803
 
 
804
        /* Send the reply(s) */
 
805
        for(local_num = 0; local_num < rem[b].num_locals; local_num++) {
 
806
                /* Copy ID over */
 
807
                answer[0] = (rem[b].local[local_num]->local_id >> 8) & 0xff;
 
808
                answer[1] = (rem[b].local[local_num]->local_id) & 0xff;
 
809
                /* Send this reply */
 
810
                if(rem[b].local[local_num]->tcp_num == -1) {
 
811
                        forward_remote_reply(answer,count + 40, &rem[b],
 
812
                                local_num);
 
813
                } else {
 
814
                        tcp_return_reply(rem[b].local[local_num]->tcp_num,
 
815
                                (void *)answer, count);
 
816
                }
 
817
        }
 
818
 
 
819
        /* Reset the pending remote connection */
 
820
        closesocket(b_remote[b]);
 
821
        b_remote[b] = -1;
 
822
        reset_rem(b);
 
823
 
 
824
        /* Free allocated memory */
 
825
        free(answer);
 
826
}
 
827
 
 
828
/* Core part of code that gets and processes remote DNS requests */
 
829
 
 
830
int get_rem_udp_packet_core(unsigned char *a, ssize_t count,
 
831
                int b, SOCKET sock, int l) {
 
832
 
 
833
        int cache_dns_reply_return_value = -1;
 
834
 
 
835
        if(count < 12) {
 
836
                return -1; /* Not a DNS packet at all */
 
837
        }
 
838
 
 
839
        if((a[2] & 0x02) == 0x00) { /* If not truncated */
 
840
                fflush(stdout);
 
841
                cache_dns_reply_return_value = cache_dns_reply(a,count);
 
842
                if(cache_dns_reply_return_value == -2) { /* Make synth NX */
 
843
                        make_synth_not_there(b,sock,a,count);
 
844
                        return -1; /* Bad reply and they got a Synth NX */
 
845
                }
 
846
                if(cache_dns_reply_return_value < 0 && deliver_all == 0) {
 
847
                        return -1; /* Bad reply */
 
848
                }
 
849
        } else if(rem[b].local[l]->tcp_num != -1 &&
 
850
                        key_n[DWM_N_tcp_listen] == 1) {
 
851
                /* Send a DNS-over-TCP packet to handle a truncated reply */
 
852
                tcp_truncated_retry(rem[b].local[l]->tcp_num, rem[b].query,
 
853
                        rem[b].local[l]->local_id);
 
854
                /* Give the UDP connection more time before timeout, so
 
855
                 * we can fully process the TCP connection */
 
856
                rem[b].die = get_time() + ((int64_t)timeout_seconds << 10);
 
857
                return 2; /* Don't kill pending UDP connection */
 
858
        }
 
859
 
 
860
        /* Now make sure the ID is the same as the one the client
 
861
         * originally sent us */
 
862
        set_dns_qid(a,count,rem[b].local[l]->local_id);
 
863
 
 
864
        /* Send the answer we got to the appropriate local connection */
 
865
        if(rem[b].local[l]->tcp_num == -1 ) {
 
866
                forward_remote_reply(a,count,&rem[b], l);
 
867
        } else if(key_n[DWM_N_tcp_listen] == 1) {
 
868
                tcp_return_reply(rem[b].local[l]->tcp_num,(void *)a,count);
 
869
        }
 
870
 
 
871
        return 1;
 
872
}
 
873
 
 
874
/* Get and process a remote DNS request */
 
875
void get_remote_udp_packet(int b, SOCKET sock) {
 
876
        ssize_t count;
 
877
        unsigned char a[520];
 
878
        int l = 0, kill = 1, core_ret = 0;
 
879
 
 
880
        count = recv(sock,a,514,0);
 
881
        if(count < 12 || count > 512) {
 
882
                return;
 
883
        }
 
884
        a[2] |= 0x80; /* Flag this as an answer (just in case they didn't) */
 
885
 
 
886
        /* Make reasonably sure this is the reply to their question */
 
887
        if(verify_dns_packet(b,a,count) != 1) {
 
888
                return;
 
889
        }
 
890
 
 
891
        /* Having this be able to handle multiple in-flight requests
 
892
         * is hairy because we have to handle both DNS-over-UDP and
 
893
         * DNS-over-TCP */
 
894
 
 
895
        for(l = 0; l < rem[b].num_locals; l++) {
 
896
                core_ret = get_rem_udp_packet_core(a,count,b,sock,l);
 
897
                if(core_ret == -1) {
 
898
                        return;
 
899
                } else if(core_ret == 2) {
 
900
                        kill = 0;
 
901
                }
 
902
        }
 
903
 
 
904
        /* Reset this pending remote connection if needed */
 
905
        if(kill == 1) {
 
906
                closesocket(b_remote[b]);
 
907
                b_remote[b] = -1;
 
908
                reset_rem(b);
 
909
        }
 
910
}
 
911
 
 
912
/* Send a server failure back to the client when there is no reply from
 
913
 * the upstream server.  Input: The pending remote connection number. */
 
914
void server_fail_noreply(int a, int local_num) {
 
915
        dw_str *packet = 0;
 
916
 
 
917
        /* 0x8182: QR = 1; Opcode = 0; AA = 0; TC = 0; RD = 1; RA = 1;
 
918
         *         Z = 0; RCODE = "server fail" (2) */
 
919
        packet = make_dns_header(rem[a].local[local_num]->local_id,0x8182,
 
920
                        0,0,0);
 
921
 
 
922
        dw_log_dwstr("Sending SERVER FAIL for query ",rem[a].query,100);
 
923
 
 
924
        if(dw_append(rem[a].query,packet) == -1 ||
 
925
           dw_put_u16(packet,1,-1) == -1 /* QCLASS: 1 */) {
 
926
                goto catch_server_fail_noreply;
 
927
        }
 
928
 
 
929
        if(rem[a].local[local_num]->tcp_num == -1) {
 
930
                forward_remote_reply((unsigned char *)packet->str,
 
931
                        packet->len,&rem[a],0);
 
932
        } else if(key_n[DWM_N_tcp_listen] == 1) {
 
933
                tcp_return_reply(rem[a].local[local_num]->tcp_num,
 
934
                        (void *)packet->str,packet->len);
 
935
        }
 
936
 
 
937
catch_server_fail_noreply:
 
938
        if(packet != 0) {
 
939
                dw_destroy(packet);
 
940
                packet = 0;
 
941
        }
 
942
}
 
943