]>
Commit | Line | Data |
---|---|---|
1 | /* SPDX-License-Identifier: LGPL-2.1-or-later */ | |
2 | ||
3 | #include <netdb.h> | |
4 | #include <poll.h> | |
5 | #include <pthread.h> | |
6 | #include <signal.h> | |
7 | #include <stdio.h> | |
8 | #include <string.h> | |
9 | #include <threads.h> | |
10 | #include <unistd.h> | |
11 | ||
12 | #include "sd-event.h" | |
13 | #include "sd-resolve.h" | |
14 | ||
15 | #include "alloc-util.h" | |
16 | #include "dns-def.h" | |
17 | #include "errno-util.h" | |
18 | #include "fd-util.h" | |
19 | #include "io-util.h" | |
20 | #include "iovec-util.h" | |
21 | #include "list.h" | |
22 | #include "log.h" | |
23 | #include "memory-util.h" | |
24 | #include "process-util.h" | |
25 | #include "resolve-private.h" | |
26 | #include "socket-util.h" | |
27 | ||
28 | #define WORKERS_MIN 1U | |
29 | #define WORKERS_MAX 16U | |
30 | #define QUERIES_MAX 256U | |
31 | #define BUFSIZE 10240U | |
32 | ||
33 | typedef enum { | |
34 | REQUEST_ADDRINFO, | |
35 | RESPONSE_ADDRINFO, | |
36 | REQUEST_NAMEINFO, | |
37 | RESPONSE_NAMEINFO, | |
38 | REQUEST_TERMINATE, | |
39 | RESPONSE_DIED | |
40 | } QueryType; | |
41 | ||
42 | enum { | |
43 | REQUEST_RECV_FD, | |
44 | REQUEST_SEND_FD, | |
45 | RESPONSE_RECV_FD, | |
46 | RESPONSE_SEND_FD, | |
47 | _FD_MAX | |
48 | }; | |
49 | ||
50 | struct sd_resolve { | |
51 | unsigned n_ref; | |
52 | ||
53 | bool dead:1; | |
54 | pid_t original_pid; | |
55 | ||
56 | int fds[_FD_MAX]; | |
57 | ||
58 | pthread_t workers[WORKERS_MAX]; | |
59 | unsigned n_valid_workers; | |
60 | ||
61 | unsigned current_id; | |
62 | sd_resolve_query* query_array[QUERIES_MAX]; | |
63 | unsigned n_queries, n_done, n_outstanding; | |
64 | ||
65 | sd_event_source *event_source; | |
66 | sd_event *event; | |
67 | ||
68 | sd_resolve_query *current; | |
69 | ||
70 | sd_resolve **default_resolve_ptr; | |
71 | pid_t tid; | |
72 | ||
73 | LIST_HEAD(sd_resolve_query, queries); | |
74 | }; | |
75 | ||
76 | struct sd_resolve_query { | |
77 | unsigned n_ref; | |
78 | ||
79 | sd_resolve *resolve; | |
80 | ||
81 | QueryType type:4; | |
82 | bool done:1; | |
83 | bool floating:1; | |
84 | unsigned id; | |
85 | ||
86 | int ret; | |
87 | int _errno; | |
88 | int _h_errno; | |
89 | struct addrinfo *addrinfo; | |
90 | char *serv, *host; | |
91 | ||
92 | union { | |
93 | sd_resolve_getaddrinfo_handler_t getaddrinfo_handler; | |
94 | sd_resolve_getnameinfo_handler_t getnameinfo_handler; | |
95 | }; | |
96 | ||
97 | void *userdata; | |
98 | sd_resolve_destroy_t destroy_callback; | |
99 | ||
100 | LIST_FIELDS(sd_resolve_query, queries); | |
101 | }; | |
102 | ||
103 | typedef struct RHeader { | |
104 | QueryType type; | |
105 | unsigned id; | |
106 | size_t length; | |
107 | } RHeader; | |
108 | ||
109 | typedef struct AddrInfoRequest { | |
110 | struct RHeader header; | |
111 | bool hints_valid; | |
112 | int ai_flags; | |
113 | int ai_family; | |
114 | int ai_socktype; | |
115 | int ai_protocol; | |
116 | size_t node_len, service_len; | |
117 | } AddrInfoRequest; | |
118 | ||
119 | typedef struct AddrInfoResponse { | |
120 | struct RHeader header; | |
121 | int ret; | |
122 | int _errno; | |
123 | int _h_errno; | |
124 | /* followed by addrinfo_serialization[] */ | |
125 | } AddrInfoResponse; | |
126 | ||
127 | typedef struct AddrInfoSerialization { | |
128 | int ai_flags; | |
129 | int ai_family; | |
130 | int ai_socktype; | |
131 | int ai_protocol; | |
132 | size_t ai_addrlen; | |
133 | size_t canonname_len; | |
134 | /* Followed by ai_addr amd ai_canonname with variable lengths */ | |
135 | } AddrInfoSerialization; | |
136 | ||
137 | typedef struct NameInfoRequest { | |
138 | struct RHeader header; | |
139 | int flags; | |
140 | socklen_t sockaddr_len; | |
141 | bool gethost:1, getserv:1; | |
142 | } NameInfoRequest; | |
143 | ||
144 | typedef struct NameInfoResponse { | |
145 | struct RHeader header; | |
146 | size_t hostlen, servlen; | |
147 | int ret; | |
148 | int _errno; | |
149 | int _h_errno; | |
150 | } NameInfoResponse; | |
151 | ||
152 | typedef union Packet { | |
153 | RHeader rheader; | |
154 | AddrInfoRequest addrinfo_request; | |
155 | AddrInfoResponse addrinfo_response; | |
156 | NameInfoRequest nameinfo_request; | |
157 | NameInfoResponse nameinfo_response; | |
158 | } Packet; | |
159 | ||
160 | static int getaddrinfo_done(sd_resolve_query* q); | |
161 | static int getnameinfo_done(sd_resolve_query *q); | |
162 | ||
163 | static void resolve_query_disconnect(sd_resolve_query *q); | |
164 | ||
165 | #define RESOLVE_DONT_DESTROY(resolve) \ | |
166 | _cleanup_(sd_resolve_unrefp) _unused_ sd_resolve *_dont_destroy_##resolve = sd_resolve_ref(resolve) | |
167 | ||
168 | static void query_assign_errno(sd_resolve_query *q, int ret, int error, int h_error) { | |
169 | assert(q); | |
170 | ||
171 | q->ret = ret; | |
172 | q->_errno = abs(error); | |
173 | q->_h_errno = h_error; | |
174 | } | |
175 | ||
176 | static int send_died(int out_fd) { | |
177 | RHeader rh = { | |
178 | .type = RESPONSE_DIED, | |
179 | .length = sizeof(RHeader), | |
180 | }; | |
181 | ||
182 | assert(out_fd >= 0); | |
183 | ||
184 | if (send(out_fd, &rh, rh.length, MSG_NOSIGNAL) < 0) | |
185 | return -errno; | |
186 | ||
187 | return 0; | |
188 | } | |
189 | ||
190 | static void *serialize_addrinfo(void *p, const struct addrinfo *ai, size_t *length, size_t maxlength) { | |
191 | AddrInfoSerialization s; | |
192 | size_t cnl, l; | |
193 | ||
194 | assert(p); | |
195 | assert(ai); | |
196 | assert(length); | |
197 | assert(*length <= maxlength); | |
198 | ||
199 | cnl = ai->ai_canonname ? strlen(ai->ai_canonname)+1 : 0; | |
200 | l = sizeof(AddrInfoSerialization) + ai->ai_addrlen + cnl; | |
201 | ||
202 | if (*length + l > maxlength) | |
203 | return NULL; | |
204 | ||
205 | s = (AddrInfoSerialization) { | |
206 | .ai_flags = ai->ai_flags, | |
207 | .ai_family = ai->ai_family, | |
208 | .ai_socktype = ai->ai_socktype, | |
209 | .ai_protocol = ai->ai_protocol, | |
210 | .ai_addrlen = ai->ai_addrlen, | |
211 | .canonname_len = cnl, | |
212 | }; | |
213 | ||
214 | memcpy((uint8_t*) p, &s, sizeof(AddrInfoSerialization)); | |
215 | memcpy((uint8_t*) p + sizeof(AddrInfoSerialization), ai->ai_addr, ai->ai_addrlen); | |
216 | memcpy_safe((char*) p + sizeof(AddrInfoSerialization) + ai->ai_addrlen, | |
217 | ai->ai_canonname, cnl); | |
218 | ||
219 | *length += l; | |
220 | return (uint8_t*) p + l; | |
221 | } | |
222 | ||
223 | static int send_addrinfo_reply( | |
224 | int out_fd, | |
225 | unsigned id, | |
226 | int ret, | |
227 | struct addrinfo *ai, | |
228 | int _errno, | |
229 | int _h_errno) { | |
230 | ||
231 | AddrInfoResponse resp = {}; | |
232 | union { | |
233 | AddrInfoSerialization ais; | |
234 | uint8_t space[BUFSIZE]; | |
235 | } buffer; | |
236 | struct iovec iov[2]; | |
237 | struct msghdr mh; | |
238 | ||
239 | assert(out_fd >= 0); | |
240 | ||
241 | resp = (AddrInfoResponse) { | |
242 | .header.type = RESPONSE_ADDRINFO, | |
243 | .header.id = id, | |
244 | .header.length = sizeof(AddrInfoResponse), | |
245 | .ret = ret, | |
246 | ._errno = _errno, | |
247 | ._h_errno = _h_errno, | |
248 | }; | |
249 | ||
250 | msan_unpoison(&resp, sizeof(resp)); | |
251 | ||
252 | if (ret == 0 && ai) { | |
253 | void *p = &buffer; | |
254 | struct addrinfo *k; | |
255 | ||
256 | for (k = ai; k; k = k->ai_next) { | |
257 | p = serialize_addrinfo(p, k, &resp.header.length, (uint8_t*) &buffer + BUFSIZE - (uint8_t*) p); | |
258 | if (!p) { | |
259 | freeaddrinfo(ai); | |
260 | return -ENOBUFS; | |
261 | } | |
262 | } | |
263 | } | |
264 | ||
265 | if (ai) | |
266 | freeaddrinfo(ai); | |
267 | ||
268 | iov[0] = IOVEC_MAKE(&resp, sizeof(AddrInfoResponse)); | |
269 | iov[1] = IOVEC_MAKE(&buffer, resp.header.length - sizeof(AddrInfoResponse)); | |
270 | ||
271 | mh = (struct msghdr) { | |
272 | .msg_iov = iov, | |
273 | .msg_iovlen = ELEMENTSOF(iov) | |
274 | }; | |
275 | ||
276 | if (sendmsg(out_fd, &mh, MSG_NOSIGNAL) < 0) | |
277 | return -errno; | |
278 | ||
279 | return 0; | |
280 | } | |
281 | ||
282 | static int send_nameinfo_reply( | |
283 | int out_fd, | |
284 | unsigned id, | |
285 | int ret, | |
286 | const char *host, | |
287 | const char *serv, | |
288 | int _errno, | |
289 | int _h_errno) { | |
290 | ||
291 | NameInfoResponse resp = {}; | |
292 | struct iovec iov[3]; | |
293 | struct msghdr mh; | |
294 | size_t hl, sl; | |
295 | ||
296 | assert(out_fd >= 0); | |
297 | ||
298 | sl = serv ? strlen(serv)+1 : 0; | |
299 | hl = host ? strlen(host)+1 : 0; | |
300 | ||
301 | resp = (NameInfoResponse) { | |
302 | .header.type = RESPONSE_NAMEINFO, | |
303 | .header.id = id, | |
304 | .header.length = sizeof(NameInfoResponse) + hl + sl, | |
305 | .hostlen = hl, | |
306 | .servlen = sl, | |
307 | .ret = ret, | |
308 | ._errno = _errno, | |
309 | ._h_errno = _h_errno, | |
310 | }; | |
311 | ||
312 | msan_unpoison(&resp, sizeof(resp)); | |
313 | ||
314 | iov[0] = IOVEC_MAKE(&resp, sizeof(NameInfoResponse)); | |
315 | iov[1] = IOVEC_MAKE((void*) host, hl); | |
316 | iov[2] = IOVEC_MAKE((void*) serv, sl); | |
317 | ||
318 | mh = (struct msghdr) { | |
319 | .msg_iov = iov, | |
320 | .msg_iovlen = ELEMENTSOF(iov) | |
321 | }; | |
322 | ||
323 | if (sendmsg(out_fd, &mh, MSG_NOSIGNAL) < 0) | |
324 | return -errno; | |
325 | ||
326 | return 0; | |
327 | } | |
328 | ||
329 | static int handle_request(int out_fd, const Packet *packet, size_t length) { | |
330 | const RHeader *req; | |
331 | ||
332 | assert(out_fd >= 0); | |
333 | assert(packet); | |
334 | ||
335 | req = &packet->rheader; | |
336 | ||
337 | assert_return(length >= sizeof(RHeader), -EIO); | |
338 | assert_return(length == req->length, -EIO); | |
339 | ||
340 | switch (req->type) { | |
341 | ||
342 | case REQUEST_ADDRINFO: { | |
343 | const AddrInfoRequest *ai_req = &packet->addrinfo_request; | |
344 | struct addrinfo hints, *result = NULL; | |
345 | const char *node, *service; | |
346 | int ret; | |
347 | ||
348 | assert_return(length >= sizeof(AddrInfoRequest), -EBADMSG); | |
349 | assert_return(length == sizeof(AddrInfoRequest) + ai_req->node_len + ai_req->service_len, -EBADMSG); | |
350 | ||
351 | hints = (struct addrinfo) { | |
352 | .ai_flags = ai_req->ai_flags, | |
353 | .ai_family = ai_req->ai_family, | |
354 | .ai_socktype = ai_req->ai_socktype, | |
355 | .ai_protocol = ai_req->ai_protocol, | |
356 | }; | |
357 | ||
358 | msan_unpoison(&hints, sizeof(hints)); | |
359 | ||
360 | node = ai_req->node_len ? (const char*) ai_req + sizeof(AddrInfoRequest) : NULL; | |
361 | service = ai_req->service_len ? (const char*) ai_req + sizeof(AddrInfoRequest) + ai_req->node_len : NULL; | |
362 | ||
363 | ret = getaddrinfo(node, service, | |
364 | ai_req->hints_valid ? &hints : NULL, | |
365 | &result); | |
366 | ||
367 | /* send_addrinfo_reply() frees result */ | |
368 | return send_addrinfo_reply(out_fd, req->id, ret, result, errno, h_errno); | |
369 | } | |
370 | ||
371 | case REQUEST_NAMEINFO: { | |
372 | const NameInfoRequest *ni_req = &packet->nameinfo_request; | |
373 | char hostbuf[NI_MAXHOST], servbuf[NI_MAXSERV]; | |
374 | union sockaddr_union sa; | |
375 | int ret; | |
376 | ||
377 | assert_return(length >= sizeof(NameInfoRequest), -EBADMSG); | |
378 | assert_return(length == sizeof(NameInfoRequest) + ni_req->sockaddr_len, -EBADMSG); | |
379 | assert_return(ni_req->sockaddr_len <= sizeof(sa), -EBADMSG); | |
380 | ||
381 | memcpy(&sa, (const uint8_t *) ni_req + sizeof(NameInfoRequest), ni_req->sockaddr_len); | |
382 | ||
383 | ret = getnameinfo(&sa.sa, ni_req->sockaddr_len, | |
384 | ni_req->gethost ? hostbuf : NULL, ni_req->gethost ? sizeof(hostbuf) : 0, | |
385 | ni_req->getserv ? servbuf : NULL, ni_req->getserv ? sizeof(servbuf) : 0, | |
386 | ni_req->flags); | |
387 | ||
388 | return send_nameinfo_reply(out_fd, req->id, ret, | |
389 | ret == 0 && ni_req->gethost ? hostbuf : NULL, | |
390 | ret == 0 && ni_req->getserv ? servbuf : NULL, | |
391 | errno, h_errno); | |
392 | } | |
393 | ||
394 | case REQUEST_TERMINATE: | |
395 | /* Quit */ | |
396 | return -ECONNRESET; | |
397 | ||
398 | default: | |
399 | assert_not_reached(); | |
400 | } | |
401 | ||
402 | return 0; | |
403 | } | |
404 | ||
405 | static void* thread_worker(void *p) { | |
406 | sd_resolve *resolve = p; | |
407 | ||
408 | /* Assign a pretty name to this thread */ | |
409 | (void) pthread_setname_np(pthread_self(), "sd-resolve"); | |
410 | ||
411 | while (!resolve->dead) { | |
412 | union { | |
413 | Packet packet; | |
414 | uint8_t space[BUFSIZE]; | |
415 | } buf; | |
416 | ssize_t length; | |
417 | ||
418 | length = recv(resolve->fds[REQUEST_RECV_FD], &buf, sizeof buf, 0); | |
419 | if (length < 0) { | |
420 | if (ERRNO_IS_TRANSIENT(errno)) | |
421 | continue; | |
422 | ||
423 | break; | |
424 | } | |
425 | if (length == 0) | |
426 | break; | |
427 | ||
428 | if (handle_request(resolve->fds[RESPONSE_SEND_FD], &buf.packet, (size_t) length) < 0) | |
429 | break; | |
430 | } | |
431 | ||
432 | send_died(resolve->fds[RESPONSE_SEND_FD]); | |
433 | ||
434 | return NULL; | |
435 | } | |
436 | ||
437 | static int start_threads(sd_resolve *resolve, unsigned extra) { | |
438 | sigset_t ss, saved_ss; | |
439 | unsigned n; | |
440 | int r, k; | |
441 | ||
442 | assert_se(sigfillset(&ss) >= 0); | |
443 | ||
444 | /* No signals in forked off threads please. We set the mask before forking, so that the threads never exist | |
445 | * with a different mask than a fully blocked one */ | |
446 | r = pthread_sigmask(SIG_BLOCK, &ss, &saved_ss); | |
447 | if (r > 0) | |
448 | return -r; | |
449 | ||
450 | n = resolve->n_outstanding + extra; | |
451 | n = CLAMP(n, WORKERS_MIN, WORKERS_MAX); | |
452 | ||
453 | while (resolve->n_valid_workers < n) { | |
454 | r = pthread_create(&resolve->workers[resolve->n_valid_workers], NULL, thread_worker, resolve); | |
455 | if (r > 0) { | |
456 | r = -r; | |
457 | goto finish; | |
458 | } | |
459 | ||
460 | resolve->n_valid_workers++; | |
461 | } | |
462 | ||
463 | r = 0; | |
464 | ||
465 | finish: | |
466 | k = pthread_sigmask(SIG_SETMASK, &saved_ss, NULL); | |
467 | if (k > 0 && r >= 0) | |
468 | r = -k; | |
469 | ||
470 | return r; | |
471 | } | |
472 | ||
473 | static bool resolve_pid_changed(sd_resolve *r) { | |
474 | assert(r); | |
475 | ||
476 | /* We don't support people creating a resolver and keeping it | |
477 | * around after fork(). Let's complain. */ | |
478 | ||
479 | return r->original_pid != getpid_cached(); | |
480 | } | |
481 | ||
482 | int sd_resolve_new(sd_resolve **ret) { | |
483 | _cleanup_(sd_resolve_unrefp) sd_resolve *resolve = NULL; | |
484 | int i; | |
485 | ||
486 | assert_return(ret, -EINVAL); | |
487 | ||
488 | resolve = new0(sd_resolve, 1); | |
489 | if (!resolve) | |
490 | return -ENOMEM; | |
491 | ||
492 | resolve->n_ref = 1; | |
493 | resolve->original_pid = getpid_cached(); | |
494 | ||
495 | for (i = 0; i < _FD_MAX; i++) | |
496 | resolve->fds[i] = -EBADF; | |
497 | ||
498 | if (socketpair(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0, resolve->fds + REQUEST_RECV_FD) < 0) | |
499 | return -errno; | |
500 | ||
501 | if (socketpair(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0, resolve->fds + RESPONSE_RECV_FD) < 0) | |
502 | return -errno; | |
503 | ||
504 | for (i = 0; i < _FD_MAX; i++) | |
505 | resolve->fds[i] = fd_move_above_stdio(resolve->fds[i]); | |
506 | ||
507 | (void) fd_inc_sndbuf(resolve->fds[REQUEST_SEND_FD], QUERIES_MAX * BUFSIZE); | |
508 | (void) fd_increase_rxbuf(resolve->fds[REQUEST_RECV_FD], QUERIES_MAX * BUFSIZE); | |
509 | (void) fd_inc_sndbuf(resolve->fds[RESPONSE_SEND_FD], QUERIES_MAX * BUFSIZE); | |
510 | (void) fd_increase_rxbuf(resolve->fds[RESPONSE_RECV_FD], QUERIES_MAX * BUFSIZE); | |
511 | ||
512 | (void) fd_nonblock(resolve->fds[RESPONSE_RECV_FD], true); | |
513 | ||
514 | *ret = TAKE_PTR(resolve); | |
515 | return 0; | |
516 | } | |
517 | ||
518 | int sd_resolve_default(sd_resolve **ret) { | |
519 | static thread_local sd_resolve *default_resolve = NULL; | |
520 | sd_resolve *e = NULL; | |
521 | int r; | |
522 | ||
523 | if (!ret) | |
524 | return !!default_resolve; | |
525 | ||
526 | if (default_resolve) { | |
527 | *ret = sd_resolve_ref(default_resolve); | |
528 | return 0; | |
529 | } | |
530 | ||
531 | r = sd_resolve_new(&e); | |
532 | if (r < 0) | |
533 | return r; | |
534 | ||
535 | e->default_resolve_ptr = &default_resolve; | |
536 | e->tid = gettid(); | |
537 | default_resolve = e; | |
538 | ||
539 | *ret = e; | |
540 | return 1; | |
541 | } | |
542 | ||
543 | int sd_resolve_get_tid(sd_resolve *resolve, pid_t *tid) { | |
544 | assert_return(resolve, -EINVAL); | |
545 | assert_return(tid, -EINVAL); | |
546 | assert_return(!resolve_pid_changed(resolve), -ECHILD); | |
547 | ||
548 | if (resolve->tid != 0) { | |
549 | *tid = resolve->tid; | |
550 | return 0; | |
551 | } | |
552 | ||
553 | if (resolve->event) | |
554 | return sd_event_get_tid(resolve->event, tid); | |
555 | ||
556 | return -ENXIO; | |
557 | } | |
558 | ||
559 | static sd_resolve *resolve_free(sd_resolve *resolve) { | |
560 | PROTECT_ERRNO; | |
561 | sd_resolve_query *q; | |
562 | unsigned i; | |
563 | ||
564 | assert(resolve); | |
565 | ||
566 | while ((q = resolve->queries)) { | |
567 | assert(q->floating); | |
568 | resolve_query_disconnect(q); | |
569 | sd_resolve_query_unref(q); | |
570 | } | |
571 | ||
572 | if (resolve->default_resolve_ptr) | |
573 | *(resolve->default_resolve_ptr) = NULL; | |
574 | ||
575 | resolve->dead = true; | |
576 | ||
577 | sd_resolve_detach_event(resolve); | |
578 | ||
579 | if (resolve->fds[REQUEST_SEND_FD] >= 0) { | |
580 | ||
581 | RHeader req = { | |
582 | .type = REQUEST_TERMINATE, | |
583 | .length = sizeof req, | |
584 | }; | |
585 | ||
586 | /* Send one termination packet for each worker */ | |
587 | for (i = 0; i < resolve->n_valid_workers; i++) | |
588 | (void) send(resolve->fds[REQUEST_SEND_FD], &req, req.length, MSG_NOSIGNAL); | |
589 | } | |
590 | ||
591 | /* Now terminate them and wait until they are gone. | |
592 | If we get an error than most likely the thread already exited. */ | |
593 | for (i = 0; i < resolve->n_valid_workers; i++) | |
594 | (void) pthread_join(resolve->workers[i], NULL); | |
595 | ||
596 | /* Close all communication channels */ | |
597 | close_many(resolve->fds, _FD_MAX); | |
598 | ||
599 | return mfree(resolve); | |
600 | } | |
601 | ||
602 | DEFINE_TRIVIAL_REF_UNREF_FUNC(sd_resolve, sd_resolve, resolve_free); | |
603 | ||
604 | int sd_resolve_get_fd(sd_resolve *resolve) { | |
605 | assert_return(resolve, -EINVAL); | |
606 | assert_return(!resolve_pid_changed(resolve), -ECHILD); | |
607 | ||
608 | return resolve->fds[RESPONSE_RECV_FD]; | |
609 | } | |
610 | ||
611 | int sd_resolve_get_events(sd_resolve *resolve) { | |
612 | assert_return(resolve, -EINVAL); | |
613 | assert_return(!resolve_pid_changed(resolve), -ECHILD); | |
614 | ||
615 | return resolve->n_queries > resolve->n_done ? POLLIN : 0; | |
616 | } | |
617 | ||
618 | int sd_resolve_get_timeout(sd_resolve *resolve, uint64_t *usec) { | |
619 | assert_return(resolve, -EINVAL); | |
620 | assert_return(usec, -EINVAL); | |
621 | assert_return(!resolve_pid_changed(resolve), -ECHILD); | |
622 | ||
623 | *usec = UINT64_MAX; | |
624 | return 0; | |
625 | } | |
626 | ||
627 | static sd_resolve_query *lookup_query(sd_resolve *resolve, unsigned id) { | |
628 | sd_resolve_query *q; | |
629 | ||
630 | assert(resolve); | |
631 | ||
632 | q = resolve->query_array[id % QUERIES_MAX]; | |
633 | if (q) | |
634 | if (q->id == id) | |
635 | return q; | |
636 | ||
637 | return NULL; | |
638 | } | |
639 | ||
640 | static int complete_query(sd_resolve *resolve, sd_resolve_query *q) { | |
641 | int r; | |
642 | ||
643 | assert(q); | |
644 | assert(!q->done); | |
645 | assert(q->resolve == resolve); | |
646 | ||
647 | q->done = true; | |
648 | resolve->n_done++; | |
649 | ||
650 | resolve->current = sd_resolve_query_ref(q); | |
651 | ||
652 | switch (q->type) { | |
653 | ||
654 | case REQUEST_ADDRINFO: | |
655 | r = getaddrinfo_done(q); | |
656 | break; | |
657 | ||
658 | case REQUEST_NAMEINFO: | |
659 | r = getnameinfo_done(q); | |
660 | break; | |
661 | ||
662 | default: | |
663 | assert_not_reached(); | |
664 | } | |
665 | ||
666 | resolve->current = NULL; | |
667 | ||
668 | if (q->floating) { | |
669 | resolve_query_disconnect(q); | |
670 | sd_resolve_query_unref(q); | |
671 | } | |
672 | ||
673 | sd_resolve_query_unref(q); | |
674 | ||
675 | return r; | |
676 | } | |
677 | ||
678 | static int unserialize_addrinfo(const void **p, size_t *length, struct addrinfo **ret_ai) { | |
679 | AddrInfoSerialization s; | |
680 | struct addrinfo *ai; | |
681 | size_t l; | |
682 | ||
683 | assert(p); | |
684 | assert(*p); | |
685 | assert(ret_ai); | |
686 | assert(length); | |
687 | ||
688 | if (*length < sizeof(AddrInfoSerialization)) | |
689 | return -EBADMSG; | |
690 | ||
691 | memcpy(&s, *p, sizeof(s)); | |
692 | ||
693 | l = sizeof(AddrInfoSerialization) + s.ai_addrlen + s.canonname_len; | |
694 | if (*length < l) | |
695 | return -EBADMSG; | |
696 | ||
697 | ai = new(struct addrinfo, 1); | |
698 | if (!ai) | |
699 | return -ENOMEM; | |
700 | ||
701 | *ai = (struct addrinfo) { | |
702 | .ai_flags = s.ai_flags, | |
703 | .ai_family = s.ai_family, | |
704 | .ai_socktype = s.ai_socktype, | |
705 | .ai_protocol = s.ai_protocol, | |
706 | .ai_addrlen = s.ai_addrlen, | |
707 | }; | |
708 | ||
709 | if (s.ai_addrlen > 0) { | |
710 | ai->ai_addr = memdup((const uint8_t*) *p + sizeof(AddrInfoSerialization), s.ai_addrlen); | |
711 | if (!ai->ai_addr) { | |
712 | free(ai); | |
713 | return -ENOMEM; | |
714 | } | |
715 | } | |
716 | ||
717 | if (s.canonname_len > 0) { | |
718 | ai->ai_canonname = memdup((const uint8_t*) *p + sizeof(AddrInfoSerialization) + s.ai_addrlen, s.canonname_len); | |
719 | if (!ai->ai_canonname) { | |
720 | free(ai->ai_addr); | |
721 | free(ai); | |
722 | return -ENOMEM; | |
723 | } | |
724 | } | |
725 | ||
726 | *length -= l; | |
727 | *ret_ai = ai; | |
728 | *p = ((const uint8_t*) *p) + l; | |
729 | ||
730 | return 0; | |
731 | } | |
732 | ||
733 | static int handle_response(sd_resolve *resolve, const Packet *packet, size_t length) { | |
734 | const RHeader *resp; | |
735 | sd_resolve_query *q; | |
736 | int r; | |
737 | ||
738 | assert(resolve); | |
739 | assert(packet); | |
740 | ||
741 | resp = &packet->rheader; | |
742 | assert_return(length >= sizeof(RHeader), -EIO); | |
743 | assert_return(length == resp->length, -EIO); | |
744 | ||
745 | if (resp->type == RESPONSE_DIED) { | |
746 | resolve->dead = true; | |
747 | return 0; | |
748 | } | |
749 | ||
750 | assert(resolve->n_outstanding > 0); | |
751 | resolve->n_outstanding--; | |
752 | ||
753 | q = lookup_query(resolve, resp->id); | |
754 | if (!q) | |
755 | return 0; | |
756 | ||
757 | switch (resp->type) { | |
758 | ||
759 | case RESPONSE_ADDRINFO: { | |
760 | const AddrInfoResponse *ai_resp = &packet->addrinfo_response; | |
761 | const void *p; | |
762 | size_t l; | |
763 | struct addrinfo *prev = NULL; | |
764 | ||
765 | assert_return(length >= sizeof(AddrInfoResponse), -EBADMSG); | |
766 | assert_return(q->type == REQUEST_ADDRINFO, -EBADMSG); | |
767 | ||
768 | query_assign_errno(q, ai_resp->ret, ai_resp->_errno, ai_resp->_h_errno); | |
769 | ||
770 | l = length - sizeof(AddrInfoResponse); | |
771 | p = (const uint8_t*) resp + sizeof(AddrInfoResponse); | |
772 | ||
773 | while (l > 0 && p) { | |
774 | struct addrinfo *ai = NULL; | |
775 | ||
776 | r = unserialize_addrinfo(&p, &l, &ai); | |
777 | if (r < 0) { | |
778 | query_assign_errno(q, EAI_SYSTEM, r, 0); | |
779 | freeaddrinfo(q->addrinfo); | |
780 | q->addrinfo = NULL; | |
781 | break; | |
782 | } | |
783 | ||
784 | if (prev) | |
785 | prev->ai_next = ai; | |
786 | else | |
787 | q->addrinfo = ai; | |
788 | ||
789 | prev = ai; | |
790 | } | |
791 | ||
792 | return complete_query(resolve, q); | |
793 | } | |
794 | ||
795 | case RESPONSE_NAMEINFO: { | |
796 | const NameInfoResponse *ni_resp = &packet->nameinfo_response; | |
797 | ||
798 | assert_return(length >= sizeof(NameInfoResponse), -EBADMSG); | |
799 | assert_return(q->type == REQUEST_NAMEINFO, -EBADMSG); | |
800 | ||
801 | if (ni_resp->hostlen > DNS_HOSTNAME_MAX || | |
802 | ni_resp->servlen > DNS_HOSTNAME_MAX || | |
803 | sizeof(NameInfoResponse) + ni_resp->hostlen + ni_resp->servlen > length) | |
804 | query_assign_errno(q, EAI_SYSTEM, EIO, 0); | |
805 | else { | |
806 | query_assign_errno(q, ni_resp->ret, ni_resp->_errno, ni_resp->_h_errno); | |
807 | ||
808 | if (ni_resp->hostlen > 0) { | |
809 | q->host = strndup((const char*) ni_resp + sizeof(NameInfoResponse), | |
810 | ni_resp->hostlen-1); | |
811 | if (!q->host) | |
812 | query_assign_errno(q, EAI_MEMORY, ENOMEM, 0); | |
813 | } | |
814 | ||
815 | if (ni_resp->servlen > 0) { | |
816 | q->serv = strndup((const char*) ni_resp + sizeof(NameInfoResponse) + ni_resp->hostlen, | |
817 | ni_resp->servlen-1); | |
818 | if (!q->serv) | |
819 | query_assign_errno(q, EAI_MEMORY, ENOMEM, 0); | |
820 | } | |
821 | } | |
822 | ||
823 | return complete_query(resolve, q); | |
824 | } | |
825 | ||
826 | default: | |
827 | return 0; | |
828 | } | |
829 | } | |
830 | ||
831 | int sd_resolve_process(sd_resolve *resolve) { | |
832 | RESOLVE_DONT_DESTROY(resolve); | |
833 | ||
834 | union { | |
835 | Packet packet; | |
836 | uint8_t space[BUFSIZE]; | |
837 | } buf; | |
838 | ssize_t l; | |
839 | int r; | |
840 | ||
841 | assert_return(resolve, -EINVAL); | |
842 | assert_return(!resolve_pid_changed(resolve), -ECHILD); | |
843 | ||
844 | /* We don't allow recursively invoking sd_resolve_process(). */ | |
845 | assert_return(!resolve->current, -EBUSY); | |
846 | ||
847 | l = recv(resolve->fds[RESPONSE_RECV_FD], &buf, sizeof buf, 0); | |
848 | if (l < 0) { | |
849 | if (ERRNO_IS_TRANSIENT(errno)) | |
850 | return 0; | |
851 | ||
852 | return -errno; | |
853 | } | |
854 | if (l == 0) | |
855 | return -ECONNREFUSED; | |
856 | ||
857 | r = handle_response(resolve, &buf.packet, (size_t) l); | |
858 | if (r < 0) | |
859 | return r; | |
860 | ||
861 | return 1; | |
862 | } | |
863 | ||
864 | int sd_resolve_wait(sd_resolve *resolve, uint64_t timeout_usec) { | |
865 | int r; | |
866 | ||
867 | assert_return(resolve, -EINVAL); | |
868 | assert_return(!resolve_pid_changed(resolve), -ECHILD); | |
869 | ||
870 | if (resolve->n_done >= resolve->n_queries) | |
871 | return 0; | |
872 | ||
873 | do { | |
874 | r = fd_wait_for_event(resolve->fds[RESPONSE_RECV_FD], POLLIN, timeout_usec); | |
875 | } while (r == -EINTR); | |
876 | ||
877 | if (r < 0) | |
878 | return r; | |
879 | if (r == 0) | |
880 | return -ETIMEDOUT; | |
881 | ||
882 | return sd_resolve_process(resolve); | |
883 | } | |
884 | ||
885 | static int alloc_query(sd_resolve *resolve, bool floating, sd_resolve_query **_q) { | |
886 | sd_resolve_query *q; | |
887 | int r; | |
888 | ||
889 | assert(resolve); | |
890 | assert(_q); | |
891 | ||
892 | if (resolve->n_queries >= QUERIES_MAX) | |
893 | return -ENOBUFS; | |
894 | ||
895 | r = start_threads(resolve, 1); | |
896 | if (r < 0) | |
897 | return r; | |
898 | ||
899 | while (resolve->query_array[resolve->current_id % QUERIES_MAX]) | |
900 | resolve->current_id++; | |
901 | ||
902 | q = resolve->query_array[resolve->current_id % QUERIES_MAX] = new0(sd_resolve_query, 1); | |
903 | if (!q) | |
904 | return -ENOMEM; | |
905 | ||
906 | q->n_ref = 1; | |
907 | q->resolve = resolve; | |
908 | q->floating = floating; | |
909 | q->id = resolve->current_id++; | |
910 | ||
911 | if (!floating) | |
912 | sd_resolve_ref(resolve); | |
913 | ||
914 | LIST_PREPEND(queries, resolve->queries, q); | |
915 | resolve->n_queries++; | |
916 | ||
917 | *_q = q; | |
918 | return 0; | |
919 | } | |
920 | ||
921 | int resolve_getaddrinfo_with_destroy_callback( | |
922 | sd_resolve *resolve, | |
923 | sd_resolve_query **ret_query, | |
924 | const char *node, const char *service, | |
925 | const struct addrinfo *hints, | |
926 | sd_resolve_getaddrinfo_handler_t callback, | |
927 | sd_resolve_destroy_t destroy_callback, | |
928 | void *userdata) { | |
929 | ||
930 | _cleanup_(sd_resolve_query_unrefp) sd_resolve_query *q = NULL; | |
931 | size_t node_len, service_len; | |
932 | AddrInfoRequest req = {}; | |
933 | struct iovec iov[3]; | |
934 | struct msghdr mh = {}; | |
935 | int r; | |
936 | ||
937 | assert_return(resolve, -EINVAL); | |
938 | assert_return(node || service, -EINVAL); | |
939 | assert_return(callback, -EINVAL); | |
940 | assert_return(!resolve_pid_changed(resolve), -ECHILD); | |
941 | ||
942 | r = alloc_query(resolve, !ret_query, &q); | |
943 | if (r < 0) | |
944 | return r; | |
945 | ||
946 | q->type = REQUEST_ADDRINFO; | |
947 | q->getaddrinfo_handler = callback; | |
948 | q->userdata = userdata; | |
949 | ||
950 | node_len = node ? strlen(node) + 1 : 0; | |
951 | service_len = service ? strlen(service) + 1 : 0; | |
952 | ||
953 | req = (AddrInfoRequest) { | |
954 | .node_len = node_len, | |
955 | .service_len = service_len, | |
956 | ||
957 | .header.id = q->id, | |
958 | .header.type = REQUEST_ADDRINFO, | |
959 | .header.length = sizeof(AddrInfoRequest) + node_len + service_len, | |
960 | ||
961 | .hints_valid = hints, | |
962 | .ai_flags = hints ? hints->ai_flags : 0, | |
963 | .ai_family = hints ? hints->ai_family : 0, | |
964 | .ai_socktype = hints ? hints->ai_socktype : 0, | |
965 | .ai_protocol = hints ? hints->ai_protocol : 0, | |
966 | }; | |
967 | ||
968 | msan_unpoison(&req, sizeof(req)); | |
969 | ||
970 | iov[mh.msg_iovlen++] = IOVEC_MAKE(&req, sizeof(AddrInfoRequest)); | |
971 | if (node) | |
972 | iov[mh.msg_iovlen++] = IOVEC_MAKE((void*) node, req.node_len); | |
973 | if (service) | |
974 | iov[mh.msg_iovlen++] = IOVEC_MAKE((void*) service, req.service_len); | |
975 | mh.msg_iov = iov; | |
976 | ||
977 | if (sendmsg(resolve->fds[REQUEST_SEND_FD], &mh, MSG_NOSIGNAL) < 0) | |
978 | return -errno; | |
979 | ||
980 | resolve->n_outstanding++; | |
981 | q->destroy_callback = destroy_callback; | |
982 | ||
983 | if (ret_query) | |
984 | *ret_query = q; | |
985 | ||
986 | TAKE_PTR(q); | |
987 | ||
988 | return 0; | |
989 | } | |
990 | ||
991 | int sd_resolve_getaddrinfo( | |
992 | sd_resolve *resolve, | |
993 | sd_resolve_query **ret_query, | |
994 | const char *node, const char *service, | |
995 | const struct addrinfo *hints, | |
996 | sd_resolve_getaddrinfo_handler_t callback, | |
997 | void *userdata) { | |
998 | ||
999 | return resolve_getaddrinfo_with_destroy_callback(resolve, ret_query, node, service, hints, callback, NULL, userdata); | |
1000 | } | |
1001 | ||
1002 | static int getaddrinfo_done(sd_resolve_query* q) { | |
1003 | assert(q); | |
1004 | assert(q->done); | |
1005 | assert(q->getaddrinfo_handler); | |
1006 | ||
1007 | errno = q->_errno; | |
1008 | h_errno = q->_h_errno; | |
1009 | ||
1010 | return q->getaddrinfo_handler(q, q->ret, q->addrinfo, q->userdata); | |
1011 | } | |
1012 | ||
1013 | int resolve_getnameinfo_with_destroy_callback( | |
1014 | sd_resolve *resolve, | |
1015 | sd_resolve_query **ret_query, | |
1016 | const struct sockaddr *sa, socklen_t salen, | |
1017 | int flags, | |
1018 | uint64_t get, | |
1019 | sd_resolve_getnameinfo_handler_t callback, | |
1020 | sd_resolve_destroy_t destroy_callback, | |
1021 | void *userdata) { | |
1022 | ||
1023 | _cleanup_(sd_resolve_query_unrefp) sd_resolve_query *q = NULL; | |
1024 | NameInfoRequest req = {}; | |
1025 | struct iovec iov[2]; | |
1026 | struct msghdr mh; | |
1027 | int r; | |
1028 | ||
1029 | assert_return(resolve, -EINVAL); | |
1030 | assert_return(sa, -EINVAL); | |
1031 | assert_return(salen >= sizeof(struct sockaddr), -EINVAL); | |
1032 | assert_return(salen <= sizeof(union sockaddr_union), -EINVAL); | |
1033 | assert_return((get & ~SD_RESOLVE_GET_BOTH) == 0, -EINVAL); | |
1034 | assert_return(callback, -EINVAL); | |
1035 | assert_return(!resolve_pid_changed(resolve), -ECHILD); | |
1036 | ||
1037 | r = alloc_query(resolve, !ret_query, &q); | |
1038 | if (r < 0) | |
1039 | return r; | |
1040 | ||
1041 | q->type = REQUEST_NAMEINFO; | |
1042 | q->getnameinfo_handler = callback; | |
1043 | q->userdata = userdata; | |
1044 | ||
1045 | req = (NameInfoRequest) { | |
1046 | .header.id = q->id, | |
1047 | .header.type = REQUEST_NAMEINFO, | |
1048 | .header.length = sizeof(NameInfoRequest) + salen, | |
1049 | ||
1050 | .flags = flags, | |
1051 | .sockaddr_len = salen, | |
1052 | .gethost = !!(get & SD_RESOLVE_GET_HOST), | |
1053 | .getserv = !!(get & SD_RESOLVE_GET_SERVICE), | |
1054 | }; | |
1055 | ||
1056 | msan_unpoison(&req, sizeof(req)); | |
1057 | ||
1058 | iov[0] = IOVEC_MAKE(&req, sizeof(NameInfoRequest)); | |
1059 | iov[1] = IOVEC_MAKE((void*) sa, salen); | |
1060 | ||
1061 | mh = (struct msghdr) { | |
1062 | .msg_iov = iov, | |
1063 | .msg_iovlen = ELEMENTSOF(iov) | |
1064 | }; | |
1065 | ||
1066 | if (sendmsg(resolve->fds[REQUEST_SEND_FD], &mh, MSG_NOSIGNAL) < 0) | |
1067 | return -errno; | |
1068 | ||
1069 | resolve->n_outstanding++; | |
1070 | q->destroy_callback = destroy_callback; | |
1071 | ||
1072 | if (ret_query) | |
1073 | *ret_query = q; | |
1074 | ||
1075 | TAKE_PTR(q); | |
1076 | ||
1077 | return 0; | |
1078 | } | |
1079 | ||
1080 | int sd_resolve_getnameinfo( | |
1081 | sd_resolve *resolve, | |
1082 | sd_resolve_query **ret_query, | |
1083 | const struct sockaddr *sa, socklen_t salen, | |
1084 | int flags, | |
1085 | uint64_t get, | |
1086 | sd_resolve_getnameinfo_handler_t callback, | |
1087 | void *userdata) { | |
1088 | ||
1089 | return resolve_getnameinfo_with_destroy_callback(resolve, ret_query, sa, salen, flags, get, callback, NULL, userdata); | |
1090 | } | |
1091 | ||
1092 | static int getnameinfo_done(sd_resolve_query *q) { | |
1093 | ||
1094 | assert(q); | |
1095 | assert(q->done); | |
1096 | assert(q->getnameinfo_handler); | |
1097 | ||
1098 | errno = q->_errno; | |
1099 | h_errno = q->_h_errno; | |
1100 | ||
1101 | return q->getnameinfo_handler(q, q->ret, q->host, q->serv, q->userdata); | |
1102 | } | |
1103 | ||
1104 | static void resolve_freeaddrinfo(struct addrinfo *ai) { | |
1105 | while (ai) { | |
1106 | struct addrinfo *next = ai->ai_next; | |
1107 | ||
1108 | free(ai->ai_addr); | |
1109 | free(ai->ai_canonname); | |
1110 | free_and_replace(ai, next); | |
1111 | } | |
1112 | } | |
1113 | ||
1114 | static void resolve_query_disconnect(sd_resolve_query *q) { | |
1115 | sd_resolve *resolve; | |
1116 | unsigned i; | |
1117 | ||
1118 | assert(q); | |
1119 | ||
1120 | if (!q->resolve) | |
1121 | return; | |
1122 | ||
1123 | resolve = q->resolve; | |
1124 | assert(resolve->n_queries > 0); | |
1125 | ||
1126 | if (q->done) { | |
1127 | assert(resolve->n_done > 0); | |
1128 | resolve->n_done--; | |
1129 | } | |
1130 | ||
1131 | i = q->id % QUERIES_MAX; | |
1132 | assert(resolve->query_array[i] == q); | |
1133 | resolve->query_array[i] = NULL; | |
1134 | LIST_REMOVE(queries, resolve->queries, q); | |
1135 | resolve->n_queries--; | |
1136 | ||
1137 | q->resolve = NULL; | |
1138 | if (!q->floating) | |
1139 | sd_resolve_unref(resolve); | |
1140 | } | |
1141 | ||
1142 | static sd_resolve_query *resolve_query_free(sd_resolve_query *q) { | |
1143 | assert(q); | |
1144 | ||
1145 | resolve_query_disconnect(q); | |
1146 | ||
1147 | if (q->destroy_callback) | |
1148 | q->destroy_callback(q->userdata); | |
1149 | ||
1150 | resolve_freeaddrinfo(q->addrinfo); | |
1151 | free(q->host); | |
1152 | free(q->serv); | |
1153 | ||
1154 | return mfree(q); | |
1155 | } | |
1156 | ||
1157 | DEFINE_TRIVIAL_REF_UNREF_FUNC(sd_resolve_query, sd_resolve_query, resolve_query_free); | |
1158 | ||
1159 | int sd_resolve_query_is_done(sd_resolve_query *q) { | |
1160 | assert_return(q, -EINVAL); | |
1161 | assert_return(!resolve_pid_changed(q->resolve), -ECHILD); | |
1162 | ||
1163 | return q->done; | |
1164 | } | |
1165 | ||
1166 | void* sd_resolve_query_set_userdata(sd_resolve_query *q, void *userdata) { | |
1167 | void *ret; | |
1168 | ||
1169 | assert_return(q, NULL); | |
1170 | assert_return(!resolve_pid_changed(q->resolve), NULL); | |
1171 | ||
1172 | ret = q->userdata; | |
1173 | q->userdata = userdata; | |
1174 | ||
1175 | return ret; | |
1176 | } | |
1177 | ||
1178 | void* sd_resolve_query_get_userdata(sd_resolve_query *q) { | |
1179 | assert_return(q, NULL); | |
1180 | assert_return(!resolve_pid_changed(q->resolve), NULL); | |
1181 | ||
1182 | return q->userdata; | |
1183 | } | |
1184 | ||
1185 | sd_resolve *sd_resolve_query_get_resolve(sd_resolve_query *q) { | |
1186 | assert_return(q, NULL); | |
1187 | assert_return(!resolve_pid_changed(q->resolve), NULL); | |
1188 | ||
1189 | return q->resolve; | |
1190 | } | |
1191 | ||
1192 | int sd_resolve_query_get_destroy_callback(sd_resolve_query *q, sd_resolve_destroy_t *destroy_callback) { | |
1193 | assert_return(q, -EINVAL); | |
1194 | ||
1195 | if (destroy_callback) | |
1196 | *destroy_callback = q->destroy_callback; | |
1197 | ||
1198 | return !!q->destroy_callback; | |
1199 | } | |
1200 | ||
1201 | int sd_resolve_query_set_destroy_callback(sd_resolve_query *q, sd_resolve_destroy_t destroy_callback) { | |
1202 | assert_return(q, -EINVAL); | |
1203 | ||
1204 | q->destroy_callback = destroy_callback; | |
1205 | return 0; | |
1206 | } | |
1207 | ||
1208 | int sd_resolve_query_get_floating(sd_resolve_query *q) { | |
1209 | assert_return(q, -EINVAL); | |
1210 | ||
1211 | return q->floating; | |
1212 | } | |
1213 | ||
1214 | int sd_resolve_query_set_floating(sd_resolve_query *q, int b) { | |
1215 | assert_return(q, -EINVAL); | |
1216 | ||
1217 | if (q->floating == !!b) | |
1218 | return 0; | |
1219 | ||
1220 | if (!q->resolve) /* Already disconnected */ | |
1221 | return -ESTALE; | |
1222 | ||
1223 | q->floating = b; | |
1224 | ||
1225 | if (b) { | |
1226 | sd_resolve_query_ref(q); | |
1227 | sd_resolve_unref(q->resolve); | |
1228 | } else { | |
1229 | sd_resolve_ref(q->resolve); | |
1230 | sd_resolve_query_unref(q); | |
1231 | } | |
1232 | ||
1233 | return 1; | |
1234 | } | |
1235 | ||
1236 | static int io_callback(sd_event_source *s, int fd, uint32_t revents, void *userdata) { | |
1237 | sd_resolve *resolve = ASSERT_PTR(userdata); | |
1238 | int r; | |
1239 | ||
1240 | r = sd_resolve_process(resolve); | |
1241 | if (r < 0) | |
1242 | return r; | |
1243 | ||
1244 | return 1; | |
1245 | } | |
1246 | ||
1247 | int sd_resolve_attach_event(sd_resolve *resolve, sd_event *event, int64_t priority) { | |
1248 | int r; | |
1249 | ||
1250 | assert_return(resolve, -EINVAL); | |
1251 | assert_return(!resolve->event, -EBUSY); | |
1252 | ||
1253 | assert(!resolve->event_source); | |
1254 | ||
1255 | if (event) | |
1256 | resolve->event = sd_event_ref(event); | |
1257 | else { | |
1258 | r = sd_event_default(&resolve->event); | |
1259 | if (r < 0) | |
1260 | return r; | |
1261 | } | |
1262 | ||
1263 | r = sd_event_add_io(resolve->event, &resolve->event_source, resolve->fds[RESPONSE_RECV_FD], POLLIN, io_callback, resolve); | |
1264 | if (r < 0) | |
1265 | goto fail; | |
1266 | ||
1267 | r = sd_event_source_set_priority(resolve->event_source, priority); | |
1268 | if (r < 0) | |
1269 | goto fail; | |
1270 | ||
1271 | return 0; | |
1272 | ||
1273 | fail: | |
1274 | sd_resolve_detach_event(resolve); | |
1275 | return r; | |
1276 | } | |
1277 | ||
1278 | int sd_resolve_detach_event(sd_resolve *resolve) { | |
1279 | assert_return(resolve, -EINVAL); | |
1280 | ||
1281 | if (!resolve->event) | |
1282 | return 0; | |
1283 | ||
1284 | resolve->event_source = sd_event_source_disable_unref(resolve->event_source); | |
1285 | resolve->event = sd_event_unref(resolve->event); | |
1286 | return 1; | |
1287 | } | |
1288 | ||
1289 | sd_event *sd_resolve_get_event(sd_resolve *resolve) { | |
1290 | assert_return(resolve, NULL); | |
1291 | ||
1292 | return resolve->event; | |
1293 | } |