Move some code.
[apps/pfixtools.git] / common / rbl.c
1 /******************************************************************************/
2 /*          pfixtools: a collection of postfix related tools                  */
3 /*          ~~~~~~~~~                                                         */
4 /*  ________________________________________________________________________  */
5 /*                                                                            */
6 /*  Redistribution and use in source and binary forms, with or without        */
7 /*  modification, are permitted provided that the following conditions        */
8 /*  are met:                                                                  */
9 /*                                                                            */
10 /*  1. Redistributions of source code must retain the above copyright         */
11 /*     notice, this list of conditions and the following disclaimer.          */
12 /*  2. Redistributions in binary form must reproduce the above copyright      */
13 /*     notice, this list of conditions and the following disclaimer in the    */
14 /*     documentation and/or other materials provided with the distribution.   */
15 /*  3. The names of its contributors may not be used to endorse or promote    */
16 /*     products derived from this software without specific prior written     */
17 /*     permission.                                                            */
18 /*                                                                            */
19 /*  THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND   */
20 /*  ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE     */
21 /*  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR        */
22 /*  PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS    */
23 /*  BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR    */
24 /*  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF      */
25 /*  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS  */
26 /*  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN   */
27 /*  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)   */
28 /*  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF    */
29 /*  THE POSSIBILITY OF SUCH DAMAGE.                                           */
30 /******************************************************************************/
31
32 /*
33  * Copyright © 2008 Florent Bruneau
34  */
35
36 #include <unbound.h>
37 #include <netdb.h>
38 #include "array.h"
39 #include "epoll.h"
40 #include "server.h"
41 #include "rbl.h"
42
43
44 typedef struct rbl_context_t {
45     rbl_result_t *result;
46     rbl_result_callback_t call;
47     void *data;
48 } rbl_context_t;
49 ARRAY(rbl_context_t);
50
51 static struct ub_ctx *ctx = NULL;
52 static server_t *async_event = NULL;
53 static PA(rbl_context_t) ctx_pool = ARRAY_INIT;
54
55 static rbl_context_t *rbl_context_new(void)
56 {
57     return p_new(rbl_context_t, 1);
58 }
59
60 static void rbl_context_delete(rbl_context_t **context)
61 {
62     if (*context) {
63         p_delete(context);
64     }
65 }
66
67 static void rbl_context_wipe(rbl_context_t *context)
68 {
69     p_clear(context, 1);
70 }
71
72 static rbl_context_t *rbl_context_acquire(void)
73 {
74     if (array_len(ctx_pool) > 0) {
75         return array_pop_last(ctx_pool);
76     } else {
77         return rbl_context_new();
78     }
79 }
80
81 static void rbl_context_release(rbl_context_t *context)
82 {
83     rbl_context_wipe(context);
84     array_add(ctx_pool, context);
85 }
86
87 static void rbl_exit(void)
88 {
89     if (ctx != NULL) {
90         ub_ctx_delete(ctx);
91         ctx = NULL;
92     }
93     if (async_event != NULL) {
94         async_event->fd = -1;
95         server_release(async_event);
96         async_event = NULL;
97     }
98     array_deep_wipe(ctx_pool, rbl_context_delete);
99 }
100 module_exit(rbl_exit);
101
102 static void rbl_callback(void *arg, int err, struct ub_result *result)
103 {
104     rbl_context_t *context = arg;
105     if (err != 0) {
106         debug("asynchronous request led to an error");
107         *context->result = RBL_ERROR;
108     } else if (result->nxdomain) {
109         debug("asynchronous request done, %s NOT FOUND", result->qname);
110         *context->result = RBL_NOTFOUND;
111     } else {
112         debug("asynchronous request done, %s FOUND", result->qname);
113         *context->result = RBL_FOUND;
114     }
115     if (context->call != NULL) {
116         debug("calling callback");
117         context->call(context->result, context->data);
118     } else {
119         debug("no callback defined");
120     }
121     ub_resolve_free(result);
122     rbl_context_release(context);
123 }
124
125 static int rbl_handler(server_t *event, void *config)
126 {
127     int retval = 0;
128     debug("rbl_handler called: ub_fd triggered");
129     epoll_modify(event->fd, 0, event);
130     if ((retval = ub_process(ctx)) != 0) {
131         err("error in DNS resolution: %s", ub_strerror(retval));
132     }
133     epoll_modify(event->fd, EPOLLIN, event);
134     return 0;
135 }
136
137 static inline bool rbl_dns_check(const char *hostname, rbl_result_t *result,
138                                  rbl_result_callback_t callback, void *data)
139 {
140     if (ctx == NULL) {
141         ctx = ub_ctx_create();
142         ub_ctx_async(ctx, true);
143         if ((async_event = server_register(ub_fd(ctx), rbl_handler, NULL)) == NULL) {
144             crit("cannot register asynchronous DNS event handler");
145             abort();
146         }
147     }
148     rbl_context_t *context = rbl_context_acquire();
149     context->result = result;
150     context->call   = callback;
151     context->data   = data;
152     debug("running dns resolution on %s", hostname);
153     if (ub_resolve_async(ctx, (char*)hostname, 1, 1, context, rbl_callback, NULL) == 0) {
154         *result = RBL_ASYNC;
155         return true;
156     } else {
157         *result = RBL_ERROR;
158         rbl_context_release(context);
159         return false;
160     }
161 }
162
163 bool rbl_check(const char *rbl, uint32_t ip, rbl_result_t *result,
164                rbl_result_callback_t callback, void *data)
165 {
166     char host[257];
167     int len;
168
169     len = snprintf(host, 257, "%d.%d.%d.%d.%s.",
170                    ip & 0xff, (ip >> 8) & 0xff, (ip >> 16) & 0xff, (ip >> 24) & 0xff,
171                    rbl);
172     if (len >= (int)sizeof(host))
173         return RBL_ERROR;
174     if (host[len - 2] == '.')
175         host[len - 1] = '\0';
176     return rbl_dns_check(host, result, callback, data);
177 }
178
179 bool rhbl_check(const char *rhbl, const char *hostname, rbl_result_t *result,
180                 rbl_result_callback_t callback, void *data)
181 {
182     char host[257];
183     int len;
184
185     len = snprintf(host, 257, "%s.%s.", hostname, rhbl);
186     if (len >= (int)sizeof(host))
187         return RBL_ERROR;
188     if (host[len - 2] == '.')
189         host[len - 1] = '\0';
190     return rbl_dns_check(host, result, callback, data);
191 }