luci-app-openvpn: quote grep expression in getPID()
[project/luci.git] / libs / rpcd-mod-rrdns / src / rrdns.c
1 /*
2  * rrdns - Rapid Reverse DNS lookup plugin for the UBUS RPC server
3  *
4  *   Copyright (C) 2016 Jo-Philipp Wich <jow@openwrt.org>
5  *
6  * Permission to use, copy, modify, and/or distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18
19 #include <stdio.h>
20 #include <string.h>
21 #include <errno.h>
22 #include <stdint.h>
23 #include <stdlib.h>
24 #include <unistd.h>
25
26 #include <arpa/nameser.h>
27 #include <arpa/inet.h>
28
29 #include <sys/types.h>
30 #include <sys/socket.h>
31
32 #include <resolv.h>
33
34 #include <libubox/avl.h>
35 #include <libubox/usock.h>
36 #include <libubox/uloop.h>
37
38 #include <rpcd/plugin.h>
39
40 #include "rrdns.h"
41
42
43 enum {
44         RPC_L_ADDRS,
45         RPC_L_TIMEOUT,
46         RPC_L_SERVER,
47         RPC_L_PORT,
48         RPC_L_LIMIT,
49         __RPC_L_MAX,
50 };
51
52 static const struct blobmsg_policy rpc_lookup_policy[__RPC_L_MAX] = {
53         [RPC_L_ADDRS]   = { .name = "addrs",   .type = BLOBMSG_TYPE_ARRAY  },
54         [RPC_L_TIMEOUT] = { .name = "timeout", .type = BLOBMSG_TYPE_INT32  },
55         [RPC_L_SERVER]  = { .name = "server",  .type = BLOBMSG_TYPE_STRING },
56         [RPC_L_PORT]    = { .name = "port",    .type = BLOBMSG_TYPE_INT16  },
57         [RPC_L_LIMIT]   = { .name = "limit",   .type = BLOBMSG_TYPE_INT32  },
58 };
59
60
61 static int
62 rrdns_cmp_id(const void *k1, const void *k2, void *ptr)
63 {
64         const uint16_t *id1 = k1, *id2 = k2;
65         return (*id1 - *id2);
66 }
67
68 static int
69 rrdns_cmp_addr(const void *k1, const void *k2, void *ptr)
70 {
71         const struct in6_addr *a1 = k1, *a2 = k2;
72         return memcmp(a1, a2, sizeof(*a1));
73 }
74
75 static int
76 rrdns_parse_response(struct rrdns_context *rctx)
77 {
78         int n, len;
79         uint16_t id;
80         struct rrdns_request *req;
81         unsigned char res[512];
82         char buf[INET6_ADDRSTRLEN], dname[MAXDNAME];
83         HEADER *hdr;
84         ns_msg handle;
85         ns_rr rr;
86
87         len = recv(rctx->socket.fd, res, sizeof(res), 0);
88
89         if (len < sizeof(*hdr))
90                 return -ENODATA;
91
92         hdr = (HEADER *)res;
93         id  = hdr->id;
94         req = avl_find_element(&rctx->request_ids, &id, req, by_id);
95
96         if (!req)
97                 return -ENOENT;
98
99         avl_delete(&rctx->request_ids, &req->by_id);
100
101         if (ns_initparse(res, len, &handle))
102                 return -EINVAL;
103
104         for (n = 0; n < ns_msg_count(handle, ns_s_an); n++) {
105                 if (ns_parserr(&handle, ns_s_an, n, &rr))
106                         return -EINVAL;
107
108                 if (ns_rr_type(rr) != ns_t_ptr)
109                         continue;
110
111                 if (ns_name_uncompress(ns_msg_base(handle), ns_msg_end(handle),
112                                        ns_rr_rdata(rr), dname, sizeof(dname)) < 0)
113                         return -EINVAL;
114
115                 inet_ntop(req->family, &req->addr, buf, sizeof(buf));
116                 blobmsg_add_string(&rctx->blob, buf, dname);
117         }
118
119         return 0;
120 }
121
122 static int
123 rrdns_next_query(struct rrdns_context *rctx)
124 {
125         const char *addr = NULL, *hex = "0123456789abcdef";
126         struct rrdns_request *req;
127         int i, alen, family;
128         char *p, dname[73];
129
130         union {
131                 unsigned char uchar[4];
132                 struct in6_addr in6;
133                 struct in_addr in;
134         } a = { };
135
136         union {
137                 unsigned char buf[512];
138                 HEADER hdr;
139         } msg;
140
141         if (rctx->addr_rem > 0 &&
142             blob_pad_len(rctx->addr_cur) <= rctx->addr_rem &&
143             blob_pad_len(rctx->addr_cur) >= sizeof(struct blob_attr)) {
144
145                 addr = blobmsg_get_string(rctx->addr_cur);
146                 rctx->addr_rem -= blob_pad_len(rctx->addr_cur);
147                 rctx->addr_cur = blob_next(rctx->addr_cur);
148         }
149
150         if (!addr)
151                 return 0;
152
153         if (inet_pton(AF_INET6, addr, &a.in6)) {
154                 memset(dname, 0, sizeof(dname));
155
156                 for (i = 0, p = dname; i < 16; i++) {
157                         *p++ = hex[a.in6.s6_addr[15-i] % 16];
158                         *p++ = '.';
159                         *p++ = hex[a.in6.s6_addr[15-i] / 16];
160                         *p++ = '.';
161                 }
162
163                 p += snprintf(p, p - dname - 1, "ip6.arpa");
164
165                 family = AF_INET6;
166                 alen = p - dname;
167         }
168         else if (inet_pton(AF_INET, addr, &a.in)) {
169                 family = AF_INET;
170                 alen = snprintf(dname, sizeof(dname), "%u.%u.%u.%u.in-addr.arpa",
171                                 a.uchar[3], a.uchar[2], a.uchar[1], a.uchar[0]);
172         }
173         else {
174                 return -EINVAL;
175         }
176
177         alen = res_mkquery(QUERY, dname, C_IN, T_PTR, NULL, 0, NULL,
178                            msg.buf, sizeof(msg.buf));
179
180         if (alen < 0)
181                 return alen;
182
183         if (avl_find(&rctx->request_addrs, &a.in6))
184                 return -ENOTUNIQ;
185
186         if (send(rctx->socket.fd, msg.buf, alen, 0) != alen)
187                 return -errno;
188
189         req = calloc(1, sizeof(*req));
190
191         if (!req)
192                 return -ENOMEM;
193
194         req->id = msg.hdr.id;
195         req->by_id.key = &req->id;
196         avl_insert(&rctx->request_ids, &req->by_id);
197
198         req->family = family;
199         req->addr.in6 = a.in6;
200         req->by_addr.key = &req->addr.in6;
201         avl_insert(&rctx->request_addrs, &req->by_addr);
202
203         return 0;
204 }
205
206 static void
207 rdns_shutdown(struct rrdns_context *rctx)
208 {
209         struct rrdns_request *req, *tmp;
210
211         uloop_timeout_cancel(&rctx->timeout);
212         uloop_fd_delete(&rctx->socket);
213
214         close(rctx->socket.fd);
215
216         ubus_send_reply(rctx->context, &rctx->request, rctx->blob.head);
217         ubus_complete_deferred_request(rctx->context, &rctx->request,
218                                        UBUS_STATUS_OK);
219
220         avl_remove_all_elements(&rctx->request_addrs, req, by_addr, tmp)
221                 free(req);
222
223         blob_buf_free(&rctx->blob);
224         free(rctx);
225 }
226
227 static void
228 rrdns_handle_timeout(struct uloop_timeout *utm)
229 {
230         struct rrdns_context *rctx =
231                 container_of(utm, struct rrdns_context, timeout);
232
233         rdns_shutdown(rctx);
234 }
235
236 static void
237 rrdns_handle_response(struct uloop_fd *ufd, unsigned int ev)
238 {
239         struct rrdns_context *rctx =
240                 container_of(ufd, struct rrdns_context, socket);
241
242         int err = rrdns_parse_response(rctx);
243
244         if (err != -ENODATA && err != -ENOENT)
245                 rrdns_next_query(rctx);
246
247         if (avl_is_empty(&rctx->request_ids))
248                 rdns_shutdown(rctx);
249 }
250
251 static char *
252 rrdns_find_nameserver(void)
253 {
254         static char line[2*INET6_ADDRSTRLEN];
255         struct in6_addr in6;
256         FILE *resolvconf;
257         char *p;
258
259         resolvconf = fopen("/etc/resolv.conf", "r");
260
261         if (!resolvconf)
262                 return NULL;
263
264         while (fgets(line, sizeof(line), resolvconf)) {
265                 p = strtok(line, " \t");
266
267                 if (!p || strcmp(p, "nameserver"))
268                         continue;
269
270                 p = strtok(NULL, " \t\r\n");
271
272                 if (!p)
273                         continue;
274
275                 if (!inet_pton(AF_INET6, p, &in6) && !inet_pton(AF_INET, p, &in6))
276                         continue;
277
278                 fclose(resolvconf);
279                 return p;
280         }
281
282         fclose(resolvconf);
283         return NULL;
284 }
285
286 static int
287 rpc_rrdns_lookup(struct ubus_context *ctx, struct ubus_object *obj,
288                      struct ubus_request_data *req, const char *method,
289                      struct blob_attr *msg)
290 {
291         int port = 53, limit = RRDNS_DEF_LIMIT, timeout = RRDNS_DEF_TIMEOUT;
292         struct blob_attr *tb[__RPC_L_MAX];
293         struct rrdns_context *rctx;
294         const char *server = NULL;
295
296         blobmsg_parse(rpc_lookup_policy, __RPC_L_MAX, tb,
297                       blob_data(msg), blob_len(msg));
298
299         if (tb[RPC_L_PORT])
300                 port = blobmsg_get_u16(tb[RPC_L_PORT]);
301
302         if (tb[RPC_L_LIMIT])
303                 limit = blobmsg_get_u32(tb[RPC_L_LIMIT]);
304
305         if (tb[RPC_L_TIMEOUT])
306                 timeout = blobmsg_get_u32(tb[RPC_L_TIMEOUT]);
307
308         if (tb[RPC_L_SERVER])
309                 server = blobmsg_get_string(tb[RPC_L_SERVER]);
310
311
312         if (!tb[RPC_L_ADDRS])
313                 return UBUS_STATUS_INVALID_ARGUMENT;
314
315         if (port <= 0)
316                 return UBUS_STATUS_INVALID_ARGUMENT;
317
318         if (limit <= 0 || limit > RRDNS_MAX_LIMIT)
319                 return UBUS_STATUS_INVALID_ARGUMENT;
320
321         if (timeout <= 0 || timeout > RRDNS_MAX_TIMEOUT)
322                 return UBUS_STATUS_INVALID_ARGUMENT;
323
324
325         if (!server || !*server)
326                 server = rrdns_find_nameserver();
327
328         if (!server)
329                 return UBUS_STATUS_NOT_FOUND;
330
331         rctx = calloc(1, sizeof(*rctx));
332
333         if (!rctx)
334                 return UBUS_STATUS_UNKNOWN_ERROR;
335
336         rctx->socket.fd = usock(USOCK_UDP, server, usock_port(port));
337
338         if (rctx->socket.fd < 0) {
339                 free(rctx);
340                 return UBUS_STATUS_UNKNOWN_ERROR;
341         }
342
343         rctx->context = ctx;
344         rctx->addr_cur = blobmsg_data(tb[RPC_L_ADDRS]);
345         rctx->addr_rem = blobmsg_data_len(tb[RPC_L_ADDRS]);
346
347         avl_init(&rctx->request_ids, rrdns_cmp_id, false, NULL);
348         avl_init(&rctx->request_addrs, rrdns_cmp_addr, false, NULL);
349
350         rctx->timeout.cb = rrdns_handle_timeout;
351         uloop_timeout_set(&rctx->timeout, timeout);
352
353         rctx->socket.cb = rrdns_handle_response;
354         uloop_fd_add(&rctx->socket, ULOOP_READ);
355
356         blob_buf_init(&rctx->blob, 0);
357
358         while (limit--)
359                 rrdns_next_query(rctx);
360
361         ubus_defer_request(ctx, req, &rctx->request);
362
363         return UBUS_STATUS_OK;
364 }
365
366
367 static int
368 rpc_rrdns_api_init(const struct rpc_daemon_ops *o, struct ubus_context *ctx)
369 {
370         static const struct ubus_method rrdns_methods[] = {
371                 UBUS_METHOD("lookup", rpc_rrdns_lookup, rpc_lookup_policy),
372         };
373
374         static struct ubus_object_type rrdns_type =
375                 UBUS_OBJECT_TYPE("rpcd-rrdns", rrdns_methods);
376
377         static struct ubus_object obj = {
378                 .name = "network.rrdns",
379                 .type = &rrdns_type,
380                 .methods = rrdns_methods,
381                 .n_methods = ARRAY_SIZE(rrdns_methods),
382         };
383
384         return ubus_add_object(ctx, &obj);
385 }
386
387 struct rpc_plugin rpc_plugin = {
388         .init = rpc_rrdns_api_init
389 };