1
/* Copyright (c) 2007-2009 Sam Trenholme and others
5
* Redistribution and use in source and binary forms, with or without
6
* modification, are permitted provided that the following conditions
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.
15
* This software is provided 'as is' with no guarantees of correctness or
16
* fitness for purpose.
20
#include "DwTcpSocket.h"
21
#include "DwCompress.h"
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[];
29
/* Parameters set in DwSys.c */
30
extern int64_t the_time;
31
extern dwr_rg *rng_seed;
32
extern dw_hash *cache;
34
/* List of addresses we will bind to */
35
extern ip_addr_T bind_address[];
36
extern ip_addr_T upstream_address[];
38
/* List of active sockets */
39
extern SOCKET b_local[];
40
extern SOCKET *b_remote;
42
/* The list of pending remote connections */
45
/* The list of active in-flight connections */
48
/* The numeric mararc parameters */
50
extern int timeout_seconds;
52
extern int upstream_port;
53
extern int handle_overload;
54
extern int resurrections;
57
extern int num_retries;
58
extern int deliver_all;
60
/* Other mararc parameters */
61
dwd_dict *blacklist_dict;
64
/* Needed for the Windows way of making a socket non-blocking */
65
extern u_long dont_block;
68
/* Initialize the inflight hash */
69
void init_inflight_hash() {
71
if(key_n[DWM_N_max_inflights] < 2) {
72
return; /* No inflight merge desired */
74
inflight = dwh_hash_init(key_n[DWM_N_maxprocs] + 10);
77
/* The upstream server we will connect to (round robin rotated) */
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 */
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) {
91
b_local[a] = INVALID_SOCKET;
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) {
103
memset(dns_udp,0,sizeof(dns_udp));
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);
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);
114
} else { /* Bad ip */
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;
126
socklen_t len_inet = sizeof(struct sockaddr_in);
128
for(a = 0; a < 10; a++) {
129
/* Set up random source port to bind to */
131
min_bind + (dwr_rng(rng_seed) & num_ports), len);
133
if (dns_udp.Family == AF_INET6)
134
len_inet = sizeof(struct sockaddr_in6);
136
/* Try to bind to that port */
137
if(bind(s, (struct sockaddr *)&dns_udp,
143
if(success == 0) { /* Bind to random port failed */
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;
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);
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);
171
return INVALID_SOCKET;
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,
180
sockaddr_all_T server;
183
ip_addr_T addr = {0,{0,0}};
184
socklen_t inet_len = sizeof(struct sockaddr_in);
186
if(rem[n].socket != INVALID_SOCKET) { /* Already used (sanity check) */
190
/* Get a random query ID to send to the remote server */
191
rnum = set_dns_qid(packet,len,dwr_rng(rng_seed));
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 */
204
if (server.Family == AF_INET6)
205
inet_len = sizeof(struct sockaddr_in6);
208
make_socket_nonblock(s); /* Linux kernel bug */
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)) {
218
/* OK, now note the pending connection */
221
rem[n].remote_id = rnum;
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
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);
237
if (client->Family == AF_INET6)
238
c_len = sizeof(struct sockaddr_in6);
240
a[2] |= 0x80; /* Set QR (reply, not question) */
241
a[3] &= 0xf0; a[3] |= 0x02; /* Set RCODE */
244
sendto(sock,(void *)a,len,0,(struct sockaddr *)client, c_len);
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
254
int find_inflight_query(unsigned char *a, int len) {
255
dw_str *query = 0, *answer = 0;
258
if(len < 12 || a == 0 || key_n[DWM_N_max_inflights] < 2) {
259
goto catch_find_inflight_query;
262
query = dw_get_dname_type(a,12,len);
264
goto catch_find_inflight_query;
267
answer = dwh_get(inflight, query, 0, 1);
268
if(answer == 0) { /* Nothing in-flight */
269
goto catch_find_inflight_query;
272
ret = dw_fetch_u16(answer,0);
274
if(rem[ret].socket == INVALID_SOCKET) { /* Not valid query */
276
goto catch_find_inflight_query;
279
if(dw_issame(query,rem[ret].query) != 1) { /* Actually not same */
280
dwh_zap(inflight,query, 0, 1); /* Remove corrupt data */
284
catch_find_inflight_query:
294
void zap_inflight(dw_str *query) {
295
if(key_n[DWM_N_max_inflights] > 1) {
296
dwh_zap(inflight,query, 0, 1);
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;
305
/* Make a new remote connection */
306
if(rem[b].socket != INVALID_SOCKET) { /* Sanity check */
309
rem[b].query = dw_get_dname_type(a,12,len);
310
make_remote_connection(b,a,len,rem[b].query);
312
rem[b].local = malloc(num_alloc * sizeof(local_T *));
313
for(counter = 0; counter < num_alloc; counter++) {
314
rem[b].local[counter] = 0;
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 */
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) {
339
num_alloc = key_n[DWM_N_max_inflights];
342
} else if(num_alloc > 32000) {
345
num_alloc++; /* Stop off-by-one attacks */
347
b = find_inflight_query(a,len);
349
b = find_free_remote();
350
if(b == -1) { /* We're out of remote pending connections */
352
} else { /* Create new connection */
354
make_new_udp_connect(b,a,len,num_alloc);
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 */
361
#ifdef INFLIGHT_VERBOSE
362
printf("Connection %d has %d locals\n",b,rem[b].num_locals);
364
#endif /* INFLIGHT_VERBOSE */
366
if(rem[b].socket == INVALID_SOCKET) {
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;
375
rem[b].local[rem[b].num_locals - 1]->ip = *from_ip;
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);
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) {
388
if(id < 0 || ancount < 0 || nscount < 0 || arcount < 0) {
389
return 0; /* Sanity check */
392
/* Destroyed after giving the client the reply */
393
out = dw_create(515);
395
/* Query ID; echo from client */
396
if(dw_put_u16(out, id, -1) == -1) {
397
goto catch_make_dns_header;
400
/* QR; Opcode; AA; TC; RD; RA; Z; RCODE */
401
if(dw_put_u16(out, flags, -1) == -1) {
402
goto catch_make_dns_header;
406
if(dw_put_u16(out,1,-1) == -1) {
407
goto catch_make_dns_header;
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;
419
catch_make_dns_header:
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
430
dw_str *make_dns_packet(dw_str *question, dw_str *answer, int32_t id) {
431
int32_t ancount = 0, nscount = 0, arcount = 0;
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);
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);
448
goto catch_make_dns_packet;
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;
459
catch_make_dns_packet:
467
/* See if a given reply is in the cache; if so, send them the reply
469
int get_reply_from_cache(dw_str *query, sockaddr_all_T *client,
470
SOCKET sock, int32_t id, int resurrect,
472
dw_str *value = 0; /* Element in cache */
473
dw_str *comp = 0; /* Compressed DNS packet */
475
socklen_t c_len = sizeof(struct sockaddr_in);
479
if (client->Family == AF_INET6) {
480
c_len = sizeof(struct sockaddr_in6);
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);
488
goto catch_get_reply_from_cache;
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;
497
dw_log_dwstr_str("Fetching ",query," from cache",100);
498
packet = make_dns_packet(query,comp,id);
500
goto catch_get_reply_from_cache;
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);
512
catch_get_reply_from_cache:
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) {
532
/* If not cached, get a reply that we will cache and send back to
534
if(forward_local_udp_packet(sock,local_id,from_ip,from_port,
535
packet,len,tcp_num) != -1) {
536
return; /* Success! */
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)
544
dw_log_string("Resurrected from cache",11);
545
return; /* Resurrected entry; we're done */
548
if(handle_overload == 1) {
549
send_server_fail(client,packet,len,sock,tcp_num);
553
/* Get and process a local DNS request */
554
void get_local_udp_packet(SOCKET sock) {
555
unsigned char packet[522];
557
sockaddr_all_T client;
559
ip_addr_T from_ip = {0,{0,0}};
560
uint16_t from_port = 0;
561
int32_t local_id = -1;
563
#ifdef VALGRIND_NOERRORS
564
memset(packet,0,522);
565
#endif /* VALGRIND_NOERRORS */
567
c_len = sizeof(client);
568
make_socket_nonblock(sock); /* Linux bug workaround */
569
len = recvfrom(sock,(void *)packet,520,0,(struct sockaddr *)&client,
572
from_port = get_from_ip_port(&from_ip,&client);
574
if(check_ip_acl(&from_ip) != 1) { /* Drop unauthorized packets */
575
goto catch_get_local_udp_packet;
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;
583
/* See if we have something in the cache; destroyed at end of
585
query = dw_get_dname_type(packet,12,len);
586
dw_log_dwstr("Got DNS query for ",query,100); /* Log it */
588
goto catch_get_local_udp_packet;
590
if(get_reply_from_cache(query,&client,sock,local_id,0,-1)
592
goto catch_get_local_udp_packet; /* In cache; we're done */
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);
599
catch_get_local_udp_packet:
606
/* Forward a remote reply back to the client */
607
void forward_remote_reply(unsigned char *packet, size_t len, remote_T *r_ip,
609
sockaddr_all_T client;
610
socklen_t len_inet = 0;
615
if(r_ip->local[local_num]->from_socket == INVALID_SOCKET) {
618
memset(&client,0,sizeof(client));
619
len_inet = sizeof(client);
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);
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,
632
len_inet = sizeof(struct sockaddr_in6);
637
sendto(r_ip->local[local_num]->from_socket,(void *)packet,len,0,
638
(struct sockaddr *)&client,len_inet);
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;
647
/* Add a reply we have received from the remote (upstream) DNS server to
649
int cache_dns_reply(unsigned char *packet, int count) {
653
dw_str *question = 0, *answer = 0;
657
question = dw_get_dname_type(packet,12,count);
658
dw_log_dwstr("Caching reply for ",question,100);
659
if((packet[3] & 0x0f) == 3) {
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;
668
ancount = dw_cachepacket_to_ancount(answer);
670
ancount = 32; /* So we can correctly cache negative answers */
673
if(question == 0 || answer == 0 || ancount == -1) {
674
goto catch_cache_dns_reply;
677
ttl = dw_get_a_dnsttl(answer,0,31536000,ancount);
680
goto catch_cache_dns_reply;
683
/* Physically put the data in the cache */
684
dwh_add(cache,question,decomp,ttl,1);
687
catch_cache_dns_reply:
689
dw_destroy(question);
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) {
704
dw_str *question = 0;
706
/* Make sure the ID we got is the same as the one we originally
708
if(get_dns_qid(packet,len,0) != rem[b].remote_id) {
709
goto catch_verify_dns_packet;
712
question = dw_get_dname_type(packet,12,len);
714
goto catch_verify_dns_packet;
717
/* Should we make this case-insensitive? Probably not. */
718
if(dw_issame(question,rem[b].query) != 1) {
719
goto catch_verify_dns_packet;
724
catch_verify_dns_packet:
726
dw_destroy(question);
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 */
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;
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];
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 */
760
answer[counter] = a[counter];
762
if(count < counter + 5 || counter > 512) { /* Sanity check */
767
/* Add the rest of the question */
769
for(;counter < count; counter++) {
770
answer[counter] = a[counter];
773
/* Add the SOA reply to the answer */
774
for(counter = 0; counter < 40; counter++) {
775
answer[count + counter] = not_there[counter];
778
/* Return the answer */
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;
787
if(a == 0 || count < 12 || rem[b].local == 0) {
791
/* Copy original header and question in to answer */
792
answer = make_synth_not_there_answer(a,count);
797
/* Flag this as an answer */
800
/* One "NS" record; no other records */
802
answer[6] = answer[7] = answer[8] = answer[10] = answer[11] = 0;
804
/* Send the reply(s) */
805
for(local_num = 0; local_num < rem[b].num_locals; local_num++) {
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],
814
tcp_return_reply(rem[b].local[local_num]->tcp_num,
815
(void *)answer, count);
819
/* Reset the pending remote connection */
820
closesocket(b_remote[b]);
824
/* Free allocated memory */
828
/* Core part of code that gets and processes remote DNS requests */
830
int get_rem_udp_packet_core(unsigned char *a, ssize_t count,
831
int b, SOCKET sock, int l) {
833
int cache_dns_reply_return_value = -1;
836
return -1; /* Not a DNS packet at all */
839
if((a[2] & 0x02) == 0x00) { /* If not truncated */
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 */
846
if(cache_dns_reply_return_value < 0 && deliver_all == 0) {
847
return -1; /* Bad reply */
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 */
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);
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);
874
/* Get and process a remote DNS request */
875
void get_remote_udp_packet(int b, SOCKET sock) {
877
unsigned char a[520];
878
int l = 0, kill = 1, core_ret = 0;
880
count = recv(sock,a,514,0);
881
if(count < 12 || count > 512) {
884
a[2] |= 0x80; /* Flag this as an answer (just in case they didn't) */
886
/* Make reasonably sure this is the reply to their question */
887
if(verify_dns_packet(b,a,count) != 1) {
891
/* Having this be able to handle multiple in-flight requests
892
* is hairy because we have to handle both DNS-over-UDP and
895
for(l = 0; l < rem[b].num_locals; l++) {
896
core_ret = get_rem_udp_packet_core(a,count,b,sock,l);
899
} else if(core_ret == 2) {
904
/* Reset this pending remote connection if needed */
906
closesocket(b_remote[b]);
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) {
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,
922
dw_log_dwstr("Sending SERVER FAIL for query ",rem[a].query,100);
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;
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);
937
catch_server_fail_noreply: