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