root/src/modules/lua_dns.c

Revision 755463ce8098d16034ad29b35726fffb6f8733fe, 14.1 kB (checked in by Theo Schlossnagle <jesus@omniti.com>, 9 months ago)

prevent crash: noit.dns():lookup(nil)

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