em-udns 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/ext/em-udns.c +635 -0
- data/ext/em-udns.h +11 -0
- data/ext/extconf.rb +32 -0
- data/ext/udns-0.1.tar.gz +0 -0
- data/lib/em-udns/resolver.rb +36 -0
- data/lib/em-udns.rb +32 -0
- data/test/test-em-udns.rb +115 -0
- metadata +68 -0
data/ext/em-udns.c
ADDED
@@ -0,0 +1,635 @@
|
|
1
|
+
#include <ruby.h>
|
2
|
+
#include <ctype.h>
|
3
|
+
#include <netinet/in.h>
|
4
|
+
#include "udns.h"
|
5
|
+
#include "em-udns.h"
|
6
|
+
|
7
|
+
|
8
|
+
static VALUE mEm;
|
9
|
+
static VALUE mEmDeferrable;
|
10
|
+
static VALUE mUdns;
|
11
|
+
|
12
|
+
static VALUE cResolver;
|
13
|
+
static VALUE cQuery;
|
14
|
+
|
15
|
+
static VALUE cRR_MX;
|
16
|
+
static VALUE cRR_SRV;
|
17
|
+
static VALUE cRR_NAPTR;
|
18
|
+
|
19
|
+
static VALUE eUdnsError;
|
20
|
+
static VALUE eUdnsTempFail;
|
21
|
+
static VALUE eUdnsNoMem;
|
22
|
+
static VALUE eUdnsBadQuery;
|
23
|
+
|
24
|
+
|
25
|
+
void Resolver_free(struct dns_ctx *dns_context)
|
26
|
+
{
|
27
|
+
if(dns_context)
|
28
|
+
dns_free(dns_context);
|
29
|
+
}
|
30
|
+
|
31
|
+
|
32
|
+
VALUE Resolver_alloc(VALUE klass)
|
33
|
+
{
|
34
|
+
struct dns_ctx *dns_context;
|
35
|
+
VALUE alloc_error = Qnil;
|
36
|
+
VALUE obj;
|
37
|
+
|
38
|
+
/* First initialize the library (so the default context). */
|
39
|
+
if (dns_init(NULL, 0) < 0)
|
40
|
+
alloc_error = rb_str_new2("udns `dns_init' failed");
|
41
|
+
|
42
|
+
/* Copy the context to a new one. */
|
43
|
+
if (!(dns_context = dns_new(NULL)))
|
44
|
+
alloc_error = rb_str_new2("udns `dns_new' failed");
|
45
|
+
|
46
|
+
obj = Data_Wrap_Struct(klass, NULL, Resolver_free, dns_context);
|
47
|
+
if (TYPE(alloc_error) == T_STRING)
|
48
|
+
rb_ivar_set(obj, rb_intern("@alloc_error"), alloc_error);
|
49
|
+
|
50
|
+
return obj;
|
51
|
+
}
|
52
|
+
|
53
|
+
|
54
|
+
void timer_cb(struct dns_ctx *dns_context, int timeout, void *data)
|
55
|
+
{
|
56
|
+
VALUE resolver;
|
57
|
+
VALUE timer;
|
58
|
+
|
59
|
+
resolver = (VALUE)data;
|
60
|
+
timer = rb_ivar_get(resolver, rb_intern("@timer"));
|
61
|
+
|
62
|
+
/* Cancel the EM::Timer. */
|
63
|
+
if (TYPE(timer) != T_NIL)
|
64
|
+
rb_funcall(timer, rb_intern("cancel"), 0);
|
65
|
+
|
66
|
+
if (timeout >= 0)
|
67
|
+
rb_funcall(resolver, rb_intern("set_timer"), 1, INT2FIX(timeout));
|
68
|
+
}
|
69
|
+
|
70
|
+
|
71
|
+
VALUE Resolver_dns_open(VALUE self)
|
72
|
+
{
|
73
|
+
struct dns_ctx *dns_context = NULL;
|
74
|
+
|
75
|
+
Data_Get_Struct(self, struct dns_ctx, dns_context);
|
76
|
+
|
77
|
+
dns_set_tmcbck(dns_context, timer_cb, (void*)self);
|
78
|
+
|
79
|
+
/* Open the new context. */
|
80
|
+
if (dns_open(dns_context) < 0)
|
81
|
+
rb_raise(eUdnsError, "udns `dns_open' failed");
|
82
|
+
|
83
|
+
return Qtrue;
|
84
|
+
}
|
85
|
+
|
86
|
+
|
87
|
+
VALUE Resolver_fd(VALUE self)
|
88
|
+
{
|
89
|
+
struct dns_ctx *dns_context = NULL;
|
90
|
+
|
91
|
+
Data_Get_Struct(self, struct dns_ctx, dns_context);
|
92
|
+
return INT2FIX(dns_sock(dns_context));
|
93
|
+
}
|
94
|
+
|
95
|
+
|
96
|
+
VALUE Resolver_ioevent(VALUE self)
|
97
|
+
{
|
98
|
+
struct dns_ctx *dns_context = NULL;
|
99
|
+
|
100
|
+
Data_Get_Struct(self, struct dns_ctx, dns_context);
|
101
|
+
dns_ioevent(dns_context, 0);
|
102
|
+
return Qfalse;
|
103
|
+
}
|
104
|
+
|
105
|
+
|
106
|
+
VALUE Resolver_timeouts(VALUE self)
|
107
|
+
{
|
108
|
+
struct dns_ctx *dns_context = NULL;
|
109
|
+
|
110
|
+
Data_Get_Struct(self, struct dns_ctx, dns_context);
|
111
|
+
dns_timeouts(dns_context, -1, 0);
|
112
|
+
|
113
|
+
return Qnil;
|
114
|
+
}
|
115
|
+
|
116
|
+
|
117
|
+
VALUE Resolver_cancel(VALUE self, VALUE query)
|
118
|
+
{
|
119
|
+
VALUE queries;
|
120
|
+
VALUE value;
|
121
|
+
|
122
|
+
queries = rb_ivar_get(self, rb_intern("@queries"));
|
123
|
+
if (TYPE(rb_hash_aref(queries, query)) == T_TRUE) {
|
124
|
+
rb_hash_aset(queries, query, Qfalse);
|
125
|
+
return Qtrue;
|
126
|
+
}
|
127
|
+
else
|
128
|
+
return Qfalse;
|
129
|
+
}
|
130
|
+
|
131
|
+
|
132
|
+
VALUE Resolver_active(VALUE self)
|
133
|
+
{
|
134
|
+
struct dns_ctx *dns_context = NULL;
|
135
|
+
|
136
|
+
Data_Get_Struct(self, struct dns_ctx, dns_context);
|
137
|
+
return INT2FIX(dns_active(dns_context));
|
138
|
+
}
|
139
|
+
|
140
|
+
|
141
|
+
static void* check_query(struct dns_ctx *dns_context, void *rr, void *data)
|
142
|
+
{
|
143
|
+
VALUE resolver;
|
144
|
+
VALUE query;
|
145
|
+
VALUE query_value_in_hash;
|
146
|
+
VALUE error;
|
147
|
+
int status;
|
148
|
+
|
149
|
+
resolver = ((struct resolver_query*)data)->resolver;
|
150
|
+
query = ((struct resolver_query*)data)->query;
|
151
|
+
xfree(data);
|
152
|
+
|
153
|
+
query_value_in_hash = rb_hash_delete(rb_ivar_get(resolver, rb_intern("@queries")), query);
|
154
|
+
/* Got response belongs to a query already removed (shouldn't occur). Ignore. */
|
155
|
+
if (query_value_in_hash == Qnil) {
|
156
|
+
if (rr) free(rr);
|
157
|
+
return NULL;
|
158
|
+
}
|
159
|
+
/* Got response belongs to a cancelled query. Ignore. */
|
160
|
+
else if (query_value_in_hash == Qfalse) {
|
161
|
+
if (rr) free(rr);
|
162
|
+
return NULL;
|
163
|
+
}
|
164
|
+
|
165
|
+
if ((status = dns_status(dns_context)) < 0) {
|
166
|
+
if (rr) free(rr);
|
167
|
+
switch(status) {
|
168
|
+
case DNS_E_TEMPFAIL:
|
169
|
+
error = ID2SYM(rb_intern("dns_error_tempfail"));
|
170
|
+
break;
|
171
|
+
case DNS_E_PROTOCOL:
|
172
|
+
error = ID2SYM(rb_intern("dns_error_protocol"));
|
173
|
+
break;
|
174
|
+
case DNS_E_NXDOMAIN:
|
175
|
+
error = ID2SYM(rb_intern("dns_error_nxdomain"));
|
176
|
+
break;
|
177
|
+
case DNS_E_NODATA:
|
178
|
+
error = ID2SYM(rb_intern("dns_error_nodata"));
|
179
|
+
break;
|
180
|
+
default:
|
181
|
+
error = ID2SYM(rb_intern("dns_error_unknown"));
|
182
|
+
break;
|
183
|
+
}
|
184
|
+
rb_funcall(query, rb_intern("set_deferred_status"), 2, ID2SYM(rb_intern("failed")), error);
|
185
|
+
return NULL;
|
186
|
+
}
|
187
|
+
|
188
|
+
return (void*)query;
|
189
|
+
}
|
190
|
+
|
191
|
+
|
192
|
+
static void dns_result_A_cb(struct dns_ctx *dns_context, struct dns_rr_a4 *rr, void *data)
|
193
|
+
{
|
194
|
+
VALUE query;
|
195
|
+
VALUE array;
|
196
|
+
int i;
|
197
|
+
char ip[INET_ADDRSTRLEN];
|
198
|
+
|
199
|
+
if (!(query = (VALUE)check_query(dns_context, rr, data))) return;
|
200
|
+
|
201
|
+
array = rb_ary_new2(rr->dnsa4_nrr);
|
202
|
+
for(i = 0; i < rr->dnsa4_nrr; i++)
|
203
|
+
rb_ary_push(array, rb_str_new2((char *)dns_ntop(AF_INET, &(rr->dnsa4_addr[i].s_addr), ip, INET_ADDRSTRLEN)));
|
204
|
+
free(rr);
|
205
|
+
|
206
|
+
rb_funcall(query, rb_intern("set_deferred_status"), 2, ID2SYM(rb_intern("succeeded")), array);
|
207
|
+
}
|
208
|
+
|
209
|
+
|
210
|
+
static void dns_result_AAAA_cb(struct dns_ctx *dns_context, struct dns_rr_a6 *rr, void *data)
|
211
|
+
{
|
212
|
+
VALUE query;
|
213
|
+
VALUE array;
|
214
|
+
int i;
|
215
|
+
char ip[INET6_ADDRSTRLEN];
|
216
|
+
|
217
|
+
if (!(query = (VALUE)check_query(dns_context, rr, data))) return;
|
218
|
+
|
219
|
+
array = rb_ary_new2(rr->dnsa6_nrr);
|
220
|
+
for(i = 0; i < rr->dnsa6_nrr; i++)
|
221
|
+
rb_ary_push(array, rb_str_new2((char *)dns_ntop(AF_INET6, &(rr->dnsa6_addr[i].s6_addr), ip, INET6_ADDRSTRLEN)));
|
222
|
+
free(rr);
|
223
|
+
|
224
|
+
rb_funcall(query, rb_intern("set_deferred_status"), 2, ID2SYM(rb_intern("succeeded")), array);
|
225
|
+
}
|
226
|
+
|
227
|
+
|
228
|
+
static void dns_result_PTR_cb(struct dns_ctx *dns_context, struct dns_rr_ptr *rr, void *data)
|
229
|
+
{
|
230
|
+
VALUE query;
|
231
|
+
VALUE array;
|
232
|
+
int i;
|
233
|
+
|
234
|
+
if (!(query = (VALUE)check_query(dns_context, rr, data))) return;
|
235
|
+
|
236
|
+
array = rb_ary_new2(rr->dnsptr_nrr);
|
237
|
+
for(i = 0; i < rr->dnsptr_nrr; i++)
|
238
|
+
rb_ary_push(array, rb_str_new2(rr->dnsptr_ptr[i]));
|
239
|
+
free(rr);
|
240
|
+
|
241
|
+
rb_funcall(query, rb_intern("set_deferred_status"), 2, ID2SYM(rb_intern("succeeded")), array);
|
242
|
+
}
|
243
|
+
|
244
|
+
|
245
|
+
static void dns_result_MX_cb(struct dns_ctx *dns_context, struct dns_rr_mx *rr, void *data)
|
246
|
+
{
|
247
|
+
VALUE query;
|
248
|
+
VALUE array;
|
249
|
+
int i;
|
250
|
+
VALUE rr_mx;
|
251
|
+
|
252
|
+
if (!(query = (VALUE)check_query(dns_context, rr, data))) return;
|
253
|
+
|
254
|
+
array = rb_ary_new2(rr->dnsmx_nrr);
|
255
|
+
for(i = 0; i < rr->dnsmx_nrr; i++) {
|
256
|
+
rr_mx = rb_obj_alloc(cRR_MX);
|
257
|
+
rb_ivar_set(rr_mx, rb_intern("@domain"), rb_str_new2(rr->dnsmx_mx[i].name));
|
258
|
+
rb_ivar_set(rr_mx, rb_intern("@priority"), INT2FIX(rr->dnsmx_mx[i].priority));
|
259
|
+
rb_ary_push(array, rr_mx);
|
260
|
+
}
|
261
|
+
free(rr);
|
262
|
+
|
263
|
+
rb_funcall(query, rb_intern("set_deferred_status"), 2, ID2SYM(rb_intern("succeeded")), array);
|
264
|
+
}
|
265
|
+
|
266
|
+
|
267
|
+
static void dns_result_TXT_cb(struct dns_ctx *dns_context, struct dns_rr_txt *rr, void *data)
|
268
|
+
{
|
269
|
+
VALUE query;
|
270
|
+
VALUE array;
|
271
|
+
int i;
|
272
|
+
|
273
|
+
if (!(query = (VALUE)check_query(dns_context, rr, data))) return;
|
274
|
+
|
275
|
+
array = rb_ary_new2(rr->dnstxt_nrr);
|
276
|
+
for(i = 0; i < rr->dnstxt_nrr; i++)
|
277
|
+
rb_ary_push(array, rb_str_new(rr->dnstxt_txt[i].txt, rr->dnstxt_txt[i].len));
|
278
|
+
free(rr);
|
279
|
+
|
280
|
+
rb_funcall(query, rb_intern("set_deferred_status"), 2, ID2SYM(rb_intern("succeeded")), array);
|
281
|
+
}
|
282
|
+
|
283
|
+
|
284
|
+
static void dns_result_SRV_cb(struct dns_ctx *dns_context, struct dns_rr_srv *rr, void *data)
|
285
|
+
{
|
286
|
+
VALUE query;
|
287
|
+
VALUE array;
|
288
|
+
int i;
|
289
|
+
VALUE rr_srv;
|
290
|
+
|
291
|
+
if (!(query = (VALUE)check_query(dns_context, rr, data))) return;
|
292
|
+
|
293
|
+
array = rb_ary_new2(rr->dnssrv_nrr);
|
294
|
+
for(i = 0; i < rr->dnssrv_nrr; i++) {
|
295
|
+
rr_srv = rb_obj_alloc(cRR_SRV);
|
296
|
+
rb_ivar_set(rr_srv, rb_intern("@domain"), rb_str_new2(rr->dnssrv_srv[i].name));
|
297
|
+
rb_ivar_set(rr_srv, rb_intern("@priority"), INT2FIX(rr->dnssrv_srv[i].priority));
|
298
|
+
rb_ivar_set(rr_srv, rb_intern("@weight"), INT2FIX(rr->dnssrv_srv[i].weight));
|
299
|
+
rb_ivar_set(rr_srv, rb_intern("@port"), INT2FIX(rr->dnssrv_srv[i].port));
|
300
|
+
rb_ary_push(array, rr_srv);
|
301
|
+
}
|
302
|
+
free(rr);
|
303
|
+
|
304
|
+
rb_funcall(query, rb_intern("set_deferred_status"), 2, ID2SYM(rb_intern("succeeded")), array);
|
305
|
+
}
|
306
|
+
|
307
|
+
|
308
|
+
static void dns_result_NAPTR_cb(struct dns_ctx *dns_context, struct dns_rr_naptr *rr, void *data)
|
309
|
+
{
|
310
|
+
VALUE query;
|
311
|
+
VALUE array;
|
312
|
+
int i;
|
313
|
+
VALUE rr_naptr;
|
314
|
+
|
315
|
+
if (!(query = (VALUE)check_query(dns_context, rr, data))) return;
|
316
|
+
|
317
|
+
array = rb_ary_new2(rr->dnsnaptr_nrr);
|
318
|
+
for(i = 0; i < rr->dnsnaptr_nrr; i++) {
|
319
|
+
rr_naptr = rb_obj_alloc(cRR_NAPTR);
|
320
|
+
rb_ivar_set(rr_naptr, rb_intern("@order"), INT2FIX(rr->dnsnaptr_naptr[i].order));
|
321
|
+
rb_ivar_set(rr_naptr, rb_intern("@preference"), INT2FIX(rr->dnsnaptr_naptr[i].preference));
|
322
|
+
rb_ivar_set(rr_naptr, rb_intern("@flags"), rb_str_new2(rr->dnsnaptr_naptr[i].flags));
|
323
|
+
rb_ivar_set(rr_naptr, rb_intern("@service"), rb_str_new2(rr->dnsnaptr_naptr[i].service));
|
324
|
+
if (strlen(rr->dnsnaptr_naptr[i].regexp) > 0)
|
325
|
+
rb_ivar_set(rr_naptr, rb_intern("@regexp"), rb_str_new2(rr->dnsnaptr_naptr[i].regexp));
|
326
|
+
else
|
327
|
+
rb_ivar_set(rr_naptr, rb_intern("@regexp"), Qnil);
|
328
|
+
if (strlen(rr->dnsnaptr_naptr[i].replacement) > 0)
|
329
|
+
rb_ivar_set(rr_naptr, rb_intern("@replacement"), rb_str_new2(rr->dnsnaptr_naptr[i].replacement));
|
330
|
+
else
|
331
|
+
rb_ivar_set(rr_naptr, rb_intern("@replacement"), Qnil);
|
332
|
+
rb_ary_push(array, rr_naptr);
|
333
|
+
}
|
334
|
+
free(rr);
|
335
|
+
|
336
|
+
rb_funcall(query, rb_intern("set_deferred_status"), 2, ID2SYM(rb_intern("succeeded")), array);
|
337
|
+
}
|
338
|
+
|
339
|
+
|
340
|
+
void raise_dns_error(struct dns_ctx *dns_context)
|
341
|
+
{
|
342
|
+
switch(dns_status(dns_context)) {
|
343
|
+
case DNS_E_TEMPFAIL:
|
344
|
+
rb_raise(eUdnsTempFail, "internal error occured");
|
345
|
+
break;
|
346
|
+
case DNS_E_NOMEM:
|
347
|
+
rb_raise(eUdnsNoMem, "no memory available to allocate query structure");
|
348
|
+
break;
|
349
|
+
case DNS_E_BADQUERY:
|
350
|
+
rb_raise(eUdnsBadQuery, "name of dn is invalid");
|
351
|
+
break;
|
352
|
+
default:
|
353
|
+
rb_raise(eUdnsError, "udns `dns_status' returns unexpected error %i", dns_status(dns_context));
|
354
|
+
break;
|
355
|
+
}
|
356
|
+
}
|
357
|
+
|
358
|
+
|
359
|
+
VALUE Resolver_submit_A(VALUE self, VALUE rb_domain)
|
360
|
+
{
|
361
|
+
struct dns_ctx *dns_context;
|
362
|
+
char *domain;
|
363
|
+
VALUE query;
|
364
|
+
struct resolver_query *data;
|
365
|
+
|
366
|
+
Data_Get_Struct(self, struct dns_ctx, dns_context);
|
367
|
+
domain = StringValueCStr(rb_domain);
|
368
|
+
query = rb_obj_alloc(cQuery);
|
369
|
+
|
370
|
+
data = ALLOC(struct resolver_query);
|
371
|
+
data->resolver = self;
|
372
|
+
data->query = query;
|
373
|
+
|
374
|
+
if (!dns_submit_a4(dns_context, domain, 0, dns_result_A_cb, (void *)data)) {
|
375
|
+
xfree(data);
|
376
|
+
raise_dns_error(dns_context);
|
377
|
+
}
|
378
|
+
|
379
|
+
rb_hash_aset(rb_ivar_get(self, rb_intern("@queries")), query, Qtrue);
|
380
|
+
return query;
|
381
|
+
}
|
382
|
+
|
383
|
+
|
384
|
+
VALUE Resolver_submit_AAAA(VALUE self, VALUE rb_domain)
|
385
|
+
{
|
386
|
+
struct dns_ctx *dns_context;
|
387
|
+
char *domain;
|
388
|
+
VALUE query;
|
389
|
+
struct resolver_query *data;
|
390
|
+
|
391
|
+
Data_Get_Struct(self, struct dns_ctx, dns_context);
|
392
|
+
domain = StringValueCStr(rb_domain);
|
393
|
+
query = rb_obj_alloc(cQuery);
|
394
|
+
|
395
|
+
data = ALLOC(struct resolver_query);
|
396
|
+
data->resolver = self;
|
397
|
+
data->query = query;
|
398
|
+
|
399
|
+
if (!dns_submit_a6(dns_context, domain, 0, dns_result_AAAA_cb, (void *)data)) {
|
400
|
+
xfree(data);
|
401
|
+
raise_dns_error(dns_context);
|
402
|
+
}
|
403
|
+
|
404
|
+
rb_hash_aset(rb_ivar_get(self, rb_intern("@queries")), query, Qtrue);
|
405
|
+
return query;
|
406
|
+
}
|
407
|
+
|
408
|
+
|
409
|
+
VALUE Resolver_submit_PTR(VALUE self, VALUE rb_ip)
|
410
|
+
{
|
411
|
+
struct dns_ctx *dns_context;
|
412
|
+
char *ip;
|
413
|
+
VALUE query;
|
414
|
+
struct resolver_query *data;
|
415
|
+
struct in_addr addr;
|
416
|
+
struct in6_addr addr6;
|
417
|
+
|
418
|
+
Data_Get_Struct(self, struct dns_ctx, dns_context);
|
419
|
+
ip = StringValueCStr(rb_ip);
|
420
|
+
query = rb_obj_alloc(cQuery);
|
421
|
+
|
422
|
+
data = ALLOC(struct resolver_query);
|
423
|
+
data->resolver = self;
|
424
|
+
data->query = query;
|
425
|
+
|
426
|
+
switch(dns_pton(AF_INET, ip, &addr)) {
|
427
|
+
/* It's valid IPv4. */
|
428
|
+
case 1:
|
429
|
+
if (!dns_submit_a4ptr(dns_context, &addr, dns_result_PTR_cb, (void *)data)) {
|
430
|
+
xfree(data);
|
431
|
+
raise_dns_error(dns_context);
|
432
|
+
}
|
433
|
+
break;
|
434
|
+
/* Invalid IPv4, let's try with IPv6. */
|
435
|
+
case 0:
|
436
|
+
switch(dns_pton(AF_INET6, ip, &addr6)) {
|
437
|
+
/* It's valid IPv6. */
|
438
|
+
case 1:
|
439
|
+
if (!dns_submit_a6ptr(dns_context, &addr6, dns_result_PTR_cb, (void *)data)) {
|
440
|
+
xfree(data);
|
441
|
+
raise_dns_error(dns_context);
|
442
|
+
}
|
443
|
+
break;
|
444
|
+
/* Also an invalid IPv6 so raise an exception. */
|
445
|
+
case 0:
|
446
|
+
xfree(data);
|
447
|
+
rb_raise(rb_eArgError, "invalid IP '%s' (neither IPv4 or IPv6)", ip);
|
448
|
+
break;
|
449
|
+
}
|
450
|
+
break;
|
451
|
+
}
|
452
|
+
|
453
|
+
rb_hash_aset(rb_ivar_get(self, rb_intern("@queries")), query, Qtrue);
|
454
|
+
return query;
|
455
|
+
}
|
456
|
+
|
457
|
+
|
458
|
+
VALUE Resolver_submit_MX(VALUE self, VALUE rb_domain)
|
459
|
+
{
|
460
|
+
struct dns_ctx *dns_context;
|
461
|
+
char *domain;
|
462
|
+
VALUE query;
|
463
|
+
struct resolver_query *data;
|
464
|
+
|
465
|
+
Data_Get_Struct(self, struct dns_ctx, dns_context);
|
466
|
+
domain = StringValueCStr(rb_domain);
|
467
|
+
query = rb_obj_alloc(cQuery);
|
468
|
+
|
469
|
+
data = ALLOC(struct resolver_query);
|
470
|
+
data->resolver = self;
|
471
|
+
data->query = query;
|
472
|
+
|
473
|
+
if (!dns_submit_mx(dns_context, domain, 0, dns_result_MX_cb, (void *)data)) {
|
474
|
+
xfree(data);
|
475
|
+
raise_dns_error(dns_context);
|
476
|
+
}
|
477
|
+
|
478
|
+
rb_hash_aset(rb_ivar_get(self, rb_intern("@queries")), query, Qtrue);
|
479
|
+
return query;
|
480
|
+
}
|
481
|
+
|
482
|
+
|
483
|
+
VALUE Resolver_submit_TXT(VALUE self, VALUE rb_domain)
|
484
|
+
{
|
485
|
+
struct dns_ctx *dns_context;
|
486
|
+
char *domain;
|
487
|
+
VALUE query;
|
488
|
+
struct resolver_query *data;
|
489
|
+
|
490
|
+
Data_Get_Struct(self, struct dns_ctx, dns_context);
|
491
|
+
domain = StringValueCStr(rb_domain);
|
492
|
+
query = rb_obj_alloc(cQuery);
|
493
|
+
|
494
|
+
data = ALLOC(struct resolver_query);
|
495
|
+
data->resolver = self;
|
496
|
+
data->query = query;
|
497
|
+
|
498
|
+
if (!dns_submit_txt(dns_context, domain, DNS_C_IN, 0, dns_result_TXT_cb, (void *)data)) {
|
499
|
+
xfree(data);
|
500
|
+
raise_dns_error(dns_context);
|
501
|
+
}
|
502
|
+
|
503
|
+
rb_hash_aset(rb_ivar_get(self, rb_intern("@queries")), query, Qtrue);
|
504
|
+
return query;
|
505
|
+
}
|
506
|
+
|
507
|
+
|
508
|
+
VALUE Resolver_submit_SRV(int argc, VALUE *argv, VALUE self)
|
509
|
+
{
|
510
|
+
struct dns_ctx *dns_context;
|
511
|
+
char *domain;
|
512
|
+
char *service = NULL;
|
513
|
+
char *protocol = NULL;
|
514
|
+
VALUE query;
|
515
|
+
struct resolver_query *data;
|
516
|
+
|
517
|
+
if (argc == 1 && TYPE(argv[0]) == T_STRING);
|
518
|
+
else if (argc == 3 && TYPE(argv[0]) == T_STRING &&
|
519
|
+
TYPE(argv[1]) == T_STRING && TYPE(argv[2]) == T_STRING) {
|
520
|
+
service = StringValueCStr(argv[1]);
|
521
|
+
protocol = StringValueCStr(argv[2]);
|
522
|
+
}
|
523
|
+
else if (argc == 3 && TYPE(argv[0]) == T_STRING &&
|
524
|
+
TYPE(argv[1]) == T_NIL && TYPE(argv[2]) == T_NIL);
|
525
|
+
else
|
526
|
+
rb_raise(rb_eArgError, "arguments must be `domain' or `domain',`service',`protocol'");
|
527
|
+
|
528
|
+
Data_Get_Struct(self, struct dns_ctx, dns_context);
|
529
|
+
domain = StringValueCStr(argv[0]);
|
530
|
+
query = rb_obj_alloc(cQuery);
|
531
|
+
|
532
|
+
data = ALLOC(struct resolver_query);
|
533
|
+
data->resolver = self;
|
534
|
+
data->query = query;
|
535
|
+
|
536
|
+
if (!dns_submit_srv(dns_context, domain, service, protocol, 0, dns_result_SRV_cb, (void *)data)) {
|
537
|
+
xfree(data);
|
538
|
+
raise_dns_error(dns_context);
|
539
|
+
}
|
540
|
+
|
541
|
+
rb_hash_aset(rb_ivar_get(self, rb_intern("@queries")), query, Qtrue);
|
542
|
+
return query;
|
543
|
+
}
|
544
|
+
|
545
|
+
|
546
|
+
VALUE Resolver_submit_NAPTR(VALUE self, VALUE rb_domain)
|
547
|
+
{
|
548
|
+
struct dns_ctx *dns_context;
|
549
|
+
char *domain;
|
550
|
+
VALUE query;
|
551
|
+
struct resolver_query *data;
|
552
|
+
|
553
|
+
Data_Get_Struct(self, struct dns_ctx, dns_context);
|
554
|
+
domain = StringValueCStr(rb_domain);
|
555
|
+
query = rb_obj_alloc(cQuery);
|
556
|
+
|
557
|
+
data = ALLOC(struct resolver_query);
|
558
|
+
data->resolver = self;
|
559
|
+
data->query = query;
|
560
|
+
|
561
|
+
if (!dns_submit_naptr(dns_context, domain, 0, dns_result_NAPTR_cb, (void *)data)) {
|
562
|
+
xfree(data);
|
563
|
+
raise_dns_error(dns_context);
|
564
|
+
}
|
565
|
+
|
566
|
+
rb_hash_aset(rb_ivar_get(self, rb_intern("@queries")), query, Qtrue);
|
567
|
+
return query;
|
568
|
+
}
|
569
|
+
|
570
|
+
|
571
|
+
/* Attribute readers. */
|
572
|
+
VALUE RR_MX_domain(VALUE self) { return rv_ivar_get(self, rb_intern("@domain")); }
|
573
|
+
VALUE RR_MX_priority(VALUE self) { return rv_ivar_get(self, rb_intern("@priority")); }
|
574
|
+
|
575
|
+
VALUE RR_SRV_priority(VALUE self) { return rv_ivar_get(self, rb_intern("@priority")); }
|
576
|
+
VALUE RR_SRV_weight(VALUE self) { return rv_ivar_get(self, rb_intern("@weight")); }
|
577
|
+
VALUE RR_SRV_port(VALUE self) { return rv_ivar_get(self, rb_intern("@port")); }
|
578
|
+
VALUE RR_SRV_domain(VALUE self) { return rv_ivar_get(self, rb_intern("@domain")); }
|
579
|
+
|
580
|
+
VALUE RR_NAPTR_order(VALUE self) { return rv_ivar_get(self, rb_intern("@order")); }
|
581
|
+
VALUE RR_NAPTR_preference(VALUE self) { return rv_ivar_get(self, rb_intern("@preference")); }
|
582
|
+
VALUE RR_NAPTR_flags(VALUE self) { return rv_ivar_get(self, rb_intern("@flags")); }
|
583
|
+
VALUE RR_NAPTR_service(VALUE self) { return rv_ivar_get(self, rb_intern("@service")); }
|
584
|
+
VALUE RR_NAPTR_regexp(VALUE self) { return rv_ivar_get(self, rb_intern("@regexp")); }
|
585
|
+
VALUE RR_NAPTR_replacement(VALUE self) { return rv_ivar_get(self, rb_intern("@replacement")); }
|
586
|
+
|
587
|
+
|
588
|
+
void Init_em_udns_ext()
|
589
|
+
{
|
590
|
+
mEm = rb_define_module("EventMachine");
|
591
|
+
mEmDeferrable = rb_define_module_under(mEm, "Deferrable");
|
592
|
+
mUdns = rb_define_module_under(mEm, "Udns");
|
593
|
+
|
594
|
+
eUdnsError = rb_define_class_under(mUdns, "UdnsError", rb_eStandardError);
|
595
|
+
eUdnsTempFail = rb_define_class_under(mUdns, "UdnsTempFail", eUdnsError);
|
596
|
+
eUdnsNoMem = rb_define_class_under(mUdns, "UdnsNoMem", eUdnsError);
|
597
|
+
eUdnsBadQuery = rb_define_class_under(mUdns, "UdnsBadQuery", eUdnsError);
|
598
|
+
|
599
|
+
cResolver = rb_define_class_under(mUdns, "Resolver", rb_cObject);
|
600
|
+
rb_define_alloc_func(cResolver, Resolver_alloc);
|
601
|
+
rb_define_private_method(cResolver, "dns_open", Resolver_dns_open, 0);
|
602
|
+
rb_define_method(cResolver, "fd", Resolver_fd, 0);
|
603
|
+
rb_define_method(cResolver, "ioevent", Resolver_ioevent, 0);
|
604
|
+
rb_define_private_method(cResolver, "timeouts", Resolver_timeouts, 0);
|
605
|
+
rb_define_method(cResolver, "active", Resolver_active, 0);
|
606
|
+
rb_define_method(cResolver, "cancel", Resolver_cancel, 1);
|
607
|
+
rb_define_method(cResolver, "submit_A", Resolver_submit_A, 1);
|
608
|
+
rb_define_method(cResolver, "submit_AAAA", Resolver_submit_AAAA, 1);
|
609
|
+
rb_define_method(cResolver, "submit_PTR", Resolver_submit_PTR, 1);
|
610
|
+
rb_define_method(cResolver, "submit_MX", Resolver_submit_MX, 1);
|
611
|
+
rb_define_method(cResolver, "submit_TXT", Resolver_submit_TXT, 1);
|
612
|
+
rb_define_method(cResolver, "submit_SRV", Resolver_submit_SRV, -1);
|
613
|
+
rb_define_method(cResolver, "submit_NAPTR", Resolver_submit_NAPTR, 1);
|
614
|
+
|
615
|
+
cQuery = rb_define_class_under(mUdns, "Query", rb_cObject);
|
616
|
+
rb_include_module(cQuery, mEmDeferrable);
|
617
|
+
|
618
|
+
cRR_MX = rb_define_class_under(mUdns, "RR_MX", rb_cObject);
|
619
|
+
rb_define_method(cRR_MX, "domain", RR_MX_domain, 0);
|
620
|
+
rb_define_method(cRR_MX, "priority", RR_MX_priority, 0);
|
621
|
+
|
622
|
+
cRR_SRV = rb_define_class_under(mUdns, "RR_SRV", rb_cObject);
|
623
|
+
rb_define_method(cRR_SRV, "priority", RR_SRV_priority, 0);
|
624
|
+
rb_define_method(cRR_SRV, "weight", RR_SRV_weight, 0);
|
625
|
+
rb_define_method(cRR_SRV, "port", RR_SRV_port, 0);
|
626
|
+
rb_define_method(cRR_SRV, "domain", RR_SRV_domain, 0);
|
627
|
+
|
628
|
+
cRR_NAPTR = rb_define_class_under(mUdns, "RR_NAPTR", rb_cObject);
|
629
|
+
rb_define_method(cRR_NAPTR, "order", RR_NAPTR_order, 0);
|
630
|
+
rb_define_method(cRR_NAPTR, "preference", RR_NAPTR_preference, 0);
|
631
|
+
rb_define_method(cRR_NAPTR, "flags", RR_NAPTR_flags, 0);
|
632
|
+
rb_define_method(cRR_NAPTR, "service", RR_NAPTR_service, 0);
|
633
|
+
rb_define_method(cRR_NAPTR, "regexp", RR_NAPTR_regexp, 0);
|
634
|
+
rb_define_method(cRR_NAPTR, "replacement", RR_NAPTR_replacement, 0);
|
635
|
+
}
|
data/ext/em-udns.h
ADDED
data/ext/extconf.rb
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
require "mkmf"
|
2
|
+
require "fileutils"
|
3
|
+
|
4
|
+
|
5
|
+
here = File.expand_path(File.dirname(__FILE__))
|
6
|
+
udns_tarball = Dir.glob("#{here}/udns-*.tar.gz").first
|
7
|
+
udns_path = udns_tarball.gsub(".tar.gz", "")
|
8
|
+
|
9
|
+
Dir.chdir(here) do
|
10
|
+
puts(cmd = "tar xzf #{udns_tarball} 2>&1")
|
11
|
+
raise "'#{cmd}' failed" unless system(cmd)
|
12
|
+
|
13
|
+
Dir.chdir(udns_path) do
|
14
|
+
puts(cmd = "./configure 2>&1")
|
15
|
+
raise "'#{cmd}' failed" unless system(cmd)
|
16
|
+
|
17
|
+
puts(cmd = "make sharedlib 2>&1")
|
18
|
+
raise "'#{cmd}' failed" unless system(cmd)
|
19
|
+
|
20
|
+
puts(cmd = "ar r libudns.a *.lo 2>&1")
|
21
|
+
raise "'#{cmd}' failed" unless system(cmd)
|
22
|
+
|
23
|
+
FileUtils.mv "libudns.a", "../"
|
24
|
+
FileUtils.mv "udns.h", "../"
|
25
|
+
end
|
26
|
+
|
27
|
+
FileUtils.remove_dir(udns_path, force = true)
|
28
|
+
end
|
29
|
+
|
30
|
+
|
31
|
+
have_library("udns") # == -ludns
|
32
|
+
create_makefile("em_udns_ext")
|
data/ext/udns-0.1.tar.gz
ADDED
Binary file
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module EventMachine::Udns
|
2
|
+
|
3
|
+
class Resolver
|
4
|
+
|
5
|
+
def initialize(nameserver = nil)
|
6
|
+
raise UdnsError, @alloc_error if @alloc_error
|
7
|
+
|
8
|
+
@queries = {}
|
9
|
+
|
10
|
+
if nameserver
|
11
|
+
ENV.delete("NAMESERVERS")
|
12
|
+
ENV["NAMESERVERS"] = case nameserver
|
13
|
+
# A single nameserver.
|
14
|
+
when String
|
15
|
+
nameserver
|
16
|
+
# Multiple nameservers.
|
17
|
+
when Array
|
18
|
+
nameserver.join(" ")
|
19
|
+
else
|
20
|
+
raise Error, "`nameserver' argument must be a String or Array of addresses"
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
dns_open
|
25
|
+
end
|
26
|
+
|
27
|
+
def set_timer(timeout)
|
28
|
+
@timer = EM::Timer.new(timeout) do
|
29
|
+
timeouts
|
30
|
+
end
|
31
|
+
end
|
32
|
+
private :set_timer
|
33
|
+
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
data/lib/em-udns.rb
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
require "eventmachine"
|
2
|
+
|
3
|
+
require "em_udns_ext"
|
4
|
+
require "em-udns/resolver"
|
5
|
+
|
6
|
+
|
7
|
+
module EventMachine::Udns
|
8
|
+
|
9
|
+
module Watcher
|
10
|
+
def initialize(resolver)
|
11
|
+
@resolver = resolver
|
12
|
+
end
|
13
|
+
|
14
|
+
def notify_readable
|
15
|
+
@resolver.ioevent
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.run(resolver)
|
20
|
+
raise Error, "EventMachine is not running" unless EM.reactor_running?
|
21
|
+
|
22
|
+
raise Error, "`resolver' argument must be a EM::Udns::Resolver instance" unless
|
23
|
+
resolver.is_a? EM::Udns::Resolver
|
24
|
+
|
25
|
+
EM.watch resolver.fd, Watcher, resolver do |dns_client|
|
26
|
+
dns_client.notify_readable = true
|
27
|
+
end
|
28
|
+
|
29
|
+
self
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
@@ -0,0 +1,115 @@
|
|
1
|
+
#!/usr/bin/ruby
|
2
|
+
|
3
|
+
require "rubygems"
|
4
|
+
require "em-udns"
|
5
|
+
|
6
|
+
|
7
|
+
def show_usage
|
8
|
+
puts <<-END_USAGE
|
9
|
+
USAGE:
|
10
|
+
|
11
|
+
#{$0} A domain [times]
|
12
|
+
#{$0} AAAA domain [times]
|
13
|
+
#{$0} PTR ip [times]
|
14
|
+
#{$0} MX domain [times]
|
15
|
+
#{$0} TXT domain [times]
|
16
|
+
#{$0} SRV _service._protocol.domain [times]
|
17
|
+
#{$0} SRV domain [service] [protocol] [times]
|
18
|
+
#{$0} NAPTR domain [times]
|
19
|
+
END_USAGE
|
20
|
+
end
|
21
|
+
|
22
|
+
|
23
|
+
if ARGV.size < 2
|
24
|
+
show_usage
|
25
|
+
exit false
|
26
|
+
end
|
27
|
+
|
28
|
+
type = ARGV[0].upcase
|
29
|
+
name = ARGV[1]
|
30
|
+
case type
|
31
|
+
when "A", "AAAA", "MX", "TXT", "PTR", "NAPTR"
|
32
|
+
times = (ARGV[2].to_i > 0) ? ARGV[2].to_i : 1
|
33
|
+
when "SRV"
|
34
|
+
if ARGV[3]
|
35
|
+
service = ARGV[2]
|
36
|
+
protocol = ARGV[3]
|
37
|
+
times = (ARGV[4].to_i > 0) ? ARGV[4].to_i : 1
|
38
|
+
else
|
39
|
+
service = nil
|
40
|
+
protocol = nil
|
41
|
+
times = (ARGV[2].to_i > 0) ? ARGV[2].to_i : 1
|
42
|
+
end
|
43
|
+
else
|
44
|
+
show_usage
|
45
|
+
exit false
|
46
|
+
end
|
47
|
+
|
48
|
+
|
49
|
+
def print_info(times, time_start)
|
50
|
+
puts "\n\nINFO: all the #{times} queries terminated"
|
51
|
+
printf "INFO: time elapsed: %.4f seconds (%.4f queries/second)\n", (time_elapsed = Time.now - time_start), (times / time_elapsed)
|
52
|
+
end
|
53
|
+
|
54
|
+
|
55
|
+
EM.set_max_timers 100000
|
56
|
+
EM.run do
|
57
|
+
|
58
|
+
resolver = EM::Udns::Resolver.new "127.0.0.1"
|
59
|
+
EM::Udns.run resolver
|
60
|
+
|
61
|
+
second = 0
|
62
|
+
EM.add_periodic_timer(1) { puts "[#{second += 1}] - active queries: #{resolver.active}" }
|
63
|
+
|
64
|
+
time_start = Time.now
|
65
|
+
sent = 0
|
66
|
+
recv = 0
|
67
|
+
|
68
|
+
timer = EM::PeriodicTimer.new(0) do
|
69
|
+
if sent == times
|
70
|
+
timer.cancel
|
71
|
+
else
|
72
|
+
sent += 1
|
73
|
+
end
|
74
|
+
|
75
|
+
query = case type
|
76
|
+
when "A"
|
77
|
+
resolver.submit_A name
|
78
|
+
when "AAAA"
|
79
|
+
resolver.submit_AAAA name
|
80
|
+
when "PTR"
|
81
|
+
resolver.submit_PTR name
|
82
|
+
when "MX"
|
83
|
+
resolver.submit_MX name
|
84
|
+
when "TXT"
|
85
|
+
resolver.submit_TXT name
|
86
|
+
when "SRV"
|
87
|
+
resolver.submit_SRV name, service, protocol
|
88
|
+
when "NAPTR"
|
89
|
+
resolver.submit_NAPTR name
|
90
|
+
end
|
91
|
+
|
92
|
+
query.callback do |result|
|
93
|
+
recv += 1
|
94
|
+
puts "#{Time.now} INFO: callback: result => #{result.inspect} (sent: #{sent} / recv: #{recv})"
|
95
|
+
if recv == times
|
96
|
+
print_info(times, time_start)
|
97
|
+
exit
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
query.errback do |error|
|
102
|
+
recv += 1
|
103
|
+
puts "#{Time.now} INFO: errback: error => #{error.inspect} (sent: #{sent} / recv: #{recv})"
|
104
|
+
if recv == times
|
105
|
+
print_info(times, time_start)
|
106
|
+
exit
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
# Uncomment to cancel the query (so no callback/errback will be called).
|
111
|
+
#resolver.cancel(query)
|
112
|
+
end
|
113
|
+
|
114
|
+
end
|
115
|
+
|
metadata
ADDED
@@ -0,0 +1,68 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: em-udns
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Iñaki Baz Castillo
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2011-02-14 00:00:00.000000000 +01:00
|
13
|
+
default_executable:
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: eventmachine
|
17
|
+
requirement: &21069240 !ruby/object:Gem::Requirement
|
18
|
+
none: false
|
19
|
+
requirements:
|
20
|
+
- - ! '>='
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: '0'
|
23
|
+
type: :runtime
|
24
|
+
prerelease: false
|
25
|
+
version_requirements: *21069240
|
26
|
+
description: em-udns is an async DNS resolver for EventMachine based on udns C library.
|
27
|
+
Having most of the core written in C, em-udns becomes very fast. It can resolve
|
28
|
+
DNS A, AAAA, PTR, MX, TXT, SRV and NAPTR records, and can handle every kind of errors
|
29
|
+
(domain/record not found, request timeout, malformed response...).
|
30
|
+
email: ibc@aliax.net
|
31
|
+
executables: []
|
32
|
+
extensions:
|
33
|
+
- ext/extconf.rb
|
34
|
+
extra_rdoc_files: []
|
35
|
+
files:
|
36
|
+
- lib/em-udns.rb
|
37
|
+
- lib/em-udns/resolver.rb
|
38
|
+
- ext/em-udns.c
|
39
|
+
- ext/em-udns.h
|
40
|
+
- ext/extconf.rb
|
41
|
+
- ext/udns-0.1.tar.gz
|
42
|
+
- test/test-em-udns.rb
|
43
|
+
has_rdoc: true
|
44
|
+
homepage: https://github.com/ibc/em-udns
|
45
|
+
licenses: []
|
46
|
+
post_install_message:
|
47
|
+
rdoc_options: []
|
48
|
+
require_paths:
|
49
|
+
- lib
|
50
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
51
|
+
none: false
|
52
|
+
requirements:
|
53
|
+
- - ! '>='
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: 1.8.7
|
56
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ! '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
requirements: []
|
63
|
+
rubyforge_project:
|
64
|
+
rubygems_version: 1.5.2
|
65
|
+
signing_key:
|
66
|
+
specification_version: 3
|
67
|
+
summary: Async DNS resolver for EventMachine based on udns C library
|
68
|
+
test_files: []
|