root/src/modules/lua_dns.c

Revision c7cb2706c8c9efa28ec98e664f1ba670d7d6e5bb, 13.9 kB (checked in by Theo Schlossnagle <jesus@omniti.com>, 4 years ago)

debug the abitrary nameserver dns context reaping

  • Property mode set to 100644
Line 
1 /*
2  * Copyright (c) 2010, OmniTI Computer Consulting, Inc.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are
7  * met:
8  *
9  *     * Redistributions of source code must retain the above copyright
10  *       notice, this list of conditions and the following disclaimer.
11  *     * Redistributions in binary form must reproduce the above
12  *       copyright notice, this list of conditions and the following
13  *       disclaimer in the documentation and/or other materials provided
14  *       with the distribution.
15  *     * Neither the name OmniTI Computer Consulting, Inc. nor the names
16  *       of its contributors may be used to endorse or promote products
17  *       derived from this software without specific prior written
18  *       permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31  */
32
33 #include "noit_defines.h"
34
35 #include <assert.h>
36 #include <math.h>
37 #include <errno.h>
38 #include <unistd.h>
39 #include <ctype.h>
40 #include <netinet/in.h>
41 #include <arpa/inet.h>
42 #ifdef HAVE_SYS_FILIO_H
43 #include <sys/filio.h>
44 #endif
45
46 #include "noit_conf.h"
47 #include "lua_noit.h"
48 #include "udns/udns.h"
49
50 static noit_hash_table dns_rtypes = NOIT_HASH_EMPTY;
51 static noit_hash_table dns_ctypes = NOIT_HASH_EMPTY;
52 static noit_hash_table dns_ctx_store = NOIT_HASH_EMPTY;
53 static pthread_mutex_t dns_ctx_store_lock;
54
55 typedef struct dns_ctx_handle {
56   char *ns;
57   struct dns_ctx *ctx;
58   noit_atomic32_t refcnt;
59   eventer_t e; /* eventer handling UDP traffic */
60   eventer_t timeout; /* the timeout managed by libudns */
61 } dns_ctx_handle_t;
62
63 typedef struct dns_lookup_ctx {
64   noit_lua_check_info_t *ci;
65   dns_ctx_handle_t *h;
66   char *error;
67   unsigned char dn[DNS_MAXDN];
68   enum dns_class query_ctype;
69   enum dns_type query_rtype;
70   int active;
71   noit_atomic32_t refcnt;
72 } dns_lookup_ctx_t;
73
74 static dns_ctx_handle_t *default_ctx_handle = NULL;
75
76 static int noit_lua_dns_eventer(eventer_t e, int mask, void *closure,
77                                 struct timeval *now) {
78   dns_ctx_handle_t *h = closure;
79   dns_ioevent(h->ctx, now->tv_sec);
80   return EVENTER_READ | EVENTER_EXCEPTION;
81 }
82
83 static int noit_lua_dns_timeouts(eventer_t e, int mask, void *closure,
84                                  struct timeval *now) {
85   dns_ctx_handle_t *h = closure;
86   dns_timeouts(h->ctx, 0, now->tv_sec);
87   return 0;
88 }
89
90 static void eventer_dns_utm_fn(struct dns_ctx *ctx, int timeout, void *data) {
91   dns_ctx_handle_t *h = data;
92   eventer_t e = NULL, newe = NULL;
93   if(h == NULL) return;
94   if(ctx == NULL) {
95     if(h->timeout) e = eventer_remove(h->timeout);
96   }
97   else {
98     assert(h->ctx == ctx);
99     if(timeout < 0) e = eventer_remove(h->timeout);
100     else {
101       newe = eventer_alloc();
102       newe->mask = EVENTER_TIMER;
103       newe->callback = noit_lua_dns_timeouts;
104       newe->closure = h;
105       gettimeofday(&newe->whence, NULL);
106       newe->whence.tv_sec += timeout;
107     }
108   }
109   if(e) eventer_free(e);
110   if(newe) eventer_add(newe);
111   h->timeout = newe;
112 }
113
114 static void dns_ctx_handle_free(void *vh) {
115   dns_ctx_handle_t *h = vh;
116   free(h->ns);
117   eventer_remove_fd(h->e->fd);
118   eventer_free(h->e);
119   h->e = NULL;
120   if(h->timeout) {
121     eventer_remove(h->timeout);
122     eventer_free(h->timeout);
123     h->timeout = NULL;
124   }
125   dns_close(h->ctx);
126   dns_free(h->ctx);
127   assert(h->timeout == NULL);
128   free(h);
129 }
130
131 static dns_ctx_handle_t *dns_ctx_alloc(const char *ns) {
132   void *vh;
133   dns_ctx_handle_t *h = NULL;
134   pthread_mutex_lock(&dns_ctx_store_lock);
135   if(ns == NULL && default_ctx_handle != NULL) {
136     /* special case -- default context */
137     h = default_ctx_handle;
138     noit_atomic_inc32(&h->refcnt);
139     goto bail;
140   }
141   if(ns &&
142      noit_hash_retrieve(&dns_ctx_store, ns, strlen(ns), &vh)) {
143     h = (dns_ctx_handle_t *)vh;
144     noit_atomic_inc32(&h->refcnt);
145   }
146   else {
147     int failed = 0;
148     h = calloc(1, sizeof(*h));
149     h->ns = ns ? strdup(ns) : NULL;
150     h->ctx = dns_new(NULL);
151     if(dns_init(h->ctx, 0) != 0) failed++;
152     if(ns) {
153       if(dns_add_serv(h->ctx, NULL) < 0) failed++;
154       if(dns_add_serv(h->ctx, ns) < 0) failed++;
155     }
156     if(dns_open(h->ctx) < 0) failed++;
157     if(failed) {
158       noitL(noit_error, "dns_open failed\n");
159       free(h->ns);
160       free(h);
161       h = NULL;
162       goto bail;
163     }
164     dns_set_tmcbck(h->ctx, eventer_dns_utm_fn, h);
165     h->e = eventer_alloc();
166     h->e->mask = EVENTER_READ | EVENTER_EXCEPTION;
167     h->e->closure = h;
168     h->e->callback = noit_lua_dns_eventer;
169     h->e->fd = dns_sock(h->ctx);
170     eventer_add(h->e);
171     h->refcnt = 1;
172     if(!ns)
173       default_ctx_handle = h;
174     else
175       noit_hash_store(&dns_ctx_store, h->ns, strlen(h->ns), h);
176   }
177  bail:
178   pthread_mutex_unlock(&dns_ctx_store_lock);
179   return h;
180 }
181
182 static void dns_ctx_release(dns_ctx_handle_t *h) {
183   if(h->ns == NULL) {
184     /* Special case for the default */
185     noit_atomic_dec32(&h->refcnt);
186     return;
187   }
188   pthread_mutex_lock(&dns_ctx_store_lock);
189   if(noit_atomic_dec32(&h->refcnt) == 0) {
190     /* I was the last one */
191     assert(noit_hash_delete(&dns_ctx_store, h->ns, strlen(h->ns),
192                             NULL, dns_ctx_handle_free));
193   }
194   pthread_mutex_unlock(&dns_ctx_store_lock);
195 }
196
197 void lookup_ctx_release(dns_lookup_ctx_t *v) {
198   if(!v) return;
199   if(v->error) free(v->error);
200   v->error = NULL;
201   if(noit_atomic_dec32(&v->refcnt) == 0) {
202     dns_ctx_release(v->h);
203     free(v);
204   }
205 }
206
207 int nl_dns_lookup(lua_State *L) {
208   dns_lookup_ctx_t *dlc, **holder;
209   const char *nameserver = NULL;
210   noit_lua_check_info_t *ci;
211
212   ci = get_ci(L);
213   assert(ci);
214   if(lua_gettop(L) > 0)
215     nameserver = lua_tostring(L, 1);
216   holder = (dns_lookup_ctx_t **)lua_newuserdata(L, sizeof(*holder));
217   dlc = calloc(1, sizeof(*dlc));
218   dlc->refcnt = 1;
219   dlc->ci = ci;
220   dlc->h = dns_ctx_alloc(nameserver);
221   *holder = dlc;
222   luaL_getmetatable(L, "noit.dns");
223   lua_setmetatable(L, -2);
224   return 1;
225 }
226
227 static char *encode_txt(char *dst, const unsigned char *src, int len) {
228   int i;
229   for(i=0; i<len; i++) {
230     if(src[i] >= 127 || src[i] <= 31) {
231       snprintf(dst, 4, "\\%02x", src[i]);
232       dst += 3;
233     }
234     else if(src[i] == '\\') {
235       *dst++ = '\\';
236       *dst++ = '\\';
237     }
238     else {
239       *dst++ = (char)src[i];
240     }
241   }
242   *dst = '\0';
243   return dst;
244 }
245
246 static void dns_cb(struct dns_ctx *ctx, void *result, void *data) {
247   int r = dns_status(ctx);
248   dns_lookup_ctx_t *dlc = data;
249   struct dns_parse p;
250   struct dns_rr rr;
251   unsigned nrr = 0;
252   unsigned char dn[DNS_MAXDN];
253   const unsigned char *pkt, *cur, *end;
254   lua_State *L;
255
256   if(!dlc->active) goto cleanup;
257   if(!result) goto cleanup;
258
259   L = dlc->ci->coro_state;
260
261   pkt = result; end = pkt + r; cur = dns_payload(pkt);
262   dns_getdn(pkt, &cur, end, dn, sizeof(dn));
263   dns_initparse(&p, NULL, pkt, cur, end);
264   p.dnsp_qcls = 0;
265   p.dnsp_qtyp = 0;
266
267   while(dns_nextrr(&p, &rr) > 0) {
268     const char *fieldname = NULL;
269     char buff[DNS_MAXDN], *txt_str, *c;
270     int totalsize;
271     const unsigned char *pkt = p.dnsp_pkt;
272     const unsigned char *end = p.dnsp_end;
273     const unsigned char *dptr = rr.dnsrr_dptr;
274     const unsigned char *dend = rr.dnsrr_dend;
275     unsigned char *dn = rr.dnsrr_dn;
276     const unsigned char *tmp;
277
278     if (!dns_dnequal(dn, rr.dnsrr_dn)) continue;
279     if ((dlc->query_ctype == DNS_C_ANY || dlc->query_ctype == rr.dnsrr_cls) &&
280         (dlc->query_rtype == DNS_T_ANY || dlc->query_rtype == rr.dnsrr_typ)) {
281       lua_newtable(L);
282       lua_pushinteger(L, rr.dnsrr_ttl);
283       lua_setfield(L, -2, "ttl");
284
285       switch(rr.dnsrr_typ) {
286         case DNS_T_A:
287           if(rr.dnsrr_dsz == 4) {
288             snprintf(buff, sizeof(buff), "%d.%d.%d.%d",
289                      dptr[0], dptr[1], dptr[2], dptr[3]);
290             lua_pushstring(L, buff);
291             lua_setfield(L, -2, "a");
292           }
293           break;
294
295         case DNS_T_AAAA:
296           if(rr.dnsrr_dsz == 16) {
297             inet_ntop(AF_INET6, dptr, buff, 16);
298             lua_pushstring(L, buff);
299             lua_setfield(L, -2, "aaaa");
300           }
301           break;
302
303         case DNS_T_TXT:
304           totalsize = 0;
305           for(tmp = dptr; tmp < dend; totalsize += *tmp, tmp += *tmp + 1)
306             if(tmp + *tmp + 1 > dend) break;
307           /* worst case: every character escaped + '\0' */
308           txt_str = alloca(totalsize * 3 + 1);
309           if(!txt_str) break;
310           c = txt_str;
311           for(tmp = dptr; tmp < dend; tmp += *tmp + 1)
312             c = encode_txt(c, tmp+1, *tmp);
313           lua_pushstring(L, txt_str);
314           lua_setfield(L, -2, "txt");
315           break;
316
317         case DNS_T_MX:
318           lua_pushinteger(L, dns_get16(dptr));
319           lua_setfield(L, -2, "preference");
320           tmp = dptr + 2;
321           if(dns_getdn(pkt, &tmp, end, dn, DNS_MAXDN) <= 0 || tmp != dend)
322             break;
323           dns_dntop(dn, buff + strlen(buff), sizeof(buff) - strlen(buff));
324           lua_pushstring(L, buff);
325           lua_setfield(L, -2, "mx");
326           break;
327
328         case DNS_T_CNAME: if(!fieldname) fieldname = "cname";
329         case DNS_T_PTR: if(!fieldname) fieldname = "ptr";
330         case DNS_T_NS: if(!fieldname) fieldname = "ns";
331         case DNS_T_MB: if(!fieldname) fieldname = "mb";
332         case DNS_T_MD: if(!fieldname) fieldname = "md";
333         case DNS_T_MF: if(!fieldname) fieldname = "mf";
334         case DNS_T_MG: if(!fieldname) fieldname = "mg";
335         case DNS_T_MR: if(!fieldname) fieldname = "mr";
336          if(dns_getdn(pkt, &dptr, end, dn, DNS_MAXDN) <= 0) break;
337          dns_dntop(dn, buff, sizeof(buff));
338          lua_pushstring(L, buff);
339          lua_setfield(L, -2, fieldname);
340          break;
341
342         default:
343           break;
344       }
345       ++nrr;
346     }
347     else if (rr.dnsrr_typ == DNS_T_CNAME && !nrr) {
348       if (dns_getdn(pkt, &rr.dnsrr_dptr, end,
349                     p.dnsp_dnbuf, sizeof(p.dnsp_dnbuf)) <= 0 ||
350           rr.dnsrr_dptr != rr.dnsrr_dend) {
351         break;
352       }
353     }
354   }
355
356  cleanup:
357   if(result) free(result);
358   if(dlc->active) noit_lua_resume(dlc->ci, nrr);
359   lookup_ctx_release(dlc);
360 }
361
362 static int noit_lua_dns_lookup(lua_State *L) {
363   dns_lookup_ctx_t *dlc, **holder;
364   const char *c, *query = "", *ctype = "IN", *rtype = "A";
365   char *ctype_up, *rtype_up, *d;
366   void *vnv_pair;
367   noit_lua_check_info_t *ci;
368
369   ci = get_ci(L);
370   assert(ci);
371
372   holder = (dns_lookup_ctx_t **)lua_touserdata(L, lua_upvalueindex(1));
373   if(holder != lua_touserdata(L,1))
374     luaL_error(L, "Must be called as method\n");
375   dlc = *holder;
376
377   if(lua_gettop(L) > 1) query = lua_tostring(L, 2);
378   if(lua_gettop(L) > 2) rtype = lua_tostring(L, 3);
379   if(lua_gettop(L) > 3) ctype = lua_tostring(L, 4);
380
381   ctype_up = alloca(strlen(ctype)+1);
382   for(d = ctype_up, c = ctype; *c; d++, c++) *d = toupper(*c);
383   *d = '\0';
384   rtype_up = alloca(strlen(rtype)+1);
385   for(d = rtype_up, c = rtype; *c; d++, c++) *d = toupper(*c);
386   *d = '\0';
387
388   if(!noit_hash_retrieve(&dns_ctypes, ctype_up, strlen(ctype_up), &vnv_pair))
389     dlc->error = strdup("bad class");
390   else
391     dlc->query_ctype = ((struct dns_nameval *)vnv_pair)->val;
392
393   if(!noit_hash_retrieve(&dns_rtypes, rtype_up, strlen(rtype_up), &vnv_pair))
394     dlc->error = strdup("bad rr type");
395   else
396     dlc->query_rtype = ((struct dns_nameval *)vnv_pair)->val;
397
398   dlc->active = 1;
399   noit_atomic_inc32(&dlc->refcnt);
400   if(!dlc->error) {
401     int abs;
402     if(!dns_ptodn(query, strlen(query), dlc->dn, sizeof(dlc->dn), &abs) ||
403        !dns_submit_dn(dlc->h->ctx, dlc->dn, dlc->query_ctype, dlc->query_rtype,
404                       abs | DNS_NOSRCH, NULL, dns_cb, dlc)) {
405       dlc->error = strdup("submission error");
406       noit_atomic_dec32(&dlc->refcnt);
407     }
408     else {
409       struct timeval now;
410       gettimeofday(&now, NULL);
411       dns_timeouts(dlc->h->ctx, -1, now.tv_sec);
412     }
413   }
414   if(dlc->error) {
415     dlc->active = 0;
416     luaL_error(L, "dns: %s\n", dlc->error);
417   }
418   return noit_lua_yield(ci, 0);
419 }
420
421 int noit_lua_dns_gc(lua_State *L) {
422   dns_lookup_ctx_t **holder;
423   holder = (dns_lookup_ctx_t **)lua_touserdata(L,1);
424   (*holder)->active = 0;
425   lookup_ctx_release(*holder);
426   return 0;
427 }
428
429 int noit_lua_dns_index_func(lua_State *L) {
430   int n;
431   const char *k;
432   dns_lookup_ctx_t **udata;
433
434   n = lua_gettop(L);
435   assert(n == 2);
436   if(!luaL_checkudata(L, 1, "noit.dns"))
437     luaL_error(L, "metatable error, arg1 is not a noit.dns");
438   udata = lua_touserdata(L, 1);
439   if(!lua_isstring(L, 2))
440     luaL_error(L, "metatable error, arg2 is not a string");
441   k = lua_tostring(L, 2);
442   if(!strcmp(k, "lookup")) {
443     lua_pushlightuserdata(L, udata);
444     lua_pushcclosure(L, noit_lua_dns_lookup, 1);
445     return 1;
446   }
447   luaL_error(L, "noit.dns no such element: %s", k);
448   return 0;
449 }
450
451 void noit_lua_init_dns() {
452   int i;
453   const struct dns_nameval *nv;
454   struct dns_ctx *pctx;
455
456   /* HASH the rr types */
457   for(i=0, nv = dns_type_index(i); nv->name; nv = dns_type_index(++i))
458     noit_hash_store(&dns_rtypes,
459                     nv->name, strlen(nv->name),
460                     (void *)nv);
461   /* HASH the class types */
462   for(i=0, nv = dns_class_index(i); nv->name; nv = dns_class_index(++i))
463     noit_hash_store(&dns_ctypes,
464                     nv->name, strlen(nv->name),
465                     (void *)nv);
466
467   eventer_name_callback("lua/dns_eventer", noit_lua_dns_eventer);
468   eventer_name_callback("lua/dns_timeouts", noit_lua_dns_timeouts);
469
470   if (dns_init(NULL, 0) < 0 || (pctx = dns_new(NULL)) == NULL) {
471     noitL(noit_error, "Unable to initialize dns subsystem\n");
472   }
473   else
474     dns_free(pctx);
475 }
Note: See TracBrowser for help on using the browser.