1 /*
2 * udp.c - handle upd connections
3 *
4 * Copyright (C) 1999 Brad M. Garcia <garsh@home.com>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19 */
20
21 #ifdef HAVE_CONFIG_H
22 #include <config.h>
23 #endif
24 #include <errno.h>
25 #include <sys/types.h>
26 #include <sys/socket.h>
27 #include <netinet/in.h>
28 #include <arpa/inet.h>
29 #include <syslog.h>
30 #include <string.h>
31 #include <time.h>
32 #include <assert.h>
33 #include "common.h"
34 #include "relay.h"
35 #include "cache.h"
36 #include "query.h"
37 #include "domnode.h"
38 #include "check.h"
39 #include "dns.h"
40
41 #ifndef EXCLUDE_MASTER
42 #include "master.h"
43 #endif
44
45 /*
46 * dnssend() 22OCT99wzk
47 *
48 * Abstract: A small wrapper for send()/sendto(). If an error occurs a
49 * message is written to syslog.
50 *
51 * Returns: The return code from sendto().
52 */
udp_send(int sock,srvnode_t * srv,void * msg,int len)53 static int udp_send(int sock, srvnode_t *srv, void *msg, int len)
54 {
55 int rc;
56 time_t now = time(NULL);
57 rc = sendto(sock, msg, len, 0,
58 (const struct sockaddr *) &srv->addr,
59 sizeof(struct sockaddr_in));
60
61 if (rc != len) {
62 log_msg(LOG_ERR, "sendto error: %s: ",
63 inet_ntoa(srv->addr.sin_addr), strerror(errno));
64 return (rc);
65 }
66 if ((srv->send_time == 0)) srv->send_time = now;
67 srv->send_count++;
68 return (rc);
69 }
70
send2current(query_t * q,void * msg,const int len)71 int send2current(query_t *q, void *msg, const int len) {
72 /* If we have domains associated with our servers, send it to the
73 appropriate server as determined by srvr */
74 domnode_t *d;
75 assert(q != NULL);
76 assert(q->domain != NULL);
77
78 d = q->domain;
79 while ((d->current != NULL) && (udp_send(q->sock, d->current, msg, len) != len)) {
80 if (reactivate_interval) deactivate_current(d);
81 }
82 if (d->current != NULL) {
83 return len;
84 } else return 0;
85 }
86
87
88 /*
89 * handle_udprequest()
90 *
91 * This function handles udp DNS requests by either replying to them (if we
92 * know the correct reply via master, caching, etc.), or forwarding them to
93 * an appropriate DNS server.
94 */
udp_handle_request()95 query_t *udp_handle_request()
96 {
97 unsigned addr_len;
98 int len;
99 const int maxsize = UDP_MAXSIZE;
100 static char msg[UDP_MAXSIZE+4];
101 struct sockaddr_in from_addr;
102 int fwd;
103 domnode_t *dptr;
104 query_t *q, *prev;
105
106 /* Read in the message */
107 addr_len = sizeof(struct sockaddr_in);
108 len = recvfrom(isock, msg, maxsize, 0,
109 (struct sockaddr *)&from_addr, &addr_len);
110 if (len < 0) {
111 log_debug(1, "recvfrom error %s", strerror(errno));
112 return NULL;
113 }
114
115 /* do some basic checking */
116 if (check_query(msg, len) < 0) return NULL;
117
118 /* Determine how query should be handled */
119 if ((fwd = handle_query(&from_addr, msg, &len, &dptr)) < 0)
120 return NULL; /* if its bogus, just ignore it */
121
122 /* If we already know the answer, send it and we're done */
123 if (fwd == 0) {
124 if (sendto(isock, msg, len, 0, (const struct sockaddr *)&from_addr,
125 addr_len) != len) {
126 log_debug(1, "sendto error %s", strerror(errno));
127 }
128 return NULL;
129 }
130
131
132 /* dptr->current should never be NULL it is checked in handle_query */
133
134 // dnsquery_add(&from_addr, msg, len);
135 // if (!send2current(dptr, msg, len)) {
136
137 /* rewrite msg, get id and add to list*/
138
139 if ((prev=query_add(dptr, dptr->current, &from_addr, msg, len)) == NULL){
140 /* of some reason we could not get any new queries. we have to
141 drop this packet */
142 return NULL;
143 }
144 q = prev->next;
145
146
147 if (send2current(q, msg, len) > 0) {
148 /* add to query list etc etc */
149 return q;
150 } else {
151
152 /* we couldn't send the query */
153 #ifndef EXCLUDE_MASTER
154 int packetlen;
155 char packet[maxsize+4];
156
157 /*
158 * If we couldn't send the packet to our DNS servers,
159 * perhaps the `network is unreachable', we tell the
160 * client that we are unable to process his request
161 * now. This will show a `No address (etc.) records
162 * available for host' in nslookup. With this the
163 * client won't wait hang around till he gets his
164 * timeout.
165 * For this feature dnrd has to run on the gateway
166 * machine.
167 */
168
169 if ((packetlen = master_dontknow(msg, len, packet)) > 0) {
170 query_delete_next(prev);
171 return NULL;
172 if (sendto(isock, msg, len, 0, (const struct sockaddr *)&from_addr,
173 addr_len) != len) {
174 log_debug(1, "sendto error %s", strerror(errno));
175 return NULL;
176 }
177 }
178 #endif
179 }
180 return q;
181 }
182
183 /*
184 * dnsrecv() 22OCT99wzk
185 *
186 * Abstract: A small wrapper for recv()/recvfrom() with output of an
187 * error message if needed.
188 *
189 * Returns: A positove number indicating of the bytes received, -1 on a
190 * recvfrom error and 0 if the received message is too large.
191 */
reply_recv(query_t * q,void * msg,int len)192 static int reply_recv(query_t *q, void *msg, int len)
193 {
194 int rc, fromlen;
195 struct sockaddr_in from;
196
197 fromlen = sizeof(struct sockaddr_in);
198 rc = recvfrom(q->sock, msg, len, 0,
199 (struct sockaddr *) &from, &fromlen);
200
201 if (rc == -1) {
202 log_msg(LOG_ERR, "recvfrom error: %s",
203 inet_ntoa(q->srv->addr.sin_addr));
204 return (-1);
205 }
206 else if (rc > len) {
207 log_msg(LOG_NOTICE, "packet too large: %s",
208 inet_ntoa(q->srv->addr.sin_addr));
209 return (0);
210 }
211 else if (memcmp(&from.sin_addr, &q->srv->addr.sin_addr,
212 sizeof(from.sin_addr)) != 0) {
213 log_msg(LOG_WARNING, "unexpected server: %s",
214 inet_ntoa(from.sin_addr));
215 return (0);
216 }
217
218 return (rc);
219 }
220
221 /*
222 * handle_udpreply()
223 *
224 * This function handles udp DNS requests by either replying to them (if we
225 * know the correct reply via master, caching, etc.), or forwarding them to
226 * an appropriate DNS server.
227 *
228 * Note that the mached query is prev->next and not prev.
229 */
udp_handle_reply(query_t * prev)230 void udp_handle_reply(query_t *prev)
231 {
232 // const int maxsize = 512; /* According to RFC 1035 */
233 static char msg[UDP_MAXSIZE+4];
234 int len;
235 unsigned addr_len;
236 query_t *q = prev->next;
237
238 log_debug(3, "handling socket %i", q->sock);
239 if ((len = reply_recv(q, msg, UDP_MAXSIZE)) < 0)
240 {
241 log_debug(1, "dnsrecv failed: %i", len);
242 query_delete_next(prev);
243 return; /* recv error */
244 }
245
246 /* do basic checking */
247 if (check_reply(q->srv, msg, len) < 0) {
248 log_debug(1, "check_reply failed");
249 query_delete_next(prev);
250 return;
251 }
252
253 if (opt_debug) {
254 char buf[256];
255 snprintf_cname(msg, len, 12, buf, sizeof(buf));
256 log_debug(3, "Received DNS reply for \"%s\"", buf);
257 }
258
259 dump_dnspacket("reply", msg, len);
260 addr_len = sizeof(struct sockaddr_in);
261
262 /* was this a dummy reactivate query? */
263 if (q->domain != NULL) {
264 /* no, lets cache the reply and send to client */
265 cache_dnspacket(msg, len, q->srv);
266
267 /* set the client qid */
268 *((unsigned short *)msg) = q->client_qid;
269 log_debug(3, "Forwarding the reply to the host %s",
270 inet_ntoa(q->client.sin_addr));
271 if (sendto(isock, msg, len, 0,
272 (const struct sockaddr *)&q->client,
273 addr_len) != len) {
274 log_debug(1, "sendto error %s", strerror(errno));
275 }
276 } else {
277 log_debug(2, "We got a reactivation dummy reply. Cool!");
278 }
279
280 /* this server is obviously alive, we reset the counters */
281 q->srv->send_time = 0;
282 if (q->srv->inactive) log_debug(1, "Reactivating server %s",
283 inet_ntoa(q->srv->addr.sin_addr));
284 q->srv->inactive = 0;
285 /* remove query from list and destroy it */
286 query_delete_next(prev);
287 }
288
289
290 /* send a dummy packet to a deactivated server to check if its back*/
udp_send_dummy(srvnode_t * s)291 int udp_send_dummy(srvnode_t *s) {
292 static unsigned char dnsbuf[] = {
293 /* HEADER */
294 /* we send a lookup for localhost */
295 /* will this work on a big endian system? */
296 0x00, 0x00, /* ID */
297 0x01, 0x00, /* QR|OC|AA|TC|RD - RA|Z|RCODE */
298 0x00, 0x01, /* QDCOUNT */
299 0x00, 0x00, /* ANCOUNT */
300 0x00, 0x00, /* NSCOUNT */
301 0x00, 0x00, /* ARCOUNT */
302
303 9, 'l','o','c','a','l','h','o','s','t',0, /* QNAME */
304 0x00,0x01, /* QTYPE A record */
305 0x00,0x01 /* QCLASS: IN */
306
307 /* in case you want to lookup root servers instead, use this: */
308 /* 0x00, */ /* QNAME: empty */
309 /* 0x00, 0x02, */ /* QTYPE: a authorative name server */
310 /* 0x00, 0x01 */ /* QCLASS: IN */
311 };
312 query_t *q;
313 struct sockaddr_in srcaddr;
314
315 /* should not happen */
316 assert(s != NULL);
317
318 if ((q=query_add(NULL, s, &srcaddr, dnsbuf, sizeof(dnsbuf))) != NULL) {
319 int rc;
320 q = q->next; /* query add returned the query 1 before in list */
321 /* don't let those queries live too long */
322 q->ttl = reactivate_interval;
323 memset(&srcaddr, 0, sizeof(srcaddr));
324 log_debug(2, "Sending dummy id=%i to %s", ((unsigned short *)dnsbuf)[0],
325 inet_ntoa(s->addr.sin_addr));
326 /* return dnssend(s, &dnsbuf, sizeof(dnsbuf)); */
327 rc=udp_send(q->sock, s, dnsbuf, sizeof(dnsbuf));
328 ((unsigned short *)dnsbuf)[0]++;
329 return rc;
330 }
331 return -1;
332 }
333