xref: /openbsd/usr.sbin/lpd/resolver.c (revision 4cfece93)
1 /*	$OpenBSD: resolver.c,v 1.4 2019/04/06 10:35:48 eric Exp $	*/
2 
3 /*
4  * Copyright (c) 2017-2018 Eric Faurot <eric@openbsd.org>
5  *
6  * Permission to use, copy, modify, and 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 <sys/types.h>
20 #include <sys/socket.h>
21 #include <sys/tree.h>
22 #include <sys/queue.h>
23 #include <netinet/in.h>
24 
25 #include <asr.h>
26 #include <ctype.h>
27 #include <errno.h>
28 #include <imsg.h>
29 #include <limits.h>
30 #include <stdarg.h>
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <unistd.h>
35 
36 #include "lpd.h"
37 
38 #include "log.h"
39 #include "proc.h"
40 
41 #define p_resolver p_engine
42 
43 struct request {
44 	SPLAY_ENTRY(request)	 entry;
45 	uint32_t		 id;
46 	void			(*cb_ai)(void *, int, struct addrinfo *);
47 	void			(*cb_ni)(void *, int, const char *, const char *);
48 	void			*arg;
49 	struct addrinfo		*ai;
50 };
51 
52 struct session {
53 	uint32_t	 reqid;
54 	struct imsgproc	*proc;
55 	char		*host;
56 	char		*serv;
57 };
58 
59 SPLAY_HEAD(reqtree, request);
60 
61 static void resolver_init(void);
62 static void resolver_getaddrinfo_cb(struct asr_result *, void *);
63 static void resolver_getnameinfo_cb(struct asr_result *, void *);
64 
65 static int request_cmp(struct request *, struct request *);
66 SPLAY_PROTOTYPE(reqtree, request, entry, request_cmp);
67 
68 static struct reqtree reqs;
69 
70 void
71 resolver_getaddrinfo(const char *hostname, const char *servname,
72     const struct addrinfo *hints, void (*cb)(void *, int, struct addrinfo *),
73     void *arg)
74 {
75 	struct request *req;
76 
77 	resolver_init();
78 
79 	req = calloc(1, sizeof(*req));
80 	if (req == NULL) {
81 		cb(arg, EAI_MEMORY, NULL);
82 		return;
83 	}
84 
85 	while (req->id == 0 || SPLAY_FIND(reqtree, &reqs, req))
86 		req->id = arc4random();
87 	req->cb_ai = cb;
88 	req->arg = arg;
89 
90 	SPLAY_INSERT(reqtree, &reqs, req);
91 
92 	m_create(p_resolver, IMSG_GETADDRINFO, req->id, 0, -1);
93 	m_add_int(p_resolver, hints ? hints->ai_flags : 0);
94 	m_add_int(p_resolver, hints ? hints->ai_family : 0);
95 	m_add_int(p_resolver, hints ? hints->ai_socktype : 0);
96 	m_add_int(p_resolver, hints ? hints->ai_protocol : 0);
97 	m_add_string(p_resolver, hostname);
98 	m_add_string(p_resolver, servname);
99 	m_close(p_resolver);
100 }
101 
102 void
103 resolver_getnameinfo(const struct sockaddr *sa, int flags,
104     void(*cb)(void *, int, const char *, const char *), void *arg)
105 {
106 	struct request *req;
107 
108 	resolver_init();
109 
110 	req = calloc(1, sizeof(*req));
111 	if (req == NULL) {
112 		cb(arg, EAI_MEMORY, NULL, NULL);
113 		return;
114 	}
115 
116 	while (req->id == 0 || SPLAY_FIND(reqtree, &reqs, req))
117 		req->id = arc4random();
118 	req->cb_ni = cb;
119 	req->arg = arg;
120 
121 	SPLAY_INSERT(reqtree, &reqs, req);
122 
123 	m_create(p_resolver, IMSG_GETNAMEINFO, req->id, 0, -1);
124 	m_add_sockaddr(p_resolver, sa);
125 	m_add_int(p_resolver, flags);
126 	m_close(p_resolver);
127 }
128 
129 void
130 resolver_dispatch_request(struct imsgproc *proc, struct imsg *imsg)
131 {
132 	const char *hostname, *servname;
133 	struct session *s;
134 	struct asr_query *q;
135 	struct addrinfo hints;
136 	struct sockaddr_storage ss;
137 	struct sockaddr *sa;
138 	uint32_t reqid;
139 	int flags, save_errno;
140 
141 	reqid = imsg->hdr.peerid;
142 
143 	switch (imsg->hdr.type) {
144 
145 	case IMSG_GETADDRINFO:
146 		servname = NULL;
147 		memset(&hints, 0 , sizeof(hints));
148 		m_get_int(proc, &hints.ai_flags);
149 		m_get_int(proc, &hints.ai_family);
150 		m_get_int(proc, &hints.ai_socktype);
151 		m_get_int(proc, &hints.ai_protocol);
152 		m_get_string(proc, &hostname);
153 		m_get_string(proc, &servname);
154 		m_end(proc);
155 
156 		s = NULL;
157 		q = NULL;
158 		if ((s = calloc(1, sizeof(*s))) &&
159 		    (q = getaddrinfo_async(hostname, servname, &hints, NULL)) &&
160 		    (event_asr_run(q, resolver_getaddrinfo_cb, s))) {
161 			s->reqid = reqid;
162 			s->proc = proc;
163 			break;
164 		}
165 		save_errno = errno;
166 
167 		if (q)
168 			asr_abort(q);
169 		if (s)
170 			free(s);
171 
172 		m_create(proc, IMSG_GETADDRINFO_END, reqid, 0, -1);
173 		m_add_int(proc, EAI_SYSTEM);
174 		m_add_int(proc, save_errno);
175 		m_close(proc);
176 		break;
177 
178 	case IMSG_GETNAMEINFO:
179 		sa = (struct sockaddr*)&ss;
180 		m_get_sockaddr(proc, sa);
181 		m_get_int(proc, &flags);
182 		m_end(proc);
183 
184 		s = NULL;
185 		q = NULL;
186 		if ((s = calloc(1, sizeof(*s))) &&
187 		    (s->host = malloc(NI_MAXHOST)) &&
188 		    (s->serv = malloc(NI_MAXSERV)) &&
189 		    (q = getnameinfo_async(sa, sa->sa_len, s->host, NI_MAXHOST,
190 			s->serv, NI_MAXSERV, flags, NULL)) &&
191 		    (event_asr_run(q, resolver_getnameinfo_cb, s))) {
192 			s->reqid = reqid;
193 			s->proc = proc;
194 			break;
195 		}
196 		save_errno = errno;
197 
198 		if (q)
199 			asr_abort(q);
200 		if (s) {
201 			free(s->host);
202 			free(s->serv);
203 			free(s);
204 		}
205 
206 		m_create(proc, IMSG_GETNAMEINFO, reqid, 0, -1);
207 		m_add_int(proc, EAI_SYSTEM);
208 		m_add_int(proc, save_errno);
209 		m_add_string(proc, NULL);
210 		m_add_string(proc, NULL);
211 		m_close(proc);
212 		break;
213 
214 	default:
215 		fatalx("%s: %s", __func__, log_fmt_imsgtype(imsg->hdr.type));
216 	}
217 }
218 
219 void
220 resolver_dispatch_result(struct imsgproc *proc, struct imsg *imsg)
221 {
222 	struct request key, *req;
223 	struct sockaddr_storage ss;
224 	struct addrinfo *ai;
225 	const char *cname, *host, *serv;
226 	int gai_errno;
227 
228 	key.id = imsg->hdr.peerid;
229 	req = SPLAY_FIND(reqtree, &reqs, &key);
230 	if (req == NULL)
231 		fatalx("%s: unknown request %08x", __func__, imsg->hdr.peerid);
232 
233 	switch (imsg->hdr.type) {
234 
235 	case IMSG_GETADDRINFO:
236 		ai = calloc(1, sizeof(*ai));
237 		if (ai == NULL) {
238 			log_warn("%s: calloc", __func__);
239 			break;
240 		}
241 		m_get_int(proc, &ai->ai_flags);
242 		m_get_int(proc, &ai->ai_family);
243 		m_get_int(proc, &ai->ai_socktype);
244 		m_get_int(proc, &ai->ai_protocol);
245 		m_get_sockaddr(proc, (struct sockaddr *)&ss);
246 		m_get_string(proc, &cname);
247 		m_end(proc);
248 
249 		ai->ai_addr = malloc(ss.ss_len);
250 		if (ai->ai_addr == NULL) {
251 			log_warn("%s: malloc", __func__);
252 			free(ai);
253 			break;
254 		}
255 
256 		memmove(ai->ai_addr, &ss, ss.ss_len);
257 
258 		if (cname) {
259 			ai->ai_canonname = strdup(cname);
260 			if (ai->ai_canonname == NULL) {
261 				log_warn("%s: strdup", __func__);
262 				free(ai->ai_addr);
263 				free(ai);
264 				break;
265 			}
266 		}
267 
268 		ai->ai_next = req->ai;
269 		req->ai = ai;
270 		break;
271 
272 	case IMSG_GETADDRINFO_END:
273 		m_get_int(proc, &gai_errno);
274 		m_get_int(proc, &errno);
275 		m_end(proc);
276 
277 		SPLAY_REMOVE(reqtree, &reqs, req);
278 		req->cb_ai(req->arg, gai_errno, req->ai);
279 		free(req);
280 		break;
281 
282 	case IMSG_GETNAMEINFO:
283 		m_get_int(proc, &gai_errno);
284 		m_get_int(proc, &errno);
285 		m_get_string(proc, &host);
286 		m_get_string(proc, &serv);
287 		m_end(proc);
288 
289 		SPLAY_REMOVE(reqtree, &reqs, req);
290 		req->cb_ni(req->arg, gai_errno, host, serv);
291 		free(req);
292 		break;
293 	}
294 }
295 
296 static void
297 resolver_init(void)
298 {
299 	static int init = 0;
300 
301 	if (init == 0) {
302 		SPLAY_INIT(&reqs);
303 		init = 1;
304 	}
305 }
306 
307 static void
308 resolver_getaddrinfo_cb(struct asr_result *ar, void *arg)
309 {
310 	struct session *s = arg;
311 	struct addrinfo *ai;
312 
313 	for (ai = ar->ar_addrinfo; ai; ai = ai->ai_next) {
314 		m_create(s->proc, IMSG_GETADDRINFO, s->reqid, 0, -1);
315 		m_add_int(s->proc, ai->ai_flags);
316 		m_add_int(s->proc, ai->ai_family);
317 		m_add_int(s->proc, ai->ai_socktype);
318 		m_add_int(s->proc, ai->ai_protocol);
319 		m_add_sockaddr(s->proc, ai->ai_addr);
320 		m_add_string(s->proc, ai->ai_canonname);
321 		m_close(s->proc);
322 	}
323 
324 	m_create(s->proc, IMSG_GETADDRINFO_END, s->reqid, 0, -1);
325 	m_add_int(s->proc, ar->ar_gai_errno);
326 	m_add_int(s->proc, ar->ar_errno);
327 	m_close(s->proc);
328 
329 	if (ar->ar_addrinfo)
330 		freeaddrinfo(ar->ar_addrinfo);
331 	free(s);
332 }
333 
334 static void
335 resolver_getnameinfo_cb(struct asr_result *ar, void *arg)
336 {
337 	struct session *s = arg;
338 
339 	m_create(s->proc, IMSG_GETNAMEINFO, s->reqid, 0, -1);
340 	m_add_int(s->proc, ar->ar_gai_errno);
341 	m_add_int(s->proc, ar->ar_errno);
342 	m_add_string(s->proc, ar->ar_gai_errno ? NULL : s->host);
343 	m_add_string(s->proc, ar->ar_gai_errno ? NULL : s->serv);
344 	m_close(s->proc);
345 
346 	free(s->host);
347 	free(s->serv);
348 	free(s);
349 }
350 
351 static int
352 request_cmp(struct request *a, struct request *b)
353 {
354 	if (a->id < b->id)
355 		return -1;
356 	if (a->id > b->id)
357 		return 1;
358 	return 0;
359 }
360 
361 SPLAY_GENERATE(reqtree, request, entry, request_cmp);
362