1 /*
2 **  Copyright (c) 2010-2012, The Trusted Domain Project.  All rights reserved.
3 **
4 */
5 
6 /* for Solaris */
7 #ifndef _REENTRANT
8 # define _REENTRANT
9 #endif /* ! REENTRANT */
10 
11 /* system includes */
12 #include <sys/param.h>
13 #include <sys/types.h>
14 #include <netinet/in.h>
15 #include <arpa/nameser.h>
16 #include <resolv.h>
17 #include <netdb.h>
18 #include <stdlib.h>
19 #include <string.h>
20 #include <assert.h>
21 #include <errno.h>
22 
23 /* libopendkim includes */
24 #include "dkim.h"
25 #include "dkim-dns.h"
26 
27 /* OpenDKIM includes */
28 #include "build-config.h"
29 
30 /* macros, limits, etc. */
31 #ifndef MAXPACKET
32 # define MAXPACKET      8192
33 #endif /* ! MAXPACKET */
34 
35 /*
36 **  Standard UNIX resolver stub functions
37 */
38 
39 struct dkim_res_qh
40 {
41 	int		rq_error;
42 	int		rq_dnssec;
43 	size_t		rq_buflen;
44 };
45 
46 /*
47 **  DKIM_RES_INIT -- initialize the resolver
48 **
49 **  Parameters:
50 **  	srv -- service handle (returned)
51 **
52 **  Return value
53 **  	0 on success, !0 on failure
54 */
55 
56 int
dkim_res_init(void ** srv)57 dkim_res_init(void **srv)
58 {
59 #ifdef HAVE_RES_NINIT
60 	struct __res_state *res;
61 
62 	res = malloc(sizeof(struct __res_state));
63 	if (res == NULL)
64 		return -1;
65 
66 	memset(res, '\0', sizeof(struct __res_state));
67 
68 	if (res_ninit(res) != 0)
69 	{
70 		free(res);
71 		return -1;
72 	}
73 
74 	*srv = res;
75 
76 	return 0;
77 #else /* HAVE_RES_NINIT */
78 	if (res_init() == 0)
79 	{
80 		*srv = (void *) 0x01;
81 		return 0;
82 	}
83 	else
84 	{
85 		return -1;
86 	}
87 #endif /* HAVE_RES_NINIT */
88 }
89 
90 /*
91 **  DKIM_RES_CLOSE -- shut down the resolver
92 **
93 **  Parameters:
94 **  	srv -- service handle
95 **
96 **  Return value:
97 **  	None.
98 */
99 
100 void
dkim_res_close(void * srv)101 dkim_res_close(void *srv)
102 {
103 #ifdef HAVE_RES_NINIT
104 	struct __res_state *res;
105 
106 	res = srv;
107 
108 	if (res != NULL)
109 	{
110 		res_nclose(res);
111 		free(res);
112 	}
113 #endif /* HAVE_RES_NINIT */
114 }
115 
116 /*
117 **  DKIM_RES_CANCEL -- cancel a pending resolver query
118 **
119 **  Parameters:
120 **  	srv -- query service handle (ignored)
121 **  	qh -- query handle (ignored)
122 **
123 **  Return value:
124 **  	0 on success, !0 on error
125 **
126 **  Notes:
127 **  	The standard UNIX resolver is synchronous, so in theory this can
128 **  	never get called.  We have not yet got any use cases for one thread
129 **  	canceling another thread's pending queries, so for now just return 0.
130 */
131 
132 int
dkim_res_cancel(void * srv,void * qh)133 dkim_res_cancel(void *srv, void *qh)
134 {
135 	if (qh != NULL)
136 		free(qh);
137 
138 	return 0;
139 }
140 
141 /*
142 **  DKIM_RES_QUERY -- initiate a DNS query
143 **
144 **  Parameters:
145 **  	srv -- service handle (ignored)
146 **  	type -- RR type to query
147 **  	query -- the question to ask
148 **  	buf -- where to write the answer
149 **  	buflen -- bytes at "buf"
150 ** 	qh -- query handle, used with dkim_res_waitreply
151 **
152 **  Return value:
153 **  	0 on success, -1 on error
154 **
155 **  Notes:
156 **  	This is a stub for the stock UNIX resolver (res_) functions, which
157 **  	are synchronous so no handle needs to be created, so "qh" is set to
158 **  	"buf".  "buf" is actually populated before this returns (unless
159 **  	there's an error).
160 */
161 
162 int
dkim_res_query(void * srv,int type,unsigned char * query,unsigned char * buf,size_t buflen,void ** qh)163 dkim_res_query(void *srv, int type, unsigned char *query, unsigned char *buf,
164                size_t buflen, void **qh)
165 {
166 	int n;
167 	int ret;
168 	struct dkim_res_qh *rq;
169 	unsigned char qbuf[HFIXEDSZ + MAXPACKET];
170 #ifdef HAVE_RES_NINIT
171 	struct __res_state *statp;
172 #endif /* HAVE_RES_NINIT */
173 
174 #ifdef HAVE_RES_NINIT
175 	statp = srv;
176 	n = res_nmkquery(statp, QUERY, (char *) query, C_IN, type, NULL, 0,
177 	                 NULL, qbuf, sizeof qbuf);
178 #else /* HAVE_RES_NINIT */
179 	n = res_mkquery(QUERY, (char *) query, C_IN, type, NULL, 0, NULL, qbuf,
180 	                sizeof qbuf);
181 #endif /* HAVE_RES_NINIT */
182 	if (n == (size_t) -1)
183 		return DKIM_DNS_ERROR;
184 
185 #ifdef HAVE_RES_NINIT
186 	ret = res_nsend(statp, qbuf, n, buf, buflen);
187 #else /* HAVE_RES_NINIT */
188 	ret = res_send(qbuf, n, buf, buflen);
189 #endif /* HAVE_RES_NINIT */
190 	if (ret == -1)
191 		return DKIM_DNS_ERROR;
192 
193 	rq = (struct dkim_res_qh *) malloc(sizeof *rq);
194 	if (rq == NULL)
195 		return DKIM_DNS_ERROR;
196 
197 	rq->rq_dnssec = DKIM_DNSSEC_UNKNOWN;
198 	if (ret == -1)
199 	{
200 		rq->rq_error = errno;
201 		rq->rq_buflen = 0;
202 	}
203 	else
204 	{
205 		rq->rq_error = 0;
206 		rq->rq_buflen = (size_t) ret;
207 	}
208 
209 	*qh = (void *) rq;
210 
211 	return DKIM_DNS_SUCCESS;
212 }
213 
214 /*
215 **  DKIM_RES_WAITREPLY -- wait for a reply to a pending query
216 **
217 **  Parameters:
218 **  	srv -- service handle
219 **  	qh -- query handle
220 **  	to -- timeout
221 **  	bytes -- number of bytes in the reply (returned)
222 **  	error -- error code (returned)
223 **
224 **  Return value:
225 **  	A DKIM_DNS_* code.
226 **
227 **  Notes:
228 **  	Since the stock UNIX resolver is synchronous, the reply was completed
229 ** 	before dkim_res_query() returned, and thus this is almost a no-op.
230 */
231 
232 int
dkim_res_waitreply(void * srv,void * qh,struct timeval * to,size_t * bytes,int * error,int * dnssec)233 dkim_res_waitreply(void *srv, void *qh, struct timeval *to, size_t *bytes,
234                    int *error, int *dnssec)
235 {
236 	struct dkim_res_qh *rq;
237 
238 	assert(qh != NULL);
239 
240 	rq = qh;
241 
242 	if (bytes != NULL)
243 		*bytes = rq->rq_buflen;
244 	if (error != NULL)
245 		*error = rq->rq_error;
246 	if (dnssec != NULL)
247 		*dnssec = rq->rq_dnssec;
248 
249 	return DKIM_DNS_SUCCESS;
250 }
251 
252 /*
253 **  DKIM_RES_SETNS -- set nameserver list
254 **
255 **  Parameters:
256 **  	srv -- service handle
257 **  	nslist -- nameserver list, as a string
258 **
259 **  Return value:
260 **  	DKIM_DNS_SUCCESS -- success
261 **  	DKIM_DNS_ERROR -- error
262 */
263 
264 int
dkim_res_nslist(void * srv,const char * nslist)265 dkim_res_nslist(void *srv, const char *nslist)
266 {
267 #ifdef HAVE_RES_SETSERVERS
268 	int nscount = 0;
269 	char *tmp;
270 	char *ns;
271 	char *last = NULL;
272 	struct sockaddr_in in;
273 # ifdef AF_INET6
274 	struct sockaddr_in6 in6;
275 # endif /* AF_INET6 */
276 	struct state *res;
277 	res_sockaddr_union nses[MAXNS];
278 
279 	assert(srv != NULL);
280 	assert(nslist != NULL);
281 
282 	memset(nses, '\0', sizeof nses);
283 
284 	tmp = strdup(nslist);
285 	if (tmp == NULL)
286 		return DKIM_DNS_ERROR;
287 
288 	for (ns = strtok_r(tmp, ",", &last);
289 	     ns != NULL && nscount < MAXNS;
290 	     ns = strtok_r(NULL, ",", &last)
291 	{
292 		memset(&in, '\0', sizeof in);
293 # ifdef AF_INET6
294 		memset(&in6, '\0', sizeof in6);
295 # endif /* AF_INET6 */
296 
297 		if (inet_pton(AF_INET, ns, (struct in_addr *) &in.sin_addr,
298 		              sizeof in.sin_addr) == 1)
299 		{
300 			in.sin_family= AF_INET;
301 			in.sin_port = htons(DNSPORT);
302 			memcpy(&nses[nscount].sin, &in,
303 			       sizeof nses[nscount].sin);
304 			nscount++;
305 		}
306 # ifdef AF_INET6
307 		else if (inet_pton(AF_INET6, ns,
308 		                   (struct in6_addr *) &in6.sin6_addr,
309 		                   sizeof in6.sin6_addr) == 1)
310 		{
311 			in6.sin6_family= AF_INET6;
312 			in6.sin6_port = htons(DNSPORT);
313 			memcpy(&nses[nscount].sin6, &in6,
314 			       sizeof nses[nscount].sin6);
315 			nscount++;
316 		}
317 # endif /* AF_INET6 */
318 		else
319 		{
320 			free(tmp);
321 			return DKIM_DNS_ERROR;
322 		}
323 	}
324 
325 	res = srv;
326 	res_setservers(res, nses, nscount);
327 
328 	free(tmp);
329 #endif /* HAVE_RES_SETSERVERS */
330 
331 	return DKIM_DNS_SUCCESS;
332 }
333