1/*
2 * dns.c
3 *
4 * Copyright (C) 2018 Aerospike, Inc.
5 *
6 * Portions may be licensed to Aerospike, Inc. under one or more contributor
7 * license agreements.
8 *
9 * This program is free software: you can redistribute it and/or modify it under
10 * the terms of the GNU Affero General Public License as published by the Free
11 * Software Foundation, either version 3 of the License, or (at your option) any
12 * later version.
13 *
14 * This program is distributed in the hope that it will be useful, but WITHOUT
15 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
16 * FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
17 * details.
18 *
19 * You should have received a copy of the GNU Affero General Public License
20 * along with this program. If not, see http://www.gnu.org/licenses/
21 */
22
23#include "dns.h"
24
25#include <errno.h>
26
27#include "citrusleaf/cf_queue.h"
28
29#include "cf_thread.h"
30#include "fault.h"
31
32/*
33 * ----------------------------------------------------------------------------
34 * Constants.
35 * ----------------------------------------------------------------------------
36 */
37/**
38 * Number of resolver threads.
39 */
40#define CF_DNS_NUM_RESOLVERS 2
41
42/**
43 * Initial size of the request queue.
44 */
45#define CF_DNS_REQ_QUEUE_INIT_SIZE 10
46
47/**
48 * The logging context.
49 */
50#define CF_DNS_LOG_CONTEXT CF_SOCKET
51
52/*
53 * ----------------------------------------------------------------------------
54 * Private data structures.
55 * ----------------------------------------------------------------------------
56 */
57
58/**
59 * Internal representation of a resolve request.
60 */
61typedef struct cf_dns_resolve_req_s
62{
63 /**
64 * The hostname to resolve.
65 */
66 char hostname[DNS_NAME_MAX_SIZE + 1];
67
68 /**
69 * Name resolution criteria for resolution.
70 */
71 addrinfo hints;
72
73 /**
74 * The call back function to invoke.
75 */
76 cf_dns_resolve_cb cb;
77
78 /**
79 * Userdata to be passed on to the callback function.
80 */
81 void* udata;
82} cf_dns_resolve_req;
83
84/*
85 * ----------------------------------------------------------------------------
86 * Globals.
87 * ----------------------------------------------------------------------------
88 */
89static cf_queue g_req_queue;
90
91/*
92 * ----------------------------------------------------------------------------
93 * Private functions.
94 * ----------------------------------------------------------------------------
95 */
96
97/**
98 * DNS resolver thread.
99 */
100static void*
101cf_dns_resolve_worker(void* arg)
102{
103 cf_dns_resolve_req req = { { 0 } };
104
105 while (cf_queue_pop(&g_req_queue, &req, CF_QUEUE_FOREVER) == CF_QUEUE_OK) {
106 addrinfo *addrs = NULL;
107 int status = getaddrinfo(req.hostname, NULL, &req.hints, &addrs);
108 req.cb(status, req.hostname, addrs, req.udata);
109 }
110
111 return NULL;
112}
113
114/*
115 * ----------------------------------------------------------------------------
116 * Public functions.
117 * ----------------------------------------------------------------------------
118 */
119
120/**
121 * Start the dns resolver threads for asynchronous processing.
122 */
123void
124cf_dns_init()
125{
126 // Initialize the request queue.
127 cf_queue_init(&g_req_queue, sizeof(cf_dns_resolve_req),
128 CF_DNS_REQ_QUEUE_INIT_SIZE, true);
129
130 // Start the resolver threads.
131 for (int i = 0; i < CF_DNS_NUM_RESOLVERS; i++) {
132 cf_thread_create_detached(cf_dns_resolve_worker, NULL);
133 }
134}
135
136/**
137 * Resolves hostname to ip addresses asynchronously.
138 *
139 * @param hostname the hostname/devicename/ip address to resolve. Should be of
140 * length less than or equal to DNS_NAME_MAX_SIZE.
141 * @param hints criteria for selecting addresses. Can be NULL.
142 * @param cb the call back invoked after resolution, either successful or
143 * unsuccessful.
144 * @param udata user define data passed to the callback. Can be NULL. Is not
145 * freed, the caller shoud be own the lifecycle of udata.
146 */
147void
148cf_dns_resolve_a(const char* hostname, addrinfo* hints, cf_dns_resolve_cb cb,
149 void* udata)
150{
151 // Create a new resolution request.
152 cf_dns_resolve_req req = { { 0 } };
153 strncpy(req.hostname, hostname, sizeof(req.hostname));
154 req.hostname[DNS_NAME_MAX_SIZE] = 0;
155 memcpy(&req.hints, hints, sizeof(req.hints));
156 req.cb = cb;
157 req.udata = udata;
158
159 // Queue the request.
160 cf_queue_push(&g_req_queue, &req);
161}
162
163/**
164 * Resolve hostname to ip addresses.
165 *
166 * @param hostname the hostname/devicename/ip address to resolve.
167 * @param hints criteria for selecting addresses. Can be NULL.
168 * @param addrs the result of resolution on success. If call is successful
169 * should be freed after use using cf_dns_free.
170 * @return 0 on success, EAI_* error code otherwise.
171 */
172CF_MUST_CHECK int
173cf_dns_resolve(const char* hostname, addrinfo* hints, addrinfo** addrs)
174{
175 return getaddrinfo(hostname, NULL, hints, addrs);
176}
177
178/**
179 * Convert dns error code to string.
180 * @param errcode the error code not equal to zero.
181 */
182const char*
183cf_dns_strerror(int errcode)
184{
185 return gai_strerror(errcode);
186}
187
188/**
189 * Free space allocated for resolved addresses.
190 *
191 * @param addrs the addresses to free up. Can be NULL.
192 */
193void
194cf_dns_free(addrinfo* addrs)
195{
196 if (addrs) {
197 freeaddrinfo(addrs);
198 }
199}
200