forked from bassosimone/libneubot
/
poller.c
426 lines (353 loc) · 8.67 KB
/
poller.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
/* libneubot/poller.c */
/*-
* Copyright (c) 2013
* Nexa Center for Internet & Society, Politecnico di Torino (DAUIN)
* and Simone Basso <bassosimone@gmail.com>.
*
* This file is part of Neubot <http://www.neubot.org/>.
*
* Neubot is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Neubot is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Neubot. If not, see <http://www.gnu.org/licenses/>.
*/
#include <arpa/inet.h>
#include <sys/queue.h>
#include <limits.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>
#ifndef WIN32
# include <signal.h>
#endif
#include <event2/buffer.h>
#include <event2/event.h>
#include <event2/event_compat.h>
#include <event2/event_struct.h>
#include <event2/dns.h>
#include <event2/dns_compat.h>
#include "neubot.h"
#include "ll2sock.h"
#include "log.h"
#include "poller.h"
#include "utils.h"
struct NeubotPoller {
#ifndef WIN32
struct event evsignal; /* for SIGINT */
#endif
struct event_base *base;
struct evdns_base *dnsbase;
};
struct NeubotEvent {
neubot_hook_vo callback;
neubot_hook_vo timeback;
struct event ev;
struct timeval tv;
evutil_socket_t fileno;
void *opaque;
};
struct ResolveContext {
neubot_hook_vos callback;
struct evbuffer *names;
void *opaque;
};
/*
* NeubotEvent implementation
*/
static void
NeubotEvent_noop(void *opaque)
{
(void) opaque;
/* nothing */ ;
}
static void
NeubotEvent_dispatch(evutil_socket_t socket, short event, void *opaque)
{
struct NeubotEvent *nevp;
(void) socket;
nevp = (struct NeubotEvent *) opaque;
if (event & EV_TIMEOUT)
nevp->timeback(nevp->opaque);
else
nevp->callback(nevp->opaque);
free(nevp);
}
static inline struct NeubotEvent *
NeubotEvent_construct(struct NeubotPoller *poller, long long fileno,
neubot_hook_vo callback, neubot_hook_vo timeback, void *opaque,
double timeout, short event)
{
struct NeubotEvent *nevp;
struct timeval *tvp;
int result;
(void) poller;
nevp = NULL;
/*
* Make sure that, if we want to do I/O, the socket is
* valid; otherwise, if we want to do timeout, make sure
* that the socket is invalid; while there, catch the
* case in which the user passes us an unexpected event.
*/
switch (event) {
case EV_READ:
case EV_WRITE:
if (!neubot_socket_valid(fileno))
goto cleanup;
break;
case EV_TIMEOUT:
if (fileno != NEUBOT_SOCKET_INVALID)
goto cleanup;
break;
default:
abort();
}
if (callback == NULL)
callback = NeubotEvent_noop;
if (timeback == NULL)
timeback = NeubotEvent_noop;
nevp = calloc(1, sizeof (*nevp));
if (nevp == NULL)
goto cleanup;
/*
* Note: `long long` simplifies the interaction with Java and
* shall be wide enough to hold evutil_socket_t, which is `int`
* on Unix and `uintptr_t` on Windows.
*/
nevp->fileno = (evutil_socket_t) fileno;
nevp->callback = callback;
nevp->timeback = timeback;
nevp->opaque = opaque;
event_set(&nevp->ev, nevp->fileno, event, NeubotEvent_dispatch, nevp);
tvp = neubot_timeval_init(&nevp->tv, timeout);
result = event_add(&nevp->ev, tvp);
if (result != 0)
goto cleanup;
return (nevp);
cleanup:
neubot_xfree(nevp);
return (NULL);
}
/*
* NeubotPoller implementation
*/
#ifndef WIN32
static void
NeubotPoller_sigint(int signo, short event, void *opaque)
{
struct NeubotPoller *self;
(void) signo;
(void) event;
self = (struct NeubotPoller *) opaque;
NeubotPoller_break_loop(self);
}
#endif
struct NeubotPoller *
NeubotPoller_construct(void)
{
struct NeubotPoller *self;
struct event_base *base;
int retval;
base = event_init();
if (base == NULL)
return (NULL);
if (evdns_init() != 0)
return (NULL);
self = (struct NeubotPoller *) calloc(1, sizeof(*self));
if (self == NULL)
return (NULL);
self->base = base;
self->dnsbase = evdns_get_global_base();
if (self->dnsbase == NULL)
abort();
#ifndef WIN32
event_set(&self->evsignal, SIGINT, EV_SIGNAL,
NeubotPoller_sigint, self);
retval = event_add(&self->evsignal, NULL);
if (retval != 0)
goto failure;
#endif
return (self);
failure:
#ifndef WIN32
event_del(&self->evsignal);
#endif
free(self);
return (NULL);
}
/* Method that we use only internally: */
struct event_base *
NeubotPoller_event_base_(struct NeubotPoller *self)
{
return (self->base);
}
/* Method that we use only internally: */
struct evdns_base *
NeubotPoller_evdns_base_(struct NeubotPoller *self)
{
return (self->dnsbase);
}
/*
* This is implemented like in Neubot; however, it is a bit dangerous
* and/or annoying that one cannot destroy pending callbacks.
*/
int
NeubotPoller_sched(struct NeubotPoller *self, double delta,
neubot_hook_vo callback, void *opaque)
{
struct NeubotEvent *nevp;
nevp = NeubotEvent_construct(self, NEUBOT_SOCKET_INVALID,
NeubotEvent_noop, callback, opaque, delta, EV_TIMEOUT);
if (nevp == NULL)
return (-1);
return (0);
}
/*
* The defer_read/defer_write/cancel interface allows to write
* simpler code on the Python side, even though the interface is
* less efficient than the Pollable interface.
*/
int
NeubotPoller_defer_read(struct NeubotPoller *self, long long fileno,
neubot_hook_vo callback, neubot_hook_vo timeback, void *opaque,
double timeout)
{
struct NeubotEvent *nevp;
nevp = NeubotEvent_construct(self, fileno, callback,
timeback, opaque, timeout, EV_READ);
if (nevp == NULL)
return (-1);
return (0);
}
int
NeubotPoller_defer_write(struct NeubotPoller *self, long long fileno,
neubot_hook_vo callback, neubot_hook_vo timeback, void *opaque,
double timeout)
{
struct NeubotEvent *nevp;
nevp = NeubotEvent_construct(self, fileno, callback,
timeback, opaque, timeout, EV_WRITE);
if (nevp == NULL)
return (-1);
return (0);
}
static void
NeubotPoller_resolve_callback_internal(int result, char type, int count,
int ttl, void *addresses, void *opaque)
{
struct ResolveContext *rc;
const char *p;
int error, family, size;
char string[128];
(void) result;
(void) ttl;
rc = (struct ResolveContext *) opaque;
switch (type) {
case DNS_IPv4_A:
family = AF_INET;
size = 4;
break;
case DNS_IPv6_AAAA:
family = AF_INET6;
size = 16;
break;
default:
abort();
}
while (--count >= 0) {
/* Note: address already in network byte order */
p = inet_ntop(family, (char *)addresses + count * size,
string, sizeof (string));
if (p == NULL) {
neubot_warn("resolve: inet_ntop() failed");
continue;
}
error = evbuffer_add(rc->names, p, strlen(p));
if (error != 0) {
neubot_warn("resolve: evbuffer_add() failed");
goto failure;
}
error = evbuffer_add(rc->names, " ", 1);
if (error != 0) {
neubot_warn("resolve: evbuffer_add() failed");
goto failure;
}
}
error = evbuffer_add(rc->names, "\0", 1);
if (error != 0) {
neubot_warn("resolve: evbuffer_add() failed");
goto failure;
}
p = (const char *) evbuffer_pullup(rc->names, -1);
if (p == NULL) {
neubot_warn("resolve: evbuffer_pullup() failed");
goto failure;
}
rc->callback(rc->opaque, p);
goto cleanup;
failure:
rc->callback(rc->opaque, "");
cleanup:
evbuffer_free(rc->names);
free(rc);
}
int
NeubotPoller_resolve(struct NeubotPoller *poller, const char *family,
const char *address, neubot_hook_vos callback, void *opaque)
{
struct ResolveContext *rc;
int result;
(void) poller;
rc = calloc(1, sizeof (*rc));
if (rc == NULL) {
neubot_warn("resolve: calloc() failed");
goto failure;
}
rc->callback = callback;
rc->opaque = opaque;
rc->names = evbuffer_new();
if (rc->names == NULL) {
neubot_warn("resolve: evbuffer_new() failed");
goto failure;
}
if (strcmp(family, "PF_INET6") == 0)
result = evdns_resolve_ipv6(address, DNS_QUERY_NO_SEARCH,
NeubotPoller_resolve_callback_internal, rc);
else if (strcmp(family, "PF_INET") == 0)
result = evdns_resolve_ipv4(address, DNS_QUERY_NO_SEARCH,
NeubotPoller_resolve_callback_internal, rc);
else {
neubot_warn("resolve: invalid family");
goto failure;
}
if (result != 0) {
neubot_warn("resolve: evdns_resolve_ipvX() failed");
goto failure;
}
return (0);
failure:
if (rc != NULL && rc->names != NULL)
evbuffer_free(rc->names);
if (rc != NULL)
free(rc);
return (-1);
}
void
NeubotPoller_loop(struct NeubotPoller *self)
{
(void) self;
event_dispatch();
}
void
NeubotPoller_break_loop(struct NeubotPoller *self)
{
(void) self;
event_loopbreak();
}