2 * rrdns - Rapid Reverse DNS lookup plugin for the UBUS RPC server
4 * Copyright (C) 2016 Jo-Philipp Wich <jow@openwrt.org>
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.
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.
26 #include <arpa/nameser.h>
27 #include <arpa/inet.h>
29 #include <sys/types.h>
30 #include <sys/socket.h>
34 #include <libubox/avl.h>
35 #include <libubox/usock.h>
36 #include <libubox/uloop.h>
38 #include <rpcd/plugin.h>
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 },
62 rrdns_cmp_id(const void *k1, const void *k2, void *ptr)
64 const uint16_t *id1 = k1, *id2 = k2;
69 rrdns_cmp_addr(const void *k1, const void *k2, void *ptr)
71 const struct in6_addr *a1 = k1, *a2 = k2;
72 return memcmp(a1, a2, sizeof(*a1));
76 rrdns_parse_response(struct rrdns_context *rctx)
80 struct rrdns_request *req;
81 unsigned char res[512];
82 char buf[INET6_ADDRSTRLEN], dname[MAXDNAME];
87 len = recv(rctx->socket.fd, res, sizeof(res), 0);
89 if (len < sizeof(*hdr))
94 req = avl_find_element(&rctx->request_ids, &id, req, by_id);
99 avl_delete(&rctx->request_ids, &req->by_id);
101 if (ns_initparse(res, len, &handle))
104 for (n = 0; n < ns_msg_count(handle, ns_s_an); n++) {
105 if (ns_parserr(&handle, ns_s_an, n, &rr))
108 if (ns_rr_type(rr) != ns_t_ptr)
111 if (ns_name_uncompress(ns_msg_base(handle), ns_msg_end(handle),
112 ns_rr_rdata(rr), dname, sizeof(dname)) < 0)
115 inet_ntop(req->family, &req->addr, buf, sizeof(buf));
116 blobmsg_add_string(&rctx->blob, buf, dname);
123 rrdns_next_query(struct rrdns_context *rctx)
125 const char *addr = NULL, *hex = "0123456789abcdef";
126 struct rrdns_request *req;
131 unsigned char uchar[4];
137 unsigned char buf[512];
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)) {
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);
153 if (inet_pton(AF_INET6, addr, &a.in6)) {
154 memset(dname, 0, sizeof(dname));
156 for (i = 0, p = dname; i < 16; i++) {
157 *p++ = hex[a.in6.s6_addr[15-i] % 16];
159 *p++ = hex[a.in6.s6_addr[15-i] / 16];
163 p += snprintf(p, p - dname - 1, "ip6.arpa");
168 else if (inet_pton(AF_INET, addr, &a.in)) {
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]);
177 alen = res_mkquery(QUERY, dname, C_IN, T_PTR, NULL, 0, NULL,
178 msg.buf, sizeof(msg.buf));
183 if (avl_find(&rctx->request_addrs, &a.in6))
186 if (send(rctx->socket.fd, msg.buf, alen, 0) != alen)
189 req = calloc(1, sizeof(*req));
194 req->id = msg.hdr.id;
195 req->by_id.key = &req->id;
196 avl_insert(&rctx->request_ids, &req->by_id);
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);
207 rdns_shutdown(struct rrdns_context *rctx)
209 struct rrdns_request *req, *tmp;
211 uloop_timeout_cancel(&rctx->timeout);
212 uloop_fd_delete(&rctx->socket);
214 close(rctx->socket.fd);
216 ubus_send_reply(rctx->context, &rctx->request, rctx->blob.head);
217 ubus_complete_deferred_request(rctx->context, &rctx->request,
220 avl_remove_all_elements(&rctx->request_addrs, req, by_addr, tmp)
223 blob_buf_free(&rctx->blob);
228 rrdns_handle_timeout(struct uloop_timeout *utm)
230 struct rrdns_context *rctx =
231 container_of(utm, struct rrdns_context, timeout);
237 rrdns_handle_response(struct uloop_fd *ufd, unsigned int ev)
239 struct rrdns_context *rctx =
240 container_of(ufd, struct rrdns_context, socket);
242 int err = rrdns_parse_response(rctx);
244 if (err != -ENODATA && err != -ENOENT)
245 rrdns_next_query(rctx);
247 if (avl_is_empty(&rctx->request_ids))
252 rrdns_find_nameserver(void)
254 static char line[2*INET6_ADDRSTRLEN];
259 resolvconf = fopen("/etc/resolv.conf", "r");
264 while (fgets(line, sizeof(line), resolvconf)) {
265 p = strtok(line, " \t");
267 if (!p || strcmp(p, "nameserver"))
270 p = strtok(NULL, " \t\r\n");
275 if (!inet_pton(AF_INET6, p, &in6) && !inet_pton(AF_INET, p, &in6))
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)
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;
296 blobmsg_parse(rpc_lookup_policy, __RPC_L_MAX, tb,
297 blob_data(msg), blob_len(msg));
300 port = blobmsg_get_u16(tb[RPC_L_PORT]);
303 limit = blobmsg_get_u32(tb[RPC_L_LIMIT]);
305 if (tb[RPC_L_TIMEOUT])
306 timeout = blobmsg_get_u32(tb[RPC_L_TIMEOUT]);
308 if (tb[RPC_L_SERVER])
309 server = blobmsg_get_string(tb[RPC_L_SERVER]);
312 if (!tb[RPC_L_ADDRS])
313 return UBUS_STATUS_INVALID_ARGUMENT;
316 return UBUS_STATUS_INVALID_ARGUMENT;
318 if (limit <= 0 || limit > RRDNS_MAX_LIMIT)
319 return UBUS_STATUS_INVALID_ARGUMENT;
321 if (timeout <= 0 || timeout > RRDNS_MAX_TIMEOUT)
322 return UBUS_STATUS_INVALID_ARGUMENT;
325 if (!server || !*server)
326 server = rrdns_find_nameserver();
329 return UBUS_STATUS_NOT_FOUND;
331 rctx = calloc(1, sizeof(*rctx));
334 return UBUS_STATUS_UNKNOWN_ERROR;
336 rctx->socket.fd = usock(USOCK_UDP, server, usock_port(port));
338 if (rctx->socket.fd < 0) {
340 return UBUS_STATUS_UNKNOWN_ERROR;
344 rctx->addr_cur = blobmsg_data(tb[RPC_L_ADDRS]);
345 rctx->addr_rem = blobmsg_data_len(tb[RPC_L_ADDRS]);
347 avl_init(&rctx->request_ids, rrdns_cmp_id, false, NULL);
348 avl_init(&rctx->request_addrs, rrdns_cmp_addr, false, NULL);
350 rctx->timeout.cb = rrdns_handle_timeout;
351 uloop_timeout_set(&rctx->timeout, timeout);
353 rctx->socket.cb = rrdns_handle_response;
354 uloop_fd_add(&rctx->socket, ULOOP_READ);
356 blob_buf_init(&rctx->blob, 0);
359 rrdns_next_query(rctx);
361 ubus_defer_request(ctx, req, &rctx->request);
363 return UBUS_STATUS_OK;
368 rpc_rrdns_api_init(const struct rpc_daemon_ops *o, struct ubus_context *ctx)
370 static const struct ubus_method rrdns_methods[] = {
371 UBUS_METHOD("lookup", rpc_rrdns_lookup, rpc_lookup_policy),
374 static struct ubus_object_type rrdns_type =
375 UBUS_OBJECT_TYPE("rpcd-rrdns", rrdns_methods);
377 static struct ubus_object obj = {
378 .name = "network.rrdns",
380 .methods = rrdns_methods,
381 .n_methods = ARRAY_SIZE(rrdns_methods),
384 return ubus_add_object(ctx, &obj);
387 struct rpc_plugin rpc_plugin = {
388 .init = rpc_rrdns_api_init