root/src/modules/lua_dns.c

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

fix copyright

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