]>
Commit | Line | Data |
---|---|---|
7b724b86 | 1 | |
2 | /* | |
ef523f99 | 3 | * $Id: dns_internal.cc,v 1.4 1999/04/16 01:00:51 wessels Exp $ |
7b724b86 | 4 | * |
5 | * DEBUG: section 78 DNS lookups; interacts with lib/rfc1035.c | |
6 | * AUTHOR: Duane Wessels | |
7 | * | |
8 | * SQUID Internet Object Cache http://squid.nlanr.net/Squid/ | |
9 | * ---------------------------------------------------------- | |
10 | * | |
11 | * Squid is the result of efforts by numerous individuals from the | |
12 | * Internet community. Development is led by Duane Wessels of the | |
13 | * National Laboratory for Applied Network Research and funded by the | |
14 | * National Science Foundation. Squid is Copyrighted (C) 1998 by | |
15 | * Duane Wessels and the University of California San Diego. Please | |
16 | * see the COPYRIGHT file for full details. Squid incorporates | |
17 | * software developed and/or copyrighted by other sources. Please see | |
18 | * the CREDITS file for full details. | |
19 | * | |
20 | * This program is free software; you can redistribute it and/or modify | |
21 | * it under the terms of the GNU General Public License as published by | |
22 | * the Free Software Foundation; either version 2 of the License, or | |
23 | * (at your option) any later version. | |
24 | * | |
25 | * This program is distributed in the hope that it will be useful, | |
26 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
27 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
28 | * GNU General Public License for more details. | |
29 | * | |
30 | * You should have received a copy of the GNU General Public License | |
31 | * along with this program; if not, write to the Free Software | |
32 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. | |
33 | * | |
34 | */ | |
35 | ||
36 | #include "squid.h" | |
37 | ||
38 | #ifndef _PATH_RESOLV_CONF | |
39 | #define _PATH_RESOLV_CONF "/etc/resolv.conf" | |
40 | #endif | |
41 | #ifndef DOMAIN_PORT | |
42 | #define DOMAIN_PORT 53 | |
43 | #endif | |
44 | ||
45 | typedef struct _ns ns; | |
46 | struct _ns { | |
47 | struct sockaddr_in S; | |
48 | int nqueries; | |
49 | int nreplies; | |
50 | }; | |
51 | static ns *nameservers = NULL; | |
52 | static int nns = 0; | |
53 | static int nns_alloc = 0; | |
7b724b86 | 54 | static dlink_list lru_list; |
7cfc1c9a | 55 | static int event_queued = 0; |
7b724b86 | 56 | |
57 | static OBJH idnsStats; | |
58 | static void idnsAddNameserver(const char *buf); | |
59 | static void idnsFreeNameservers(void); | |
60 | static void idnsParseResolvConf(void); | |
61 | static void idnsSendQuery(idns_query * q); | |
62 | static int idnsFromKnownNameserver(struct sockaddr_in *from); | |
63 | static idns_query *idnsFindQuery(unsigned short id); | |
64 | static void idnsGrokReply(const char *buf, size_t sz); | |
65 | static PF idnsRead; | |
7cfc1c9a | 66 | static EVH idnsCheckQueue; |
7b724b86 | 67 | |
68 | static void | |
69 | idnsAddNameserver(const char *buf) | |
70 | { | |
71 | if (nns == nns_alloc) { | |
72 | int oldalloc = nns_alloc; | |
73 | ns *oldptr = nameservers; | |
74 | if (nns_alloc == 0) | |
75 | nns_alloc = 2; | |
76 | else | |
77 | nns_alloc <<= 1; | |
78 | nameservers = xcalloc(nns_alloc, sizeof(*nameservers)); | |
79 | if (oldptr && oldalloc) | |
80 | xmemcpy(nameservers, oldptr, oldalloc * sizeof(*nameservers)); | |
81 | if (oldptr) | |
82 | safe_free(oldptr); | |
83 | } | |
84 | assert(nns < nns_alloc); | |
85 | nameservers[nns].S.sin_family = AF_INET; | |
86 | nameservers[nns].S.sin_port = htons(DOMAIN_PORT); | |
87 | nameservers[nns].S.sin_addr.s_addr = inet_addr(buf); | |
88 | debug(78, 1) ("idnsAddNameserver: Added nameserver #%d: %s\n", | |
89 | nns, inet_ntoa(nameservers[nns].S.sin_addr)); | |
90 | nns++; | |
91 | } | |
92 | ||
93 | static void | |
94 | idnsFreeNameservers(void) | |
95 | { | |
96 | safe_free(nameservers); | |
97 | nns = nns_alloc = 0; | |
98 | } | |
99 | ||
100 | static void | |
101 | idnsParseResolvConf(void) | |
102 | { | |
103 | FILE *fp; | |
104 | char buf[512]; | |
105 | char *t; | |
106 | fp = fopen(_PATH_RESOLV_CONF, "r"); | |
107 | if (fp == NULL) { | |
108 | debug(78, 1) ("%s: %s\n", _PATH_RESOLV_CONF, xstrerror()); | |
109 | return; | |
110 | } | |
111 | idnsFreeNameservers(); | |
112 | while (fgets(buf, 512, fp)) { | |
113 | t = strtok(buf, w_space); | |
114 | if (strcasecmp(t, "nameserver")) | |
115 | continue; | |
116 | t = strtok(NULL, w_space); | |
117 | if (t == NULL) | |
118 | continue;; | |
119 | debug(78, 1) ("idnsParseResolvConf: nameserver %s\n", t); | |
120 | idnsAddNameserver(t); | |
121 | } | |
122 | fclose(fp); | |
123 | } | |
124 | ||
125 | static void | |
126 | idnsStats(StoreEntry * sentry) | |
127 | { | |
a16b4aa0 | 128 | dlink_node *n; |
129 | idns_query *q; | |
7cfc1c9a | 130 | int i; |
7b724b86 | 131 | storeAppendPrintf(sentry, "Internal DNS Statistics:\n"); |
a16b4aa0 | 132 | storeAppendPrintf(sentry, "\nThe Queue:\n"); |
7cfc1c9a | 133 | storeAppendPrintf(sentry, " ID SIZE SENDS DELAY\n"); |
134 | storeAppendPrintf(sentry, "------ ---- ----- --------\n"); | |
a16b4aa0 | 135 | for (n = lru_list.head; n; n = n->next) { |
136 | q = n->data; | |
ef523f99 | 137 | storeAppendPrintf(sentry, "%#06x %4d %5d %8.3f\n", |
138 | (int) q->id, q->sz, q->nsends, | |
7cfc1c9a | 139 | tvSubDsec(q->start_t, current_time)); |
140 | } | |
141 | storeAppendPrintf(sentry, "\nNameservers:\n"); | |
142 | storeAppendPrintf(sentry, "IP ADDRESS # QUERIES # REPLIES\n"); | |
143 | storeAppendPrintf(sentry, "--------------- --------- ---------\n"); | |
144 | for (i = 0; i < nns; i++) { | |
145 | storeAppendPrintf(sentry, "%-15s %9d %9d\n", | |
146 | inet_ntoa(nameservers[i].S.sin_addr), | |
147 | nameservers[i].nqueries, | |
148 | nameservers[i].nreplies); | |
a16b4aa0 | 149 | } |
7b724b86 | 150 | } |
151 | ||
152 | static void | |
153 | idnsSendQuery(idns_query * q) | |
154 | { | |
155 | int x; | |
ef523f99 | 156 | int ns; |
7b724b86 | 157 | /* XXX Select nameserver */ |
158 | assert(nns > 0); | |
159 | assert(q->lru.next == NULL); | |
160 | assert(q->lru.prev == NULL); | |
ef523f99 | 161 | ns = q->nsends % nns; |
162 | x = comm_udp_sendto(DnsSocket, | |
7b724b86 | 163 | &nameservers[ns].S, |
164 | sizeof(nameservers[ns].S), | |
165 | q->buf, | |
166 | q->sz); | |
a16b4aa0 | 167 | q->nsends++; |
7cfc1c9a | 168 | q->sent_t = current_time; |
169 | nameservers[ns].nqueries++; | |
7b724b86 | 170 | dlinkAdd(q, &q->lru, &lru_list); |
7cfc1c9a | 171 | if (!event_queued) { |
172 | eventAdd("idnsCheckQueue", idnsCheckQueue, NULL, 1.0, 1); | |
173 | event_queued = 1; | |
174 | } | |
7b724b86 | 175 | } |
176 | ||
177 | static int | |
178 | idnsFromKnownNameserver(struct sockaddr_in *from) | |
179 | { | |
180 | int i; | |
181 | for (i = 0; i < nns; i++) { | |
182 | if (nameservers[i].S.sin_addr.s_addr != from->sin_addr.s_addr) | |
183 | continue; | |
184 | if (nameservers[i].S.sin_port != from->sin_port) | |
185 | continue; | |
7cfc1c9a | 186 | return i; |
7b724b86 | 187 | } |
7cfc1c9a | 188 | return -1; |
7b724b86 | 189 | } |
190 | ||
191 | static idns_query * | |
192 | idnsFindQuery(unsigned short id) | |
193 | { | |
194 | dlink_node *n; | |
195 | idns_query *q; | |
196 | for (n = lru_list.tail; n; n = n->prev) { | |
197 | q = n->data; | |
198 | if (q->id == id) | |
199 | return q; | |
200 | } | |
201 | return NULL; | |
202 | } | |
203 | ||
204 | static void | |
205 | idnsGrokReply(const char *buf, size_t sz) | |
206 | { | |
207 | int n; | |
208 | int valid; | |
209 | rfc1035_rr *answers = NULL; | |
210 | unsigned short rid = 0xFFFF; | |
211 | idns_query *q; | |
212 | n = rfc1035AnswersUnpack(buf, | |
213 | sz, | |
214 | &answers, | |
215 | &rid); | |
a16b4aa0 | 216 | debug(78, 3) ("idnsGrokReply: ID %#hx, %d answers\n", rid, n); |
7b724b86 | 217 | if (rid == 0xFFFF) { |
218 | debug(78, 1) ("idnsGrokReply: Unknown error\n"); | |
219 | /* XXX leak answers? */ | |
220 | return; | |
221 | } | |
222 | q = idnsFindQuery(rid); | |
223 | if (q == NULL) { | |
224 | debug(78, 1) ("idnsGrokReply: Didn't find query!\n"); | |
225 | rfc1035RRDestroy(answers, n); | |
226 | return; | |
227 | } | |
a16b4aa0 | 228 | dlinkDelete(&q->lru, &lru_list); |
7b724b86 | 229 | if (n < 0) |
230 | debug(78, 1) ("idnsGrokReply: error %d\n", rfc1035_errno); | |
231 | valid = cbdataValid(q->callback_data); | |
232 | cbdataUnlock(q->callback_data); | |
233 | if (valid) | |
234 | q->callback(q->callback_data, answers, n); | |
235 | rfc1035RRDestroy(answers, n); | |
a16b4aa0 | 236 | memFree(q, MEM_IDNS_QUERY); |
7b724b86 | 237 | } |
238 | ||
239 | static void | |
240 | idnsRead(int fd, void *data) | |
241 | { | |
ef523f99 | 242 | int *N = data; |
7b724b86 | 243 | ssize_t len; |
244 | struct sockaddr_in from; | |
245 | socklen_t from_len; | |
246 | int max = 10; | |
247 | static char rbuf[512]; | |
7cfc1c9a | 248 | int ns; |
7b724b86 | 249 | commSetSelect(fd, COMM_SELECT_READ, idnsRead, NULL, 0); |
250 | while (max--) { | |
251 | from_len = sizeof(from); | |
252 | memset(&from, '\0', from_len); | |
253 | Counter.syscalls.sock.recvfroms++; | |
254 | len = recvfrom(fd, rbuf, 512, 0, (struct sockaddr *) &from, &from_len); | |
255 | if (len == 0) | |
256 | break; | |
257 | if (len < 0) { | |
258 | if (ignoreErrno(errno)) | |
259 | break; | |
260 | #ifdef _SQUID_LINUX_ | |
261 | /* Some Linux systems seem to set the FD for reading and then | |
262 | * return ECONNREFUSED when sendto() fails and generates an ICMP | |
263 | * port unreachable message. */ | |
264 | /* or maybe an EHOSTUNREACH "No route to host" message */ | |
265 | if (errno != ECONNREFUSED && errno != EHOSTUNREACH) | |
266 | #endif | |
267 | debug(50, 1) ("idnsRead: FD %d recvfrom: %s\n", | |
268 | fd, xstrerror()); | |
269 | break; | |
270 | } | |
ef523f99 | 271 | (*N)++; |
a16b4aa0 | 272 | debug(78, 3) ("idnsRead: FD %d: received %d bytes from %s.\n", |
7b724b86 | 273 | fd, |
274 | len, | |
275 | inet_ntoa(from.sin_addr)); | |
7cfc1c9a | 276 | ns = idnsFromKnownNameserver(&from); |
277 | if (ns < 0) { | |
7b724b86 | 278 | debug(78, 1) ("idnsRead: Reply from unknown nameserver [%s]\n", |
279 | inet_ntoa(from.sin_addr)); | |
280 | continue; | |
281 | } | |
7cfc1c9a | 282 | nameservers[ns].nreplies++; |
7b724b86 | 283 | idnsGrokReply(rbuf, len); |
284 | } | |
285 | } | |
286 | ||
7cfc1c9a | 287 | static void |
288 | idnsCheckQueue(void *unused) | |
289 | { | |
290 | dlink_node *n; | |
291 | idns_query *q; | |
292 | event_queued = 0; | |
293 | for (n = lru_list.tail; n; n = n->prev) { | |
294 | q = n->data; | |
295 | if (tvSubDsec(q->sent_t, current_time) < 5.0) | |
296 | break; | |
297 | debug(78, 1) ("idnsCheckQueue: ID %#04x timeout\n", | |
298 | q->id); | |
299 | dlinkDelete(&q->lru, &lru_list); | |
300 | idnsSendQuery(q); | |
301 | } | |
302 | } | |
303 | ||
7b724b86 | 304 | /* ====================================================================== */ |
305 | ||
306 | void | |
307 | idnsInit(void) | |
308 | { | |
309 | static int init = 0; | |
ef523f99 | 310 | if (DnsSocket < 0) { |
311 | DnsSocket = comm_open(SOCK_DGRAM, | |
7b724b86 | 312 | 0, |
313 | Config.Addrs.udp_outgoing, | |
314 | 0, | |
315 | COMM_NONBLOCKING, | |
316 | "DNS Socket"); | |
ef523f99 | 317 | if (DnsSocket < 0) |
7b724b86 | 318 | fatal("Could not create a DNS socket"); |
ef523f99 | 319 | debug(78, 1) ("DNS Socket created on FD %d\n", DnsSocket); |
320 | commSetSelect(DnsSocket, COMM_SELECT_READ, idnsRead, NULL, 0); | |
7b724b86 | 321 | } |
322 | if (nns == 0) | |
323 | idnsParseResolvConf(); | |
324 | if (!init) { | |
325 | cachemgrRegister("idns", | |
326 | "Internal DNS Statistics", | |
327 | idnsStats, 0, 1); | |
328 | } | |
329 | init++; | |
330 | } | |
331 | ||
332 | void | |
333 | idnsShutdown(void) | |
334 | { | |
ef523f99 | 335 | if (DnsSocket < 0) |
7b724b86 | 336 | return; |
ef523f99 | 337 | comm_close(DnsSocket); |
338 | DnsSocket = -1; | |
7b724b86 | 339 | } |
340 | ||
341 | void | |
342 | idnsALookup(const char *name, IDNSCB * callback, void *data) | |
343 | { | |
344 | idns_query *q = memAllocate(MEM_IDNS_QUERY); | |
345 | q->sz = sizeof(q->buf); | |
346 | q->id = rfc1035BuildAQuery(name, q->buf, &q->sz); | |
a16b4aa0 | 347 | debug(78, 3) ("idnsALookup: buf is %d bytes for %s, id = %#hx\n", |
7b724b86 | 348 | (int) q->sz, name, q->id); |
349 | q->callback = callback; | |
350 | q->callback_data = data; | |
351 | cbdataLock(q->callback_data); | |
7cfc1c9a | 352 | q->start_t = current_time; |
7b724b86 | 353 | idnsSendQuery(q); |
354 | } |