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