File: | libsofia-sip-ua/sresolv/sres.c |
Warning: | line 4053, column 61 Although the value stored to 's' is used in the enclosing expression, the value is never actually read from 's' |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
1 | /* |
2 | * This file is part of the Sofia-SIP package |
3 | * |
4 | * Copyright (C) 2006 Nokia Corporation. |
5 | * Copyright (C) 2006 Dimitri E. Prado. |
6 | * |
7 | * Contact: Pekka Pessi <pekka.pessi@nokia.com> |
8 | * |
9 | * This library is free software; you can redistribute it and/or |
10 | * modify it under the terms of the GNU Lesser General Public License |
11 | * as published by the Free Software Foundation; either version 2.1 of |
12 | * the License, or (at your option) any later version. |
13 | * |
14 | * This library is distributed in the hope that it will be useful, but |
15 | * WITHOUT ANY WARRANTY; without even the implied warranty of |
16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
17 | * Lesser General Public License for more details. |
18 | * |
19 | * You should have received a copy of the GNU Lesser General Public |
20 | * License along with this library; if not, write to the Free Software |
21 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA |
22 | * 02110-1301 USA |
23 | * |
24 | */ |
25 | |
26 | /**@CFILE sres.c |
27 | * @brief Sofia DNS Resolver implementation. |
28 | * |
29 | * @author Pekka Pessi <Pekka.Pessi@nokia.com> |
30 | * @author Teemu Jalava <Teemu.Jalava@nokia.com> |
31 | * @author Mikko Haataja |
32 | * @author Kai Vehmanen <kai.vehmanen@nokia.com> |
33 | * (work on the win32 nameserver discovery) |
34 | * @author Dimitri E. Prado |
35 | * (initial version of win32 nameserver discovery) |
36 | * |
37 | * @todo The resolver should allow handling arbitrary records, too. |
38 | */ |
39 | |
40 | #include "config.h" |
41 | |
42 | #if HAVE_STDINT_H1 |
43 | #include <stdint.h> |
44 | #elif HAVE_INTTYPES_H1 |
45 | #include <inttypes.h> |
46 | #else |
47 | #if defined(HAVE_WIN32) |
48 | typedef _int8 int8_t; |
49 | typedef unsigned _int8 uint8_t; |
50 | typedef unsigned _int16 uint16_t; |
51 | typedef unsigned _int32 uint32_t; |
52 | #endif |
53 | #endif |
54 | |
55 | #if HAVE_NETINET_IN_H1 |
56 | #include <sys/types.h> |
57 | #include <sys/socket.h> |
58 | #include <netinet/in.h> |
59 | #endif |
60 | |
61 | #if HAVE_ARPA_INET_H1 |
62 | #include <arpa/inet.h> |
63 | #endif |
64 | |
65 | #if HAVE_WINSOCK2_H |
66 | #include <winsock2.h> |
67 | #include <ws2tcpip.h> |
68 | #ifndef IPPROTO_IPV6IPPROTO_IPV6 /* socklen_t is used with @RFC2133 API */ |
69 | typedef int socklen_t; |
70 | #endif |
71 | #endif |
72 | |
73 | #if HAVE_IPHLPAPI_H |
74 | #include <iphlpapi.h> |
75 | #endif |
76 | |
77 | #if HAVE_IP_RECVERR1 || HAVE_IPV6_RECVERR1 |
78 | #include <linux1/types.h> |
79 | #include <linux1/errqueue.h> |
80 | #include <sys/uio.h> |
81 | #endif |
82 | |
83 | #include <time.h> |
84 | |
85 | #include "sofia-resolv/sres.h" |
86 | #include "sofia-resolv/sres_cache.h" |
87 | #include "sofia-resolv/sres_record.h" |
88 | #include "sofia-resolv/sres_async.h" |
89 | |
90 | #include <sofia-sip/su_alloc.h> |
91 | #include <sofia-sip/su_strlst.h> |
92 | #include <sofia-sip/su_string.h> |
93 | #include <sofia-sip/su_errno.h> |
94 | |
95 | #include "sofia-sip/htable.h" |
96 | |
97 | #include <sys/types.h> |
98 | #include <sys/stat.h> |
99 | #include <fcntl.h> |
100 | #include <unistd.h> |
101 | |
102 | #include <stdlib.h> |
103 | #include <stdarg.h> |
104 | #include <stddef.h> |
105 | #include <string.h> |
106 | #include <stdio.h> |
107 | #include <errno(*__errno_location ()).h> |
108 | |
109 | #include <limits.h> |
110 | |
111 | #include <assert.h> |
112 | |
113 | #if HAVE_WINSOCK2_H |
114 | /* Posix send() */ |
115 | su_inlinestatic inline |
116 | ssize_t sres_send(sres_socket_t s, void *b, size_t length, int flags)send((sres_socket_t s),(void *b),(size_t length),(int flags)) |
117 | { |
118 | if (length > INT_MAX2147483647) |
119 | length = INT_MAX2147483647; |
120 | return (ssize_t)send(s, b, (int)length, flags); |
121 | } |
122 | |
123 | /* Posix recvfrom() */ |
124 | su_inlinestatic inline |
125 | ssize_t sres_recvfrom(sres_socket_t s, void *buffer, size_t length, int flags,recvfrom((sres_socket_t s),(void *buffer),(size_t length),(int flags),(struct sockaddr *from),(socklen_t *fromlen)) |
126 | struct sockaddr *from, socklen_t *fromlen)recvfrom((sres_socket_t s),(void *buffer),(size_t length),(int flags),(struct sockaddr *from),(socklen_t *fromlen)) |
127 | { |
128 | int retval, ilen = 0; |
129 | |
130 | if (fromlen) |
131 | ilen = *fromlen; |
132 | |
133 | if (length > INT_MAX2147483647) |
134 | length = INT_MAX2147483647; |
135 | |
136 | retval = recvfrom(s, buffer, (int)length, flags, |
137 | (void *)from, fromlen ? &ilen : NULL((void*)0)); |
138 | |
139 | if (fromlen) |
140 | *fromlen = ilen; |
141 | |
142 | return (ssize_t)retval; |
143 | } |
144 | |
145 | su_inlinestatic inline |
146 | int sres_close(sres_socket_t s)close((sres_socket_t s)) |
147 | { |
148 | return closesocket(s); |
149 | } |
150 | |
151 | #if !defined(IPPROTO_IPV6IPPROTO_IPV6) && (_WIN32_WINNT < 0x0600) |
152 | #if HAVE_SIN61 |
153 | #include <tpipv6.h> |
154 | #else |
155 | #if !defined(__MINGW32__) |
156 | struct sockaddr_storage { |
157 | short ss_family; |
158 | char ss_pad[126]; |
159 | }; |
160 | #endif |
161 | #endif |
162 | #endif |
163 | #else |
164 | |
165 | #define sres_send(s,b,len,flags)send((s),(b),(len),(flags)) send((s),(b),(len),(flags)) |
166 | #define sres_recvfrom(s,b,len,flags,a,alen)recvfrom((s),(b),(len),(flags),(a),(alen)) \ |
167 | recvfrom((s),(b),(len),(flags),(a),(alen)) |
168 | #define sres_close(s)close((s)) close((s)) |
169 | #define SOCKET_ERROR(-1) (-1) |
170 | #define INVALID_SOCKET((sres_socket_t)-1) ((sres_socket_t)-1) |
171 | #endif |
172 | |
173 | #define SRES_TIME_MAX((time_t)9223372036854775807L) ((time_t)LONG_MAX9223372036854775807L) |
174 | |
175 | #if !HAVE_INET_PTON1 |
176 | int su_inet_ptoninet_pton(int af, char const *src, void *dst); |
177 | #else |
178 | #define su_inet_ptoninet_pton inet_pton |
179 | #endif |
180 | #if !HAVE_INET_NTOP1 |
181 | const char *su_inet_ntopinet_ntop(int af, void const *src, char *dst, size_t size); |
182 | #else |
183 | #define su_inet_ntopinet_ntop inet_ntop |
184 | #endif |
185 | |
186 | #if defined(va_copy) |
187 | #elif defined(__va_copy) |
188 | #define va_copy(dst, src)__builtin_va_copy(dst, src) __va_copy((dst), (src))__builtin_va_copy((dst),(src)) |
189 | #else |
190 | #define va_copy(dst, src)__builtin_va_copy(dst, src) (memcpy(&(dst), &(src), sizeof (va_list))) |
191 | #endif |
192 | |
193 | /* |
194 | * 3571 is a prime => |
195 | * we hash successive id values to different parts of hash tables |
196 | */ |
197 | #define Q_PRIME3571 3571 |
198 | #define SRES_QUERY_HASH(q)((q)->q_hash) ((q)->q_hash) |
199 | |
200 | /** |
201 | * How often to recheck nameserver information (seconds). |
202 | */ |
203 | #ifndef HAVE_WIN32 |
204 | #define SRES_UPDATE_INTERVAL_SECS5 5 |
205 | #else |
206 | #define SRES_UPDATE_INTERVAL_SECS5 180 |
207 | #endif |
208 | void sres_cache_clean(sres_cache_t *cache, time_t now); |
209 | |
210 | typedef struct sres_message sres_message_t; |
211 | typedef struct sres_config sres_config_t; |
212 | typedef struct sres_server sres_server_t; |
213 | typedef struct sres_nameserver sres_nameserver_t; |
214 | |
215 | /** Default path to resolv.conf */ |
216 | static char const sres_conf_file_path[] = "/etc/resolv.conf"; |
217 | |
218 | /** EDNS0 support. @internal */ |
219 | enum edns { |
220 | edns_not_tried = -1, |
221 | edns_not_supported = 0, |
222 | edns0_configured = 1, |
223 | edns0_supported = 2, |
224 | }; |
225 | |
226 | struct sres_server { |
227 | sres_socket_t dns_socket; |
228 | |
229 | char dns_name[48]; /**< Server name */ |
230 | struct sockaddr_storage dns_addr[1]; /**< Server node address */ |
231 | ssize_t dns_addrlen; /**< Size of address */ |
232 | |
233 | enum edns dns_edns; /**< Server supports edns. */ |
234 | |
235 | /** ICMP/temporary error received, zero when successful. */ |
236 | time_t dns_icmp; |
237 | /** Persistent error, zero when successful or timeout. |
238 | * |
239 | * Never selected if dns_error is SRES_TIME_MAX. |
240 | */ |
241 | time_t dns_error; |
242 | }; |
243 | |
244 | HTABLE_DECLARE_WITH(sres_qtable, qt, sres_query_t, unsigned, size_t)typedef struct sres_qtable_s { unsigned qt_size; unsigned qt_used ; sres_query_t**qt_table; } sres_qtable_t; |
245 | |
246 | struct sres_resolver_s { |
247 | su_home_t res_home[1]; |
248 | |
249 | void *res_userdata; |
250 | sres_cache_t *res_cache; |
251 | |
252 | time_t res_now; |
253 | sres_qtable_t res_queries[1]; /**< Table of active queries */ |
254 | |
255 | char const *res_cnffile; /**< Configuration file name */ |
256 | char const **res_options; /**< Option strings */ |
257 | |
258 | sres_config_t const *res_config; |
259 | time_t res_checked; |
260 | |
261 | unsigned long res_updated; |
262 | sres_update_f *res_updcb; |
263 | sres_async_t *res_async; |
264 | sres_schedule_f *res_schedulecb; |
265 | short res_update_all; |
266 | |
267 | uint16_t res_id; |
268 | short res_i_server; /**< Current server to try |
269 | (when doing round-robin) */ |
270 | short res_n_servers; /**< Number of servers */ |
271 | sres_server_t **res_servers; |
272 | }; |
273 | |
274 | /* Parsed configuration. @internal */ |
275 | struct sres_config { |
276 | su_home_t c_home[1]; |
277 | |
278 | time_t c_modified; |
279 | char const *c_filename; |
280 | |
281 | /* domain and search */ |
282 | char const *c_search[SRES_MAX_SEARCH(SRES_MAX_SEARCH) + 1]; |
283 | |
284 | /* nameserver */ |
285 | struct sres_nameserver { |
286 | struct sockaddr_storage ns_addr[1]; |
287 | ssize_t ns_addrlen; |
288 | } *c_nameservers[SRES_MAX_NAMESERVERS(SRES_MAX_NAMESERVERS) + 1]; |
289 | |
290 | /* sortlist */ |
291 | struct sres_sortlist { |
292 | struct sockaddr_storage addr[1]; |
293 | ssize_t addrlen; |
294 | char const *name; |
295 | } *c_sortlist[SRES_MAX_SORTLIST(SRES_MAX_SORTLIST) + 1]; |
296 | |
297 | uint16_t c_port; /**< Server port to use */ |
298 | |
299 | /* options */ |
300 | struct sres_options { |
301 | uint16_t timeout; |
302 | uint16_t attempts; |
303 | uint16_t ndots; |
304 | enum edns edns; |
305 | unsigned debug:1; |
306 | unsigned rotate:1; |
307 | unsigned check_names:1; |
308 | unsigned inet6:1; |
309 | unsigned ip6int:1; |
310 | unsigned ip6bytestring:1; |
311 | } c_opt; |
312 | }; |
313 | |
314 | struct sres_query_s { |
315 | unsigned q_hash; |
316 | sres_resolver_t*q_res; |
317 | sres_answer_f *q_callback; |
318 | sres_context_t *q_context; |
319 | char *q_name; |
320 | time_t q_timestamp; |
321 | uint16_t q_type; |
322 | uint16_t q_class; |
323 | uint16_t q_id; /**< If nonzero, not answered */ |
324 | uint16_t q_retry_count; |
325 | uint8_t q_n_servers; |
326 | uint8_t q_i_server; |
327 | int8_t q_edns; |
328 | uint8_t q_n_subs; |
329 | sres_query_t *q_subqueries[1 + SRES_MAX_SEARCH(SRES_MAX_SEARCH)]; |
330 | sres_record_t **q_subanswers[1 + SRES_MAX_SEARCH(SRES_MAX_SEARCH)]; |
331 | }; |
332 | |
333 | |
334 | struct sres_message { |
335 | uint16_t m_offset; |
336 | uint16_t m_size; |
337 | char const *m_error; |
338 | union { |
339 | struct { |
340 | /* Header defined in RFC 1035 section 4.1.1 (page 26) */ |
341 | uint16_t mh_id; /* Query ID */ |
342 | uint16_t mh_flags; /* Flags */ |
343 | uint16_t mh_qdcount; /* Question record count */ |
344 | uint16_t mh_ancount; /* Answer record count */ |
345 | uint16_t mh_nscount; /* Authority records count */ |
346 | uint16_t mh_arcount; /* Additional records count */ |
347 | } mp_header; |
348 | uint8_t mp_data[1500 - 40]; /**< IPv6 datagram */ |
349 | } m_packet; |
350 | #define m_idm_packet.mp_header.mh_id m_packet.mp_header.mh_id |
351 | #define m_flagsm_packet.mp_header.mh_flags m_packet.mp_header.mh_flags |
352 | #define m_qdcountm_packet.mp_header.mh_qdcount m_packet.mp_header.mh_qdcount |
353 | #define m_ancountm_packet.mp_header.mh_ancount m_packet.mp_header.mh_ancount |
354 | #define m_nscountm_packet.mp_header.mh_nscount m_packet.mp_header.mh_nscount |
355 | #define m_arcountm_packet.mp_header.mh_arcount m_packet.mp_header.mh_arcount |
356 | #define m_datam_packet.mp_data m_packet.mp_data |
357 | }; |
358 | |
359 | #define sr_refcountsr_record->r_refcount sr_record->r_refcount |
360 | #define sr_namesr_record->r_name sr_record->r_name |
361 | #define sr_statussr_record->r_status sr_record->r_status |
362 | #define sr_sizesr_record->r_size sr_record->r_size |
363 | #define sr_typesr_record->r_type sr_record->r_type |
364 | #define sr_classsr_record->r_class sr_record->r_class |
365 | #define sr_ttlsr_record->r_ttl sr_record->r_ttl |
366 | #define sr_rdlensr_record->r_rdlen sr_record->r_rdlen |
367 | #define sr_parsedsr_record->r_parsed sr_record->r_parsed |
368 | #define sr_rdatasr_generic->g_data sr_generic->g_data |
369 | |
370 | enum { |
371 | SRES_HDR_QR = (1 << 15), |
372 | SRES_HDR_QUERY = (0 << 11), |
373 | SRES_HDR_IQUERY = (1 << 11), |
374 | SRES_HDR_STATUS = (2 << 11), |
375 | SRES_HDR_OPCODE = (15 << 11), /* mask */ |
376 | SRES_HDR_AA = (1 << 10), |
377 | SRES_HDR_TC = (1 << 9), |
378 | SRES_HDR_RD = (1 << 8), |
379 | SRES_HDR_RA = (1 << 7), |
380 | SRES_HDR_RCODE = (15 << 0) /* mask of return code */ |
381 | }; |
382 | |
383 | HTABLE_PROTOS_WITH(sres_qtable, qt, sres_query_t, unsigned, size_t)static inline int sres_qtable_resize(su_home_t *, sres_qtable_t qt[1], unsigned); static inline int sres_qtable_is_full(sres_qtable_t const *); static inline sres_query_t **sres_qtable_hash(sres_qtable_t const *, size_t hv); static inline sres_query_t **sres_qtable_next (sres_qtable_t const *, sres_query_t * const *ee); static inline void sres_qtable_append(sres_qtable_t *qt, sres_query_t const *e); static inline void sres_qtable_insert(sres_qtable_t *qt , sres_query_t const *e); static inline int sres_qtable_remove (sres_qtable_t *, sres_query_t const *e); |
384 | |
385 | #define CHOME(cache)((su_home_t *)(cache)) ((su_home_t *)(cache)) |
386 | |
387 | /** Get address from sockaddr storage. */ |
388 | #if HAVE_SIN61 |
389 | #define SS_ADDR(ss)((ss)->ss_family == 2 ? (void *)&((struct sockaddr_in * )ss)->sin_addr : ((ss)->ss_family == 10 ? (void *)& ((struct sockaddr_in6 *)ss)->sin6_addr : (void *)&((struct sockaddr *)ss)->sa_data)) \ |
390 | ((ss)->ss_family == AF_INET2 ? \ |
391 | (void *)&((struct sockaddr_in *)ss)->sin_addr : \ |
392 | ((ss)->ss_family == AF_INET610 ? \ |
393 | (void *)&((struct sockaddr_in6 *)ss)->sin6_addr : \ |
394 | (void *)&((struct sockaddr *)ss)->sa_data)) |
395 | #else |
396 | #define SS_ADDR(ss)((ss)->ss_family == 2 ? (void *)&((struct sockaddr_in * )ss)->sin_addr : ((ss)->ss_family == 10 ? (void *)& ((struct sockaddr_in6 *)ss)->sin6_addr : (void *)&((struct sockaddr *)ss)->sa_data)) \ |
397 | ((ss)->ss_family == AF_INET2 ? \ |
398 | (void *)&((struct sockaddr_in *)ss)->sin_addr : \ |
399 | (void *)&((struct sockaddr *)ss)->sa_data) |
400 | #endif |
401 | |
402 | static int sres_config_changed_servers(sres_config_t const *new_c, |
403 | sres_config_t const *old_c); |
404 | static sres_server_t **sres_servers_new(sres_resolver_t *res, |
405 | sres_config_t const *c); |
406 | static sres_answer_f sres_resolving_cname; |
407 | |
408 | /** Generate new 16-bit identifier for DNS query. */ |
409 | static void |
410 | sres_gen_id(sres_resolver_t *res, sres_query_t *query) |
411 | { |
412 | if (res->res_id == 0) { |
413 | res->res_id = 1; |
414 | } |
415 | query->q_id = res->res_id++; |
416 | query->q_hash = query->q_id * Q_PRIME3571; |
417 | } |
418 | |
419 | /** Return true if we have a search list or a local domain name. */ |
420 | static int |
421 | sres_has_search_domain(sres_resolver_t *res) |
422 | { |
423 | return res->res_config->c_search[0] != NULL((void*)0); |
424 | } |
425 | |
426 | static void sres_resolver_destructor(void *); |
427 | |
428 | sres_resolver_t * |
429 | sres_resolver_new_with_cache_va(char const *conf_file_path, |
430 | sres_cache_t *cache, |
431 | char const *options, |
432 | va_list va); |
433 | static |
434 | sres_resolver_t * |
435 | sres_resolver_new_internal(sres_cache_t *cache, |
436 | sres_config_t const *config, |
437 | char const *conf_file_path, |
438 | char const **options); |
439 | |
440 | static void sres_servers_close(sres_resolver_t *res, |
441 | sres_server_t **servers); |
442 | |
443 | static int sres_servers_count(sres_server_t * const *servers); |
444 | |
445 | static sres_socket_t sres_server_socket(sres_resolver_t *res, |
446 | sres_server_t *dns); |
447 | |
448 | static sres_query_t * sres_query_alloc(sres_resolver_t *res, |
449 | sres_answer_f *callback, |
450 | sres_context_t *context, |
451 | uint16_t type, |
452 | char const * domain); |
453 | |
454 | static void sres_free_query(sres_resolver_t *res, sres_query_t *q); |
455 | |
456 | static |
457 | int sres_sockaddr2string(sres_resolver_t *, |
458 | char name[], size_t namelen, |
459 | struct sockaddr const *); |
460 | |
461 | static |
462 | sres_config_t *sres_parse_resolv_conf(sres_resolver_t *res, |
463 | char const **options); |
464 | |
465 | static |
466 | sres_server_t *sres_next_server(sres_resolver_t *res, |
467 | uint8_t *in_out_i, |
468 | int always); |
469 | |
470 | static |
471 | int sres_send_dns_query(sres_resolver_t *res, sres_query_t *q); |
472 | |
473 | static |
474 | void sres_answer_subquery(sres_context_t *context, |
475 | sres_query_t *query, |
476 | sres_record_t **answers); |
477 | |
478 | static |
479 | sres_record_t ** |
480 | sres_combine_results(sres_resolver_t *res, |
481 | sres_record_t **search_results[SRES_MAX_SEARCH(SRES_MAX_SEARCH) + 1]); |
482 | |
483 | static |
484 | void sres_query_report_error(sres_query_t *q, |
485 | sres_record_t **answers); |
486 | |
487 | static void |
488 | sres_resend_dns_query(sres_resolver_t *res, sres_query_t *q, int timeout); |
489 | |
490 | static |
491 | sres_server_t *sres_server_by_socket(sres_resolver_t const *ts, |
492 | sres_socket_t socket); |
493 | |
494 | static |
495 | int sres_resolver_report_error(sres_resolver_t *res, |
496 | sres_socket_t socket, |
497 | int errcode, |
498 | struct sockaddr_storage *remote, |
499 | socklen_t remotelen, |
500 | char const *info); |
501 | |
502 | static |
503 | void sres_log_response(sres_resolver_t const *res, |
504 | sres_message_t const *m, |
505 | struct sockaddr_storage const *from, |
506 | sres_query_t const *query, |
507 | sres_record_t * const *reply); |
508 | |
509 | static int sres_decode_msg(sres_resolver_t *res, |
510 | sres_message_t *m, |
511 | sres_query_t **, |
512 | sres_record_t ***aanswers); |
513 | |
514 | static char const *sres_toplevel(char buf[], size_t bsize, char const *domain); |
515 | |
516 | static sres_record_t *sres_create_record(sres_resolver_t *, |
517 | sres_message_t *m, |
518 | int nth); |
519 | |
520 | static sres_record_t *sres_init_rr_soa(sres_cache_t *cache, |
521 | sres_soa_record_t *, |
522 | sres_message_t *m); |
523 | static sres_record_t *sres_init_rr_a(sres_cache_t *cache, |
524 | sres_a_record_t *, |
525 | sres_message_t *m); |
526 | static sres_record_t *sres_init_rr_a6(sres_cache_t *cache, |
527 | sres_a6_record_t *, |
528 | sres_message_t *m); |
529 | static sres_record_t *sres_init_rr_aaaa(sres_cache_t *cache, |
530 | sres_aaaa_record_t *, |
531 | sres_message_t *m); |
532 | static sres_record_t *sres_init_rr_cname(sres_cache_t *cache, |
533 | sres_cname_record_t *, |
534 | sres_message_t *m); |
535 | static sres_record_t *sres_init_rr_ptr(sres_cache_t *cache, |
536 | sres_ptr_record_t *, |
537 | sres_message_t *m); |
538 | static sres_record_t *sres_init_rr_srv(sres_cache_t *cache, |
539 | sres_srv_record_t *, |
540 | sres_message_t *m); |
541 | static sres_record_t *sres_init_rr_naptr(sres_cache_t *cache, |
542 | sres_naptr_record_t *, |
543 | sres_message_t *m); |
544 | static sres_record_t *sres_init_rr_unknown(sres_cache_t *cache, |
545 | sres_common_t *r, |
546 | sres_message_t *m); |
547 | |
548 | static sres_record_t *sres_create_error_rr(sres_cache_t *cache, |
549 | sres_query_t const *q, |
550 | uint16_t errcode); |
551 | |
552 | static void m_put_uint16(sres_message_t *m, uint16_t h); |
553 | static void m_put_uint32(sres_message_t *m, uint32_t w); |
554 | |
555 | static uint16_t m_put_domain(sres_message_t *m, |
556 | char const *domain, |
557 | uint16_t top, |
558 | char const *topdomain); |
559 | |
560 | static uint32_t m_get_uint32(sres_message_t *m); |
561 | static uint16_t m_get_uint16(sres_message_t *m); |
562 | static uint8_t m_get_uint8(sres_message_t *m); |
563 | |
564 | static unsigned m_get_string(char *d, unsigned n, sres_message_t *m, uint16_t offset); |
565 | static unsigned m_get_domain(char *d, unsigned n, sres_message_t *m, uint16_t offset); |
566 | |
567 | /* ---------------------------------------------------------------------- */ |
568 | |
569 | #define SU_LOGsresolv_log sresolv_log |
570 | |
571 | #include <sofia-sip/su_debug.h> |
572 | |
573 | #ifdef HAVE_WIN32 |
574 | #include <winreg.h> |
575 | #endif |
576 | |
577 | /**@ingroup sresolv_env |
578 | * |
579 | * Environment variable determining the debug log level for @b sresolv |
580 | * module. |
581 | * |
582 | * The SRESOLV_DEBUG environment variable is used to determine the debug |
583 | * logging level for @b sresolv module. The default level is 3. |
584 | * |
585 | * @sa <sofia-sip/su_debug.h>, sresolv_log, SOFIA_DEBUG |
586 | */ |
587 | #ifdef DOXYGEN |
588 | extern char const SRESOLV_DEBUG[]; /* dummy declaration for Doxygen */ |
589 | #endif |
590 | |
591 | #ifndef SU_DEBUG0 |
592 | #define SU_DEBUG0 3 |
593 | #endif |
594 | |
595 | /**Debug log for @b sresolv module. |
596 | * |
597 | * The sresolv_log is the log object used by @b sresolv module. The level of |
598 | * #sresolv_log is set using #SRESOLV_DEBUG environment variable. |
599 | */ |
600 | su_log_t sresolv_log[] = { SU_LOG_INIT("sresolv", "SRESOLV_DEBUG", SU_DEBUG){ sizeof(su_log_t), "sresolv", "SRESOLV_DEBUG", 0, SU_LOG_MAX , 0, ((void*)0), ((void*)0), } }; |
601 | |
602 | /** Internal errors */ |
603 | enum { |
604 | SRES_EDNS0_ERR = 255 /**< Server did not support EDNS. */ |
605 | }; |
606 | |
607 | /* ---------------------------------------------------------------------- */ |
608 | |
609 | /**Create a resolver. |
610 | * |
611 | * Allocate and initialize a new sres resolver object. The resolver object |
612 | * contains the parsed resolv.conf file, a cache object containing past |
613 | * answers from DNS, and a list of active queries. The default resolv.conf |
614 | * file can be overriden by giving the name of the configuration file as @a |
615 | * conf_file_path. |
616 | * |
617 | * @param conf_file_path name of the resolv.conf configuration file |
618 | * |
619 | * @return A pointer to a newly created sres resolver object, or NULL upon |
620 | * an error. |
621 | */ |
622 | sres_resolver_t * |
623 | sres_resolver_new(char const *conf_file_path) |
624 | { |
625 | return sres_resolver_new_internal(NULL((void*)0), NULL((void*)0), conf_file_path, NULL((void*)0)); |
626 | } |
627 | |
628 | /** Copy a resolver. |
629 | * |
630 | * Make a copy of resolver sharing the configuration and cache with old |
631 | * resolver. |
632 | */ |
633 | sres_resolver_t *sres_resolver_copy(sres_resolver_t *res) |
634 | { |
635 | char const *cnffile; |
636 | sres_config_t *config; |
637 | sres_cache_t *cache; |
638 | char const **options; |
639 | |
640 | if (!res) |
641 | return NULL((void*)0); |
642 | |
643 | cnffile = res->res_cnffile; |
644 | config = su_home_ref(res->res_config->c_home); |
645 | cache = res->res_cache; |
646 | options = res->res_options; |
647 | |
648 | return sres_resolver_new_internal(cache, config, cnffile, options); |
649 | } |
650 | |
651 | /**New resolver object. |
652 | * |
653 | * Allocate and initialize a new sres resolver object. The resolver object |
654 | * contains the parsed resolv.conf file, a cache object containing past |
655 | * answers from DNS, and a list of active queries. The default resolv.conf |
656 | * file can be overriden by giving the name of the configuration file as @a |
657 | * conf_file_path. |
658 | * |
659 | * It is also possible to override the values in the resolv.conf and |
660 | * RES_OPTIONS by giving the directives in the NULL-terminated list. |
661 | * |
662 | * @param conf_file_path name of the resolv.conf configuration file |
663 | * @param cache optional pointer to a resolver cache (may be NULL) |
664 | * @param option, ... list of resolv.conf options directives |
665 | * (overriding options in conf_file) |
666 | * |
667 | * @par Environment Variables |
668 | * - #LOCALDOMAIN overrides @c domain or @c search directives |
669 | * - #RES_OPTIONS overrides values of @a options in resolv.conf |
670 | * - #SRES_OPTIONS overrides values of @a options in resolv.conf, #RES_OPTIONS, |
671 | * and @a options, ... list given as argument for this function |
672 | * |
673 | * @return A pointer to a newly created sres resolver object, or NULL upon |
674 | * an error. |
675 | */ |
676 | sres_resolver_t * |
677 | sres_resolver_new_with_cache(char const *conf_file_path, |
678 | sres_cache_t *cache, |
679 | char const *option, ...) |
680 | { |
681 | sres_resolver_t *retval; |
682 | va_list va; |
683 | va_start(va, option)__builtin_va_start(va, option); |
684 | retval = sres_resolver_new_with_cache_va(conf_file_path, cache, option, va); |
685 | va_end(va)__builtin_va_end(va); |
686 | return retval; |
687 | } |
688 | |
689 | /**Create a resolver. |
690 | * |
691 | * Allocate and initialize a new sres resolver object. |
692 | * |
693 | * This is a stdarg version of sres_resolver_new_with_cache(). |
694 | */ |
695 | sres_resolver_t * |
696 | sres_resolver_new_with_cache_va(char const *conf_file_path, |
697 | sres_cache_t *cache, |
698 | char const *option, |
699 | va_list va) |
700 | { |
701 | va_list va0; |
702 | size_t i; |
703 | char const *o, *oarray[16], **olist = oarray; |
704 | sres_resolver_t *res; |
705 | |
706 | va_copy(va0, va)__builtin_va_copy(va0, va); |
707 | |
708 | for (i = 0, o = option; o; o = va_arg(va0, char const *)__builtin_va_arg(va0, char const *)) { |
709 | if (i < 16) |
710 | olist[i] = o; |
711 | i++; |
712 | } |
713 | |
714 | if (i >= 16) { |
715 | olist = malloc((i + 1) * sizeof *olist); |
716 | if (!olist) |
717 | return NULL((void*)0); |
718 | for (i = 0, o = option; o; o = va_arg(va, char const *)__builtin_va_arg(va, char const *)) { |
719 | olist[i++] = o; |
720 | i++; |
721 | } |
722 | } |
723 | olist[i] = NULL((void*)0); |
724 | res = sres_resolver_new_internal(cache, NULL((void*)0), conf_file_path, olist); |
725 | if (olist != oarray) |
726 | free(olist); |
727 | |
728 | va_end(va0)__builtin_va_end(va0); |
729 | |
730 | return res; |
731 | } |
732 | |
733 | sres_resolver_t * |
734 | sres_resolver_new_internal(sres_cache_t *cache, |
735 | sres_config_t const *config, |
736 | char const *conf_file_path, |
737 | char const **options) |
738 | { |
739 | sres_resolver_t *res; |
740 | size_t i, n, len; |
741 | char **array, *o, *end; |
742 | |
743 | for (n = 0, len = 0; options && options[n]; n++) |
744 | len += strlen(options[n]) + 1; |
745 | |
746 | res = su_home_new(sizeof(*res) + (n + 1) * (sizeof *options) + len); |
747 | |
748 | if (res == NULL((void*)0)) |
749 | return NULL((void*)0); |
750 | |
751 | array = (void *)(res + 1); |
752 | o = (void *)(array + n + 1); |
753 | end = o + len; |
754 | |
755 | for (i = 0; options && options[i]; i++) |
756 | o = memccpy(array[i] = o, options[i], '\0', len - (end - o)); |
757 | assert(o == end)((void) sizeof ((o == end) ? 1 : 0), __extension__ ({ if (o == end) ; else __assert_fail ("o == end", "sres.c", 757, __extension__ __PRETTY_FUNCTION__); })); |
758 | |
759 | su_home_destructor(res->res_home, sres_resolver_destructor); |
760 | |
761 | while (res->res_id == 0) { |
762 | #if HAVE_DEV_URANDOM1 |
763 | int fd; |
764 | if ((fd = open("/dev/urandom", O_RDONLY00, 0)) != -1) { |
765 | size_t len = read(fd, &res->res_id, (sizeof res->res_id)); (void)len; |
766 | close(fd); |
767 | } |
768 | else |
769 | #endif |
770 | res->res_id = time(NULL((void*)0)); |
771 | } |
772 | |
773 | time(&res->res_now); |
774 | |
775 | if (cache) |
776 | res->res_cache = sres_cache_ref(cache); |
777 | else |
778 | res->res_cache = sres_cache_new(0); |
779 | |
780 | res->res_config = config; |
781 | |
782 | if (conf_file_path && conf_file_path != sres_conf_file_path) |
783 | res->res_cnffile = su_strdup(res->res_home, conf_file_path); |
784 | else |
785 | res->res_cnffile = conf_file_path = sres_conf_file_path; |
786 | |
787 | if (!res->res_cache || !res->res_cnffile) { |
788 | perror("sres: malloc"); |
789 | } |
790 | else if (sres_qtable_resize(res->res_home, res->res_queries, 0) < 0) { |
791 | perror("sres: res_qtable_resize"); |
792 | } |
793 | else if (sres_resolver_update(res, config == NULL((void*)0)) < 0) { |
794 | perror("sres: sres_resolver_update"); |
795 | } |
796 | else { |
797 | return res; |
798 | } |
799 | |
800 | sres_resolver_unref(res); |
801 | |
802 | return NULL((void*)0); |
803 | } |
804 | |
805 | /** Increase reference count on a resolver object. */ |
806 | sres_resolver_t * |
807 | sres_resolver_ref(sres_resolver_t *res) |
808 | { |
809 | return su_home_ref(res->res_home); |
810 | } |
811 | |
812 | /** Decrease the reference count on a resolver object. */ |
813 | void |
814 | sres_resolver_unref(sres_resolver_t *res) |
815 | { |
816 | su_home_unref(res->res_home); |
817 | } |
818 | |
819 | /** Set userdata pointer. |
820 | * |
821 | * @return New userdata pointer. |
822 | * |
823 | * @ERRORS |
824 | * @ERROR EFAULT @a res points outside the address space |
825 | */ |
826 | void * |
827 | sres_resolver_set_userdata(sres_resolver_t *res, |
828 | void *userdata) |
829 | { |
830 | void *old; |
831 | |
832 | if (!res) |
833 | return su_seterrno(EFAULT14), (void *)NULL((void*)0); |
834 | |
835 | old = res->res_userdata, res->res_userdata = userdata; |
836 | |
837 | return old; |
838 | } |
839 | |
840 | /**Get userdata pointer. |
841 | * |
842 | * @return Userdata pointer. |
843 | * |
844 | * @ERRORS |
845 | * @ERROR EFAULT @a res points outside the address space |
846 | */ |
847 | void * |
848 | sres_resolver_get_userdata(sres_resolver_t const *res) |
849 | { |
850 | if (res == NULL((void*)0)) |
851 | return su_seterrno(EFAULT14), (void *)NULL((void*)0); |
852 | else |
853 | return res->res_userdata; |
854 | } |
855 | |
856 | /** Set async object. |
857 | * |
858 | * @return Set async object. |
859 | * |
860 | * @ERRORS |
861 | * @ERROR EFAULT @a res points outside the address space |
862 | * @ERROR EALREADY different async callback already set |
863 | */ |
864 | sres_async_t * |
865 | sres_resolver_set_async(sres_resolver_t *res, |
866 | sres_update_f *callback, |
867 | sres_async_t *async, |
868 | int update_all) |
869 | { |
870 | if (!res) |
871 | return su_seterrno(EFAULT14), (void *)NULL((void*)0); |
872 | |
873 | if (res->res_updcb && res->res_updcb != callback) |
874 | return su_seterrno(EALREADY114), (void *)NULL((void*)0); |
875 | |
876 | res->res_async = async; |
877 | res->res_updcb = callback; |
878 | res->res_update_all = callback && update_all != 0; |
879 | |
880 | return async; |
881 | } |
882 | |
883 | /** Get async object */ |
884 | sres_async_t * |
885 | sres_resolver_get_async(sres_resolver_t const *res, |
886 | sres_update_f *callback) |
887 | { |
888 | if (res == NULL((void*)0)) |
889 | return su_seterrno(EFAULT14), (void *)NULL((void*)0); |
890 | else if (callback == NULL((void*)0)) |
891 | return res->res_async ? (sres_async_t *)-1 : 0; |
892 | else if (res->res_updcb != callback) |
893 | return NULL((void*)0); |
894 | else |
895 | return res->res_async; |
896 | } |
897 | |
898 | /** Register resolver timer callback. */ |
899 | int sres_resolver_set_timer_cb(sres_resolver_t *res, |
900 | sres_schedule_f *callback, |
901 | sres_async_t *async) |
902 | { |
903 | if (res == NULL((void*)0)) |
904 | return su_seterrno(EFAULT14); |
905 | if (res->res_async != async) |
906 | return su_seterrno(EALREADY114); |
907 | |
908 | res->res_schedulecb = callback; |
909 | return 0; |
910 | } |
911 | |
912 | /**Send a DNS query. |
913 | * |
914 | * Sends a DNS query with specified @a type and @a domain to the DNS server. |
915 | * When an answer is received, the @a callback function is called with |
916 | * @a context and returned records as arguments. |
917 | * |
918 | * The sres resolver takes care of retransmitting the query if a root object |
919 | * is associate with the resolver or if sres_resolver_timer() is called in |
920 | * regular intervals. It generates an error record with nonzero status if no |
921 | * response is received. |
922 | * |
923 | * @param res pointer to resolver |
924 | * @param callback function called when query is answered or times out |
925 | * @param context pointer given as an extra argument to @a callback function |
926 | * @param type record type to query (see #sres_qtypes) |
927 | * @param domain name to query |
928 | * |
929 | * Query types also indicate the record type of the result. |
930 | * Any record can be queried with #sres_qtype_any. |
931 | * Well-known query types understood and decoded by @b sres include |
932 | * #sres_type_a, |
933 | * #sres_type_aaaa, |
934 | * #sres_type_cname, |
935 | * #sres_type_ptr |
936 | * #sres_type_soa, |
937 | * #sres_type_aaaa, |
938 | * #sres_type_srv, and |
939 | * #sres_type_naptr. |
940 | * |
941 | * Deprecated query type #sres_type_a6 is also decoded. |
942 | * |
943 | * @note The domain name is @b not concatenated with the domains from seach |
944 | * path or with the local domain. Use sres_search() in order to try domains |
945 | * in search path. |
946 | * |
947 | * @sa sres_search(), sres_blocking_query(), sres_cached_answers(), |
948 | * sres_query_sockaddr() |
949 | * |
950 | * @ERRORS |
951 | * @ERROR EFAULT @a res or @a domain point outside the address space |
952 | * @ERROR ENAMETOOLONG @a domain is longer than SRES_MAXDNAME |
953 | * @ERROR ENETDOWN no DNS servers configured |
954 | * @ERROR ENOMEM memory exhausted |
955 | */ |
956 | sres_query_t * |
957 | sres_query(sres_resolver_t *res, |
958 | sres_answer_f *callback, |
959 | sres_context_t *context, |
960 | uint16_t type, |
961 | char const *domain) |
962 | { |
963 | sres_query_t *query = NULL((void*)0); |
964 | size_t dlen; |
965 | |
966 | char b[8]; |
967 | SU_DEBUG_9(("sres_query(%p, %p, %s, \"%s\") called\n",(((sresolv_log != ((void*)0) && sresolv_log->log_init ) == 0 ? 9 : ((sresolv_log != ((void*)0) && sresolv_log ->log_init > 1) ? sresolv_log->log_level : su_log_default ->log_level)) >= 9 ? (_su_llog(sresolv_log, 9, "sres.c" , (const char *)__func__, 968, "sres_query(%p, %p, %s, \"%s\") called\n" , (void *)res, (void *)context, sres_record_type(type, b), domain )) : (void)0) |
968 | (void *)res, (void *)context, sres_record_type(type, b), domain))(((sresolv_log != ((void*)0) && sresolv_log->log_init ) == 0 ? 9 : ((sresolv_log != ((void*)0) && sresolv_log ->log_init > 1) ? sresolv_log->log_level : su_log_default ->log_level)) >= 9 ? (_su_llog(sresolv_log, 9, "sres.c" , (const char *)__func__, 968, "sres_query(%p, %p, %s, \"%s\") called\n" , (void *)res, (void *)context, sres_record_type(type, b), domain )) : (void)0); |
969 | |
970 | if (res == NULL((void*)0) || domain == NULL((void*)0)) |
971 | return su_seterrno(EFAULT14), (void *)NULL((void*)0); |
972 | |
973 | dlen = strlen(domain); |
974 | if (dlen > SRES_MAXDNAME(SRES_MAXDNAME) || |
975 | (dlen == SRES_MAXDNAME(SRES_MAXDNAME) && domain[dlen - 1] != '.')) { |
976 | su_seterrno(ENAMETOOLONG36); |
977 | return NULL((void*)0); |
978 | } |
979 | |
980 | /* Reread resolv.conf if needed */ |
981 | sres_resolver_update(res, 0); |
982 | |
983 | if (res->res_n_servers == 0) |
984 | return (void)su_seterrno(ENETDOWN100), (sres_query_t *)NULL((void*)0); |
985 | |
986 | query = sres_query_alloc(res, callback, context, type, domain); |
987 | |
988 | if (query && sres_send_dns_query(res, query) != 0) |
989 | sres_free_query(res, query), query = NULL((void*)0); |
990 | |
991 | return query; |
992 | } |
993 | |
994 | /**Search DNS. |
995 | * |
996 | * Sends DNS queries with specified @a type and @a name to the DNS server. |
997 | * If the @a name does not contain enought dots, the search domains are |
998 | * appended to the name and resulting domain name are also queried. When |
999 | * answer to all the search domains is received, the @a callback function |
1000 | * is called with @a context and combined records from answers as arguments. |
1001 | * |
1002 | * The sres resolver takes care of retransmitting the queries if a root |
1003 | * object is associate with the resolver or if sres_resolver_timer() is |
1004 | * called in regular intervals. It generates an error record with nonzero |
1005 | * status if no response is received. |
1006 | * |
1007 | * @param res pointer to resolver object |
1008 | * @param callback pointer to completion function |
1009 | * @param context argument given to the completion function |
1010 | * @param type record type to search (or sres_qtype_any for any record) |
1011 | * @param name host or domain name to search from DNS |
1012 | * |
1013 | * @ERRORS |
1014 | * @ERROR EFAULT @a res or @a domain point outside the address space |
1015 | * @ERROR ENAMETOOLONG @a domain is longer than SRES_MAXDNAME |
1016 | * @ERROR ENETDOWN no DNS servers configured |
1017 | * @ERROR ENOMEM memory exhausted |
1018 | * |
1019 | * @sa sres_query(), sres_blocking_search(), sres_search_cached_answers(). |
1020 | */ |
1021 | sres_query_t * |
1022 | sres_search(sres_resolver_t *res, |
1023 | sres_answer_f *callback, |
1024 | sres_context_t *context, |
1025 | uint16_t type, |
1026 | char const *name) |
1027 | { |
1028 | char const *domain = name; |
1029 | sres_query_t *query = NULL((void*)0); |
1030 | size_t dlen; |
1031 | unsigned dots; char const *dot; |
1032 | char b[8]; |
1033 | |
1034 | SU_DEBUG_9(("sres_search(%p, %p, %s, \"%s\") called\n",(((sresolv_log != ((void*)0) && sresolv_log->log_init ) == 0 ? 9 : ((sresolv_log != ((void*)0) && sresolv_log ->log_init > 1) ? sresolv_log->log_level : su_log_default ->log_level)) >= 9 ? (_su_llog(sresolv_log, 9, "sres.c" , (const char *)__func__, 1035, "sres_search(%p, %p, %s, \"%s\") called\n" , (void *)res, (void *)context, sres_record_type(type, b), domain )) : (void)0) |
1035 | (void *)res, (void *)context, sres_record_type(type, b), domain))(((sresolv_log != ((void*)0) && sresolv_log->log_init ) == 0 ? 9 : ((sresolv_log != ((void*)0) && sresolv_log ->log_init > 1) ? sresolv_log->log_level : su_log_default ->log_level)) >= 9 ? (_su_llog(sresolv_log, 9, "sres.c" , (const char *)__func__, 1035, "sres_search(%p, %p, %s, \"%s\") called\n" , (void *)res, (void *)context, sres_record_type(type, b), domain )) : (void)0); |
1036 | |
1037 | if (res == NULL((void*)0) || domain == NULL((void*)0)) |
1038 | return su_seterrno(EFAULT14), (void *)NULL((void*)0); |
1039 | |
1040 | dlen = strlen(domain); |
1041 | if (dlen > SRES_MAXDNAME(SRES_MAXDNAME) || |
1042 | (dlen == SRES_MAXDNAME(SRES_MAXDNAME) && domain[dlen - 1] != '.')) { |
1043 | su_seterrno(ENAMETOOLONG36); |
1044 | return NULL((void*)0); |
1045 | } |
1046 | |
1047 | sres_resolver_update(res, 0); |
1048 | |
1049 | if (res->res_n_servers == 0) |
1050 | return (void)su_seterrno(ENETDOWN100), (sres_query_t *)NULL((void*)0); |
1051 | |
1052 | if (domain[dlen - 1] == '.') |
1053 | /* Domain ends with dot - do not search */ |
1054 | dots = res->res_config->c_opt.ndots; |
1055 | else if (sres_has_search_domain(res)) |
1056 | for (dots = 0, dot = strchr(domain, '.'); |
1057 | dots < res->res_config->c_opt.ndots && dot; |
1058 | dots++, dot = strchr(dot + 1, '.')) |
1059 | ; |
1060 | else |
1061 | dots = 0; |
1062 | |
1063 | query = sres_query_alloc(res, callback, context, type, domain); |
1064 | |
1065 | if (query) { |
1066 | /* Create sub-query for each search domain */ |
1067 | if (dots < res->res_config->c_opt.ndots) { |
1068 | sres_query_t *sub; |
1069 | int i, subs; |
1070 | size_t len; |
1071 | char const *const *domains = res->res_config->c_search; |
1072 | char search[SRES_MAXDNAME(SRES_MAXDNAME) + 1]; |
1073 | |
1074 | assert(dlen < SRES_MAXDNAME)((void) sizeof ((dlen < (SRES_MAXDNAME)) ? 1 : 0), __extension__ ({ if (dlen < (SRES_MAXDNAME)) ; else __assert_fail ("dlen < SRES_MAXDNAME" , "sres.c", 1074, __extension__ __PRETTY_FUNCTION__); })); |
1075 | |
1076 | memcpy(search, domain, dlen); |
1077 | search[dlen++] = '.'; |
1078 | search[dlen] = '\0'; |
1079 | |
1080 | for (i = 0, subs = 0; i <= SRES_MAX_SEARCH(SRES_MAX_SEARCH); i++) { |
1081 | if (domains[i]) { |
1082 | len = strlen(domains[i]); |
1083 | |
1084 | if (dlen + len + 1 > SRES_MAXDNAME(SRES_MAXDNAME)) |
1085 | continue; |
1086 | |
1087 | memcpy(search + dlen, domains[i], len); |
1088 | search[dlen + len] = '.'; |
1089 | search[dlen + len + 1] = '\0'; |
1090 | sub = sres_query_alloc(res, sres_answer_subquery, (void *)query, |
1091 | type, search); |
1092 | |
1093 | if (sub == NULL((void*)0)) { |
1094 | } |
1095 | else if (sres_send_dns_query(res, sub) == 0) { |
1096 | query->q_subqueries[i] = sub; |
1097 | } |
1098 | else { |
1099 | sres_free_query(res, sub), sub = NULL((void*)0); |
1100 | } |
1101 | subs += sub != NULL((void*)0); |
1102 | } |
1103 | } |
1104 | |
1105 | query->q_n_subs = subs; |
1106 | } |
1107 | |
1108 | if (sres_send_dns_query(res, query) != 0) { |
1109 | if (!query->q_n_subs) |
1110 | sres_free_query(res, query), query = NULL((void*)0); |
1111 | else |
1112 | query->q_id = 0; |
1113 | } |
1114 | } |
1115 | |
1116 | return query; |
1117 | } |
1118 | |
1119 | /** Make a reverse DNS query. |
1120 | * |
1121 | * Send a query to DNS server with specified @a type and domain name formed |
1122 | * from the socket address @a addr. The sres resolver takes care of |
1123 | * retransmitting the query if a root object is associate with the resolver or |
1124 | * if sres_resolver_timer() is called in regular intervals. It generates an |
1125 | * error record with nonzero status if no response is received. |
1126 | * |
1127 | * @param res pointer to resolver |
1128 | * @param callback function called when query is answered or times out |
1129 | * @param context pointer given as an extra argument to @a callback function |
1130 | * @param type record type to query (or sres_qtype_any for any record) |
1131 | * @param addr socket address structure |
1132 | * |
1133 | * The @a type should be #sres_type_ptr. The @a addr should contain either |
1134 | * IPv4 (AF_INET) or IPv6 (AF_INET6) address. |
1135 | * |
1136 | * If the #SRES_OPTIONS environment variable, #RES_OPTIONS environment |
1137 | * variable, or an "options" entry in resolv.conf file contains an option |
1138 | * "ip6-dotint", the IPv6 addresses are resolved using suffix ".ip6.int" |
1139 | * instead of the standard ".ip6.arpa" suffix. |
1140 | * |
1141 | * @ERRORS |
1142 | * @ERROR EAFNOSUPPORT address family specified in @a addr is not supported |
1143 | * @ERROR ENETDOWN no DNS servers configured |
1144 | * @ERROR EFAULT @a res or @a addr point outside the address space |
1145 | * @ERROR ENOMEM memory exhausted |
1146 | * |
1147 | * @sa sres_query(), sres_blocking_query_sockaddr(), |
1148 | * sres_cached_answers_sockaddr() |
1149 | * |
1150 | */ |
1151 | sres_query_t * |
1152 | sres_query_sockaddr(sres_resolver_t *res, |
1153 | sres_answer_f *callback, |
1154 | sres_context_t *context, |
1155 | uint16_t type, |
1156 | struct sockaddr const *addr) |
1157 | { |
1158 | char name[80]; |
1159 | |
1160 | if (!res || !addr) |
1161 | return su_seterrno(EFAULT14), (void *)NULL((void*)0); |
1162 | |
1163 | if (!sres_sockaddr2string(res, name, sizeof(name), addr)) |
1164 | return NULL((void*)0); |
1165 | |
1166 | return sres_query(res, callback, context, type, name); |
1167 | } |
1168 | |
1169 | |
1170 | /** Make a DNS query. |
1171 | * |
1172 | * @deprecated Use sres_query() instead. |
1173 | */ |
1174 | sres_query_t * |
1175 | sres_query_make(sres_resolver_t *res, |
1176 | sres_answer_f *callback, |
1177 | sres_context_t *context, |
1178 | int dummy, |
1179 | uint16_t type, |
1180 | char const *domain) |
1181 | { |
1182 | return sres_query(res, callback, context, type, domain); |
1183 | } |
1184 | |
1185 | /** Make a reverse DNS query. |
1186 | * |
1187 | * @deprecated Use sres_query_sockaddr() instead. |
1188 | */ |
1189 | sres_query_t * |
1190 | sres_query_make_sockaddr(sres_resolver_t *res, |
1191 | sres_answer_f *callback, |
1192 | sres_context_t *context, |
1193 | int dummy, |
1194 | uint16_t type, |
1195 | struct sockaddr const *addr) |
1196 | { |
1197 | char name[80]; |
1198 | |
1199 | if (!res || !addr) |
1200 | return su_seterrno(EFAULT14), (void *)NULL((void*)0); |
1201 | |
1202 | if (!sres_sockaddr2string(res, name, sizeof(name), addr)) |
1203 | return NULL((void*)0); |
1204 | |
1205 | return sres_query_make(res, callback, context, dummy, type, name); |
1206 | } |
1207 | |
1208 | |
1209 | /** Bind a query with another callback and context pointer. |
1210 | * |
1211 | * @param query pointer to a query object to bind |
1212 | * @param callback pointer to new callback function (may be NULL) |
1213 | * @param context pointer to callback context (may be NULL) |
1214 | */ |
1215 | void sres_query_bind(sres_query_t *query, |
1216 | sres_answer_f *callback, |
1217 | sres_context_t *context) |
1218 | { |
1219 | if (query) { |
1220 | query->q_callback = callback; |
1221 | query->q_context = context; |
1222 | } |
1223 | } |
1224 | |
1225 | /**Get a list of matching (type/domain) records from cache. |
1226 | * |
1227 | * @return |
1228 | * pointer to an array of pointers to cached records, or |
1229 | * NULL if no entry was found. |
1230 | * |
1231 | * @ERRORS |
1232 | * @ERROR ENAMETOOLONG @a domain is longer than SRES_MAXDNAME |
1233 | * @ERROR ENOENT no cached records were found |
1234 | * @ERROR EFAULT @a res or @a domain point outside the address space |
1235 | * @ERROR ENOMEM memory exhausted |
1236 | */ |
1237 | sres_record_t ** |
1238 | sres_cached_answers(sres_resolver_t *res, |
1239 | uint16_t type, |
1240 | char const *domain) |
1241 | { |
1242 | sres_record_t **result; |
1243 | char rooted_domain[SRES_MAXDNAME(SRES_MAXDNAME)]; |
1244 | |
1245 | if (!res) |
1246 | return su_seterrno(EFAULT14), (void *)NULL((void*)0); |
1247 | |
1248 | domain = sres_toplevel(rooted_domain, sizeof rooted_domain, domain); |
1249 | |
1250 | if (!domain) |
1251 | return NULL((void*)0); |
1252 | |
1253 | if (!sres_cache_get(res->res_cache, type, domain, &result)) |
1254 | return su_seterrno(ENOENT2), (void *)NULL((void*)0); |
1255 | |
1256 | return result; |
1257 | } |
1258 | |
1259 | /**Search for a list of matching (type/name) records from cache. |
1260 | * |
1261 | * @return |
1262 | * pointer to an array of pointers to cached records, or |
1263 | * NULL if no entry was found. |
1264 | * |
1265 | * @ERRORS |
1266 | * @ERROR ENAMETOOLONG @a name or resulting domain is longer than SRES_MAXDNAME |
1267 | * @ERROR ENOENT no cached records were found |
1268 | * @ERROR EFAULT @a res or @a domain point outside the address space |
1269 | * @ERROR ENOMEM memory exhausted |
1270 | * |
1271 | * @sa sres_search(), sres_cached_answers() |
1272 | */ |
1273 | sres_record_t ** |
1274 | sres_search_cached_answers(sres_resolver_t *res, |
1275 | uint16_t type, |
1276 | char const *name) |
1277 | { |
1278 | char const *domain = name; |
1279 | sres_record_t **search_results[SRES_MAX_SEARCH(SRES_MAX_SEARCH) + 1] = { NULL((void*)0) }; |
1280 | char rooted_domain[SRES_MAXDNAME(SRES_MAXDNAME)]; |
1281 | unsigned dots; char const *dot; |
1282 | size_t found = 0; |
1283 | int i; |
1284 | |
1285 | SU_DEBUG_9(("sres_search_cached_answers(%p, %s, \"%s\") called\n",(((sresolv_log != ((void*)0) && sresolv_log->log_init ) == 0 ? 9 : ((sresolv_log != ((void*)0) && sresolv_log ->log_init > 1) ? sresolv_log->log_level : su_log_default ->log_level)) >= 9 ? (_su_llog(sresolv_log, 9, "sres.c" , (const char *)__func__, 1286, "sres_search_cached_answers(%p, %s, \"%s\") called\n" , (void *)res, sres_record_type(type, rooted_domain), domain) ) : (void)0) |
1286 | (void *)res, sres_record_type(type, rooted_domain), domain))(((sresolv_log != ((void*)0) && sresolv_log->log_init ) == 0 ? 9 : ((sresolv_log != ((void*)0) && sresolv_log ->log_init > 1) ? sresolv_log->log_level : su_log_default ->log_level)) >= 9 ? (_su_llog(sresolv_log, 9, "sres.c" , (const char *)__func__, 1286, "sres_search_cached_answers(%p, %s, \"%s\") called\n" , (void *)res, sres_record_type(type, rooted_domain), domain) ) : (void)0); |
1287 | |
1288 | if (!res || !name) |
1289 | return su_seterrno(EFAULT14), (void *)NULL((void*)0); |
1290 | |
1291 | if (sres_has_search_domain(res)) |
1292 | for (dots = 0, dot = strchr(domain, '.'); |
1293 | dots < res->res_config->c_opt.ndots && dot; |
1294 | dots++, dot = strchr(dot + 1, '.')) |
1295 | ; |
1296 | else |
1297 | dots = 0; |
1298 | |
1299 | domain = sres_toplevel(rooted_domain, sizeof rooted_domain, domain); |
1300 | |
1301 | if (!domain) |
1302 | return NULL((void*)0); |
1303 | |
1304 | if (sres_cache_get(res->res_cache, type, domain, &search_results[0])) |
1305 | found = 1; |
1306 | |
1307 | if (dots < res->res_config->c_opt.ndots) { |
1308 | char const *const *domains = res->res_config->c_search; |
1309 | size_t dlen = strlen(domain); |
1310 | |
1311 | for (i = 0; domains[i] && i < SRES_MAX_SEARCH(SRES_MAX_SEARCH); i++) { |
1312 | size_t len = strlen(domains[i]); |
1313 | if (dlen + len + 1 >= SRES_MAXDNAME(SRES_MAXDNAME)) |
1314 | continue; |
1315 | if (domain != rooted_domain) |
1316 | domain = memcpy(rooted_domain, domain, dlen); |
1317 | memcpy(rooted_domain + dlen, domains[i], len); |
1318 | strcpy(rooted_domain + dlen + len, "."); |
1319 | if (sres_cache_get(res->res_cache, type, domain, search_results + i + 1)) |
1320 | found++; |
1321 | } |
1322 | } |
1323 | |
1324 | if (found == 0) |
1325 | return su_seterrno(ENOENT2), (void *)NULL((void*)0); |
1326 | |
1327 | if (found == 1) { |
1328 | for (i = 0; i <= SRES_MAX_SEARCH(SRES_MAX_SEARCH); i++) |
1329 | if (search_results[i]) |
1330 | return search_results[i]; |
1331 | } |
1332 | |
1333 | return sres_combine_results(res, search_results); |
1334 | } |
1335 | |
1336 | /**Get a list of matching (type/domain) reverse records from cache. |
1337 | * |
1338 | * @param res pointer to resolver |
1339 | * @param type record type to query (or sres_qtype_any for any record) |
1340 | * @param addr socket address structure |
1341 | * |
1342 | * The @a type should be #sres_type_ptr. The @a addr should contain either |
1343 | * IPv4 (AF_INET) or IPv6 (AF_INET6) address. |
1344 | * |
1345 | * If the #SRES_OPTIONS environment variable, #RES_OPTIONS environment |
1346 | * variable or an "options" entry in resolv.conf file contains an option |
1347 | * "ip6-dotint", the IPv6 addresses are resolved using suffix ".ip6.int" |
1348 | * instead of default ".ip6.arpa". |
1349 | * |
1350 | * @retval |
1351 | * pointer to an array of pointers to cached records, or |
1352 | * NULL if no entry was found. |
1353 | * |
1354 | * @ERRORS |
1355 | * @ERROR EAFNOSUPPORT address family specified in @a addr is not supported |
1356 | * @ERROR ENOENT no cached records were found |
1357 | * @ERROR EFAULT @a res or @a addr point outside the address space |
1358 | * @ERROR ENOMEM memory exhausted |
1359 | */ |
1360 | sres_record_t ** |
1361 | sres_cached_answers_sockaddr(sres_resolver_t *res, |
1362 | uint16_t type, |
1363 | struct sockaddr const *addr) |
1364 | { |
1365 | sres_record_t **result; |
1366 | char name[80]; |
1367 | |
1368 | if (!res || !addr) |
1369 | return su_seterrno(EFAULT14), (void *)NULL((void*)0); |
1370 | |
1371 | if (!sres_sockaddr2string(res, name, sizeof name, addr)) |
1372 | return NULL((void*)0); |
1373 | |
1374 | if (!sres_cache_get(res->res_cache, type, name, &result)) |
1375 | return su_seterrno(ENOENT2), (void *)NULL((void*)0); |
1376 | |
1377 | return result; |
1378 | } |
1379 | |
1380 | /** Set the priority of the matching cached SRV record. |
1381 | * |
1382 | * The SRV records with the domain name, target and port are matched and |
1383 | * their priority value is adjusted. This function is used to implement |
1384 | * greylisting of SIP servers. |
1385 | * |
1386 | * @param res pointer to resolver |
1387 | * @param domain domain name of the SRV record(s) to modify |
1388 | * @param target SRV target of the SRV record(s) to modify |
1389 | * @param port port number of SRV record(s) to modify |
1390 | * (in host byte order) |
1391 | * @param ttl new ttl for SRV records of the domain |
1392 | * @param priority new priority value (0=highest, 65535=lowest) |
1393 | * |
1394 | * @sa sres_cache_set_srv_priority() |
1395 | * |
1396 | * @NEW_1_12_8 |
1397 | */ |
1398 | int sres_set_cached_srv_priority(sres_resolver_t *res, |
1399 | char const *domain, |
1400 | char const *target, |
1401 | uint16_t port, |
1402 | uint32_t ttl, |
1403 | uint16_t priority) |
1404 | { |
1405 | char rooted_domain[SRES_MAXDNAME(SRES_MAXDNAME)]; |
1406 | |
1407 | if (res == NULL((void*)0) || res->res_cache == NULL((void*)0)) |
1408 | return su_seterrno(EFAULT14); |
1409 | |
1410 | domain = sres_toplevel(rooted_domain, sizeof rooted_domain, domain); |
1411 | |
1412 | if (!domain) |
1413 | return -1; |
1414 | |
1415 | return sres_cache_set_srv_priority(res->res_cache, |
1416 | domain, target, port, |
1417 | ttl, priority); |
1418 | } |
1419 | |
1420 | |
1421 | /** Sort answers. */ |
1422 | int |
1423 | sres_sort_answers(sres_resolver_t *res, sres_record_t **answers) |
1424 | { |
1425 | int i, j; |
1426 | |
1427 | if (res == NULL((void*)0) || answers == NULL((void*)0)) |
1428 | return su_seterrno(EFAULT14); |
1429 | |
1430 | if (answers[0] == NULL((void*)0) || answers[1] == NULL((void*)0)) |
1431 | return 0; |
1432 | |
1433 | /* Simple insertion sorting */ |
1434 | /* |
1435 | * We do not use qsort because we want later extend this to sort |
1436 | * local A records first etc. |
1437 | */ |
1438 | for (i = 1; answers[i]; i++) { |
1439 | for (j = 0; j < i; j++) { |
1440 | if (sres_record_compare(answers[i], answers[j]) < 0) |
1441 | break; |
1442 | } |
1443 | if (j < i) { |
1444 | sres_record_t *r = answers[i]; |
1445 | for (; j < i; i--) { |
1446 | answers[i] = answers[i - 1]; |
1447 | } |
1448 | answers[j] = r; |
1449 | } |
1450 | } |
1451 | |
1452 | return 0; |
1453 | } |
1454 | |
1455 | /** Sort and filter query results */ |
1456 | int |
1457 | sres_filter_answers(sres_resolver_t *res, |
1458 | sres_record_t **answers, |
1459 | uint16_t type) |
1460 | { |
1461 | int i, n; |
1462 | |
1463 | if (res == NULL((void*)0) || answers == NULL((void*)0)) |
1464 | return su_seterrno(EFAULT14); |
1465 | |
1466 | for (n = 0, i = 0; answers[i]; i++) { |
1467 | if (answers[i]->sr_record->r_status || |
1468 | answers[i]->sr_record->r_class != sres_class_in || |
1469 | (type != 0 && answers[i]->sr_record->r_type != type)) { |
1470 | sres_free_answer(res, answers[i]); |
1471 | continue; |
1472 | } |
1473 | answers[n++] = answers[i]; |
1474 | } |
1475 | answers[n] = NULL((void*)0); |
1476 | |
1477 | sres_sort_answers(res, answers); |
1478 | |
1479 | return n; |
1480 | } |
1481 | |
1482 | |
1483 | /** Free and zero one record. */ |
1484 | void sres_free_answer(sres_resolver_t *res, sres_record_t *answer) |
1485 | { |
1486 | if (res && answer) |
1487 | sres_cache_free_one(res->res_cache, answer); |
1488 | } |
1489 | |
1490 | /** Free and zero an array of records. |
1491 | * |
1492 | * The array of records can be returned by sres_cached_answers() or |
1493 | * given by callback function. |
1494 | */ |
1495 | void |
1496 | sres_free_answers(sres_resolver_t *res, |
1497 | sres_record_t **answers) |
1498 | { |
1499 | if (res && answers) |
1500 | sres_cache_free_answers(res->res_cache, answers); |
1501 | } |
1502 | |
1503 | /** Convert type to its name. */ |
1504 | char const *sres_record_type(int type, char buffer[8]) |
1505 | { |
1506 | switch (type) { |
1507 | case sres_type_a: return "A"; |
1508 | case sres_type_ns: return "NS"; |
1509 | case sres_type_mf: return "MF"; |
1510 | case sres_type_cname: return "CNAME"; |
1511 | case sres_type_soa: return "SOA"; |
1512 | case sres_type_mb: return "MB"; |
1513 | case sres_type_mg: return "MG"; |
1514 | case sres_type_mr: return "MR"; |
1515 | case sres_type_null: return "NULL"; |
1516 | case sres_type_wks: return "WKS"; |
1517 | case sres_type_ptr: return "PTR"; |
1518 | case sres_type_hinfo: return "HINFO"; |
1519 | case sres_type_minfo: return "MINFO"; |
1520 | case sres_type_mx: return "MX"; |
1521 | case sres_type_txt: return "TXT"; |
1522 | case sres_type_rp: return "RP"; |
1523 | case sres_type_afsdb: return "AFSDB"; |
1524 | case sres_type_x25: return "X25"; |
1525 | case sres_type_isdn: return "ISDN"; |
1526 | case sres_type_rt: return "RT"; |
1527 | case sres_type_nsap: return "NSAP"; |
1528 | case sres_type_nsap_ptr: return "NSAP_PTR"; |
1529 | case sres_type_sig: return "SIG"; |
1530 | case sres_type_key: return "KEY"; |
1531 | case sres_type_px: return "PX"; |
1532 | case sres_type_gpos: return "GPOS"; |
1533 | case sres_type_aaaa: return "AAAA"; |
1534 | case sres_type_loc: return "LOC"; |
1535 | case sres_type_nxt: return "NXT"; |
1536 | case sres_type_eid: return "EID"; |
1537 | case sres_type_nimloc: return "NIMLOC"; |
1538 | case sres_type_srv: return "SRV"; |
1539 | case sres_type_atma: return "ATMA"; |
1540 | case sres_type_naptr: return "NAPTR"; |
1541 | case sres_type_kx: return "KX"; |
1542 | case sres_type_cert: return "CERT"; |
1543 | case sres_type_a6: return "A6"; |
1544 | case sres_type_dname: return "DNAME"; |
1545 | case sres_type_sink: return "SINK"; |
1546 | case sres_type_opt: return "OPT"; |
1547 | |
1548 | case sres_qtype_tsig: return "TSIG"; |
1549 | case sres_qtype_ixfr: return "IXFR"; |
1550 | case sres_qtype_axfr: return "AXFR"; |
1551 | case sres_qtype_mailb: return "MAILB"; |
1552 | case sres_qtype_maila: return "MAILA"; |
1553 | case sres_qtype_any: return "ANY"; |
1554 | |
1555 | default: |
1556 | if (buffer) |
1557 | sprintf(buffer, "%u?", type & 65535); |
1558 | return buffer; |
1559 | } |
1560 | } |
1561 | |
1562 | /** Convert record status to its name */ |
1563 | char const *sres_record_status(int status, char buffer[8]) |
1564 | { |
1565 | switch (status) { |
1566 | case SRES_OK: return "OK"; |
1567 | case SRES_FORMAT_ERR: return "FORMAT_ERR"; |
1568 | case SRES_SERVER_ERR: return "SERVER_ERR"; |
1569 | case SRES_NAME_ERR: return "NAME_ERR"; |
1570 | case SRES_UNIMPL_ERR: return "UNIMPL_ERR"; |
1571 | case SRES_AUTH_ERR: return "AUTH_ERR"; |
1572 | |
1573 | /* Errors generated by sresolv */ |
1574 | case SRES_TIMEOUT_ERR: return "TIMEOUT_ERR"; |
1575 | case SRES_RECORD_ERR: return "RECORD_ERR"; |
1576 | case SRES_INTERNAL_ERR: return "INTERNAL_ERR"; |
1577 | case SRES_NETWORK_ERR: return "NETWORK_ERR"; |
1578 | |
1579 | default: |
1580 | if (buffer) |
1581 | sprintf(buffer, "%u?", status & 255); |
1582 | return buffer; |
1583 | } |
1584 | } |
1585 | |
1586 | |
1587 | /** Convert class to its name. */ |
1588 | static char const * |
1589 | sres_record_class(int rclass, char buffer[8]) |
1590 | { |
1591 | switch (rclass) { |
1592 | case 1: return "IN"; |
1593 | case 2: return "2?"; |
1594 | case 3: return "CHAOS"; |
1595 | case 4: return "HS"; |
1596 | case 254: return "NONE"; |
1597 | case 255: return "ANY"; |
1598 | |
1599 | default: |
1600 | sprintf(buffer, "%u?", rclass & 65535); |
1601 | return buffer; |
1602 | } |
1603 | } |
1604 | |
1605 | /** Compare two records. */ |
1606 | int |
1607 | sres_record_compare(sres_record_t const *aa, sres_record_t const *bb) |
1608 | { |
1609 | int D; |
1610 | sres_common_t const *a = aa->sr_record, *b = bb->sr_record; |
1611 | |
1612 | D = a->r_status - b->r_status; if (D) return D; |
1613 | D = a->r_class - b->r_class; if (D) return D; |
1614 | D = a->r_type - b->r_type; if (D) return D; |
1615 | |
1616 | if (a->r_status) |
1617 | return 0; |
1618 | |
1619 | switch (a->r_type) { |
1620 | case sres_type_soa: |
1621 | { |
1622 | sres_soa_record_t const *A = aa->sr_soa, *B = bb->sr_soa; |
1623 | D = A->soa_serial - B->soa_serial; if (D) return D; |
1624 | D = su_strcasecmp(A->soa_mname, B->soa_mname); if (D) return D; |
1625 | D = su_strcasecmp(A->soa_rname, B->soa_rname); if (D) return D; |
1626 | D = A->soa_refresh - B->soa_refresh; if (D) return D; |
1627 | D = A->soa_retry - B->soa_retry; if (D) return D; |
1628 | D = A->soa_expire - B->soa_expire; if (D) return D; |
1629 | D = A->soa_minimum - B->soa_minimum; if (D) return D; |
1630 | return 0; |
1631 | } |
1632 | case sres_type_a: |
1633 | { |
1634 | sres_a_record_t const *A = aa->sr_a, *B = bb->sr_a; |
1635 | return memcmp(&A->a_addr, &B->a_addr, sizeof A->a_addr); |
1636 | } |
1637 | case sres_type_a6: |
1638 | { |
1639 | sres_a6_record_t const *A = aa->sr_a6, *B = bb->sr_a6; |
1640 | D = A->a6_prelen - B->a6_prelen; if (D) return D; |
1641 | D = !A->a6_prename - !B->a6_prename; |
1642 | if (D == 0 && A->a6_prename && B->a6_prename) |
1643 | D = su_strcasecmp(A->a6_prename, B->a6_prename); |
1644 | if (D) return D; |
1645 | return memcmp(&A->a6_suffix, &B->a6_suffix, sizeof A->a6_suffix); |
1646 | } |
1647 | case sres_type_aaaa: |
1648 | { |
1649 | sres_aaaa_record_t const *A = aa->sr_aaaa, *B = bb->sr_aaaa; |
1650 | return memcmp(&A->aaaa_addr, &B->aaaa_addr, sizeof A->aaaa_addr); |
1651 | } |
1652 | case sres_type_cname: |
1653 | { |
1654 | sres_cname_record_t const *A = aa->sr_cname, *B = bb->sr_cname; |
1655 | return strcmp(A->cn_cname, B->cn_cname); |
1656 | } |
1657 | case sres_type_ptr: |
1658 | { |
1659 | sres_ptr_record_t const *A = aa->sr_ptr, *B = bb->sr_ptr; |
1660 | return strcmp(A->ptr_domain, B->ptr_domain); |
1661 | } |
1662 | case sres_type_srv: |
1663 | { |
1664 | sres_srv_record_t const *A = aa->sr_srv, *B = bb->sr_srv; |
1665 | D = A->srv_priority - B->srv_priority; if (D) return D; |
1666 | /* Record with larger weight first */ |
1667 | D = B->srv_weight - A->srv_weight; if (D) return D; |
1668 | D = strcmp(A->srv_target, B->srv_target); if (D) return D; |
1669 | return A->srv_port - B->srv_port; |
1670 | } |
1671 | case sres_type_naptr: |
1672 | { |
1673 | sres_naptr_record_t const *A = aa->sr_naptr, *B = bb->sr_naptr; |
1674 | D = A->na_order - B->na_order; if (D) return D; |
1675 | D = A->na_prefer - B->na_prefer; if (D) return D; |
1676 | D = strcmp(A->na_flags, B->na_flags); if (D) return D; |
1677 | D = strcmp(A->na_services, B->na_services); if (D) return D; |
1678 | D = strcmp(A->na_regexp, B->na_regexp); if (D) return D; |
1679 | return strcmp(A->na_replace, B->na_replace); |
1680 | } |
1681 | default: |
1682 | return 0; |
1683 | } |
1684 | } |
1685 | |
1686 | /* ---------------------------------------------------------------------- */ |
1687 | /* Private functions */ |
1688 | |
1689 | /** Destruct */ |
1690 | static |
1691 | void |
1692 | sres_resolver_destructor(void *arg) |
1693 | { |
1694 | sres_resolver_t *res = arg; |
1695 | |
1696 | assert(res)((void) sizeof ((res) ? 1 : 0), __extension__ ({ if (res) ; else __assert_fail ("res", "sres.c", 1696, __extension__ __PRETTY_FUNCTION__ ); })); |
1697 | sres_cache_unref(res->res_cache); |
1698 | res->res_cache = NULL((void*)0); |
1699 | |
1700 | sres_servers_close(res, res->res_servers); |
1701 | |
1702 | if (res->res_config) |
1703 | su_home_unref((su_home_t *)res->res_config->c_home); |
1704 | |
1705 | if (res->res_updcb) |
1706 | res->res_updcb(res->res_async, INVALID_SOCKET((sres_socket_t)-1), INVALID_SOCKET((sres_socket_t)-1)); |
1707 | } |
1708 | |
1709 | #ifdef __clang__1 |
1710 | #pragma clang diagnostic push |
1711 | #pragma clang diagnostic ignored "-Wunused-function" |
1712 | #endif |
1713 | |
1714 | HTABLE_BODIES_WITH(sres_qtable, qt, sres_query_t, SRES_QUERY_HASH,static inline int sres_qtable_resize(su_home_t *home, sres_qtable_t qt[], unsigned new_size) { sres_query_t **new_hash; sres_query_t **old_hash = qt->qt_table; unsigned old_size; unsigned i, j, i0; unsigned again = 0; unsigned used = 0, collisions = 0 ; if (new_size == 0) new_size = 2 * qt->qt_size + 1; if (new_size < 31) new_size = 31; if (new_size < 5 * qt->qt_used / 4) new_size = 5 * qt->qt_used / 4; if (!(new_hash = su_zalloc (home, sizeof(*new_hash) * new_size))) return -1; old_size = qt ->qt_size; do for (j = 0; j < old_size; j++) { if (!old_hash [j]) continue; if (again < 2 && ((old_hash[j])-> q_hash) % old_size > j) { again = 1; continue; } i0 = ((old_hash [j])->q_hash) % new_size; for (i = i0; new_hash[i]; i = (i + 1) % new_size, ((void) sizeof ((i != i0) ? 1 : 0), __extension__ ({ if (i != i0) ; else __assert_fail ("i != i0", "sres.c", 1715 , __extension__ __PRETTY_FUNCTION__); }))) collisions++; new_hash [i] = old_hash[j], old_hash[j] = ((void*)0); used++; } while ( again++ == 1); qt->qt_table = new_hash, qt->qt_size = new_size ; ((void) sizeof ((qt->qt_used == used) ? 1 : 0), __extension__ ({ if (qt->qt_used == used) ; else __assert_fail ("qt->qt_used == used" , "sres.c", 1715, __extension__ __PRETTY_FUNCTION__); })); su_free (home, old_hash); return 0; } static inline int sres_qtable_is_full (sres_qtable_t const *qt) { return qt->qt_table == ((void* )0) || 3 * qt->qt_used > 2 * qt->qt_size; } static inline sres_query_t **sres_qtable_hash(sres_qtable_t const *qt, size_t hv) { return qt->qt_table + hv % qt->qt_size; } static inline sres_query_t **sres_qtable_next(sres_qtable_t const * qt, sres_query_t * const *ee) { if (++ee < qt->qt_table + qt->qt_size && ee >= qt->qt_table) return (sres_query_t **)ee; else return qt->qt_table; } static inline void sres_qtable_append(sres_qtable_t *qt, sres_query_t const *e) { sres_query_t **ee; qt->qt_used++; for (ee = sres_qtable_hash (qt, ((e)->q_hash)); *ee; ee = sres_qtable_next(qt, ee)) ; *ee = (sres_query_t *)e; } static inline void sres_qtable_insert (sres_qtable_t *qt, sres_query_t const *e) { sres_query_t *e0 , **ee; qt->qt_used++; for (ee = sres_qtable_hash(qt, ((e) ->q_hash)); (e0 = *ee); ee = sres_qtable_next(qt, ee)) *ee = (sres_query_t *)e, e = e0; *ee = (sres_query_t *)e; } static inline int sres_qtable_remove(sres_qtable_t *qt, sres_query_t const *e) { unsigned i, j, k; unsigned size = qt->qt_size ; sres_query_t **htable = qt->qt_table; if (!e) return -1; for (i = ((e)->q_hash) % size; htable[i]; i = (i + 1) % size ) if (e == htable[i]) break; if (!htable[i]) return -1; for ( j = (i + 1) % size; htable[j]; j = (j + 1) % size) { k = ((htable [j])->q_hash) % size; if (k == j) continue; if (j > i ? (i < k && k < j) : (i < k || k < j)) continue ; htable[i] = htable[j], i = j; } qt->qt_used--; htable[i] = ((void*)0); return 0; } extern int sres_qtable_dummy |
1715 | unsigned, size_t)static inline int sres_qtable_resize(su_home_t *home, sres_qtable_t qt[], unsigned new_size) { sres_query_t **new_hash; sres_query_t **old_hash = qt->qt_table; unsigned old_size; unsigned i, j, i0; unsigned again = 0; unsigned used = 0, collisions = 0 ; if (new_size == 0) new_size = 2 * qt->qt_size + 1; if (new_size < 31) new_size = 31; if (new_size < 5 * qt->qt_used / 4) new_size = 5 * qt->qt_used / 4; if (!(new_hash = su_zalloc (home, sizeof(*new_hash) * new_size))) return -1; old_size = qt ->qt_size; do for (j = 0; j < old_size; j++) { if (!old_hash [j]) continue; if (again < 2 && ((old_hash[j])-> q_hash) % old_size > j) { again = 1; continue; } i0 = ((old_hash [j])->q_hash) % new_size; for (i = i0; new_hash[i]; i = (i + 1) % new_size, ((void) sizeof ((i != i0) ? 1 : 0), __extension__ ({ if (i != i0) ; else __assert_fail ("i != i0", "sres.c", 1715 , __extension__ __PRETTY_FUNCTION__); }))) collisions++; new_hash [i] = old_hash[j], old_hash[j] = ((void*)0); used++; } while ( again++ == 1); qt->qt_table = new_hash, qt->qt_size = new_size ; ((void) sizeof ((qt->qt_used == used) ? 1 : 0), __extension__ ({ if (qt->qt_used == used) ; else __assert_fail ("qt->qt_used == used" , "sres.c", 1715, __extension__ __PRETTY_FUNCTION__); })); su_free (home, old_hash); return 0; } static inline int sres_qtable_is_full (sres_qtable_t const *qt) { return qt->qt_table == ((void* )0) || 3 * qt->qt_used > 2 * qt->qt_size; } static inline sres_query_t **sres_qtable_hash(sres_qtable_t const *qt, size_t hv) { return qt->qt_table + hv % qt->qt_size; } static inline sres_query_t **sres_qtable_next(sres_qtable_t const * qt, sres_query_t * const *ee) { if (++ee < qt->qt_table + qt->qt_size && ee >= qt->qt_table) return (sres_query_t **)ee; else return qt->qt_table; } static inline void sres_qtable_append(sres_qtable_t *qt, sres_query_t const *e) { sres_query_t **ee; qt->qt_used++; for (ee = sres_qtable_hash (qt, ((e)->q_hash)); *ee; ee = sres_qtable_next(qt, ee)) ; *ee = (sres_query_t *)e; } static inline void sres_qtable_insert (sres_qtable_t *qt, sres_query_t const *e) { sres_query_t *e0 , **ee; qt->qt_used++; for (ee = sres_qtable_hash(qt, ((e) ->q_hash)); (e0 = *ee); ee = sres_qtable_next(qt, ee)) *ee = (sres_query_t *)e, e = e0; *ee = (sres_query_t *)e; } static inline int sres_qtable_remove(sres_qtable_t *qt, sres_query_t const *e) { unsigned i, j, k; unsigned size = qt->qt_size ; sres_query_t **htable = qt->qt_table; if (!e) return -1; for (i = ((e)->q_hash) % size; htable[i]; i = (i + 1) % size ) if (e == htable[i]) break; if (!htable[i]) return -1; for ( j = (i + 1) % size; htable[j]; j = (j + 1) % size) { k = ((htable [j])->q_hash) % size; if (k == j) continue; if (j > i ? (i < k && k < j) : (i < k || k < j)) continue ; htable[i] = htable[j], i = j; } qt->qt_used--; htable[i] = ((void*)0); return 0; } extern int sres_qtable_dummy; |
1716 | |
1717 | #ifdef __clang__1 |
1718 | #pragma clang diagnostic pop |
1719 | #endif |
1720 | |
1721 | /** Allocate a query structure */ |
1722 | static |
1723 | sres_query_t * |
1724 | sres_query_alloc(sres_resolver_t *res, |
1725 | sres_answer_f *callback, |
1726 | sres_context_t *context, |
1727 | uint16_t type, |
1728 | char const *domain) |
1729 | { |
1730 | sres_query_t *query; |
1731 | size_t dlen = strlen(domain); |
1732 | |
1733 | if (sres_qtable_is_full(res->res_queries)) |
1734 | if (sres_qtable_resize(res->res_home, res->res_queries, 0) < 0) |
1735 | return NULL((void*)0); |
1736 | |
1737 | query = su_alloc(res->res_home, sizeof(*query) + dlen + 1); |
1738 | |
1739 | if (query) { |
1740 | memset(query, 0, sizeof *query); |
1741 | query->q_res = res; |
1742 | query->q_callback = callback; |
1743 | query->q_context = context; |
1744 | query->q_type = type; |
1745 | query->q_class = sres_class_in; |
1746 | query->q_timestamp = res->res_now; |
1747 | query->q_name = strcpy((char *)(query + 1), domain); |
1748 | |
1749 | sres_gen_id(res, query); |
1750 | assert(query->q_id)((void) sizeof ((query->q_id) ? 1 : 0), __extension__ ({ if (query->q_id) ; else __assert_fail ("query->q_id", "sres.c" , 1750, __extension__ __PRETTY_FUNCTION__); })); |
1751 | |
1752 | query->q_i_server = res->res_i_server; |
1753 | query->q_n_servers = res->res_n_servers; |
1754 | |
1755 | sres_qtable_append(res->res_queries, query); |
1756 | |
1757 | if (res->res_schedulecb && res->res_queries->qt_used == 1) |
1758 | res->res_schedulecb(res->res_async, 2 * SRES_RETRANSMIT_INTERVAL(SRES_RETRANSMIT_INTERVAL)); |
1759 | } |
1760 | |
1761 | return query; |
1762 | } |
1763 | |
1764 | su_inlinestatic inline |
1765 | void |
1766 | sres_remove_query(sres_resolver_t *res, sres_query_t *q, int all) |
1767 | { |
1768 | int i; |
1769 | |
1770 | if (q->q_hash) { |
1771 | sres_qtable_remove(res->res_queries, q), q->q_hash = 0; |
1772 | |
1773 | if (all) |
1774 | for (i = 0; i <= SRES_MAX_SEARCH(SRES_MAX_SEARCH); i++) { |
1775 | if (q->q_subqueries[i] && q->q_subqueries[i]->q_hash) { |
1776 | sres_qtable_remove(res->res_queries, q->q_subqueries[i]); |
1777 | q->q_subqueries[i]->q_hash = 0; |
1778 | } |
1779 | } |
1780 | } |
1781 | } |
1782 | |
1783 | /** Remove a query from hash table and free it. */ |
1784 | static |
1785 | void sres_free_query(sres_resolver_t *res, sres_query_t *q) |
1786 | { |
1787 | int i; |
1788 | |
1789 | if (q == NULL((void*)0)) |
1790 | return; |
1791 | |
1792 | if (q->q_hash) |
1793 | sres_qtable_remove(res->res_queries, q), q->q_hash = 0; |
1794 | |
1795 | for (i = 0; i <= SRES_MAX_SEARCH(SRES_MAX_SEARCH); i++) { |
1796 | sres_query_t *sq; |
1797 | |
1798 | sq = q->q_subqueries[i]; |
1799 | q->q_subqueries[i] = NULL((void*)0); |
1800 | if (sq) |
1801 | sres_free_query(res, sq); |
1802 | if (q->q_subanswers[i]) |
1803 | sres_cache_free_answers(res->res_cache, q->q_subanswers[i]); |
1804 | q->q_subanswers[i] = NULL((void*)0); |
1805 | } |
1806 | |
1807 | su_free(res->res_home, q); |
1808 | } |
1809 | |
1810 | static |
1811 | sres_record_t ** |
1812 | sres_combine_results(sres_resolver_t *res, |
1813 | sres_record_t **search_results[SRES_MAX_SEARCH(SRES_MAX_SEARCH) + 1]) |
1814 | { |
1815 | sres_record_t **combined_result; |
1816 | int i, j, found; |
1817 | |
1818 | /* Combine the results into a single list. */ |
1819 | for (i = 0, found = 0; i <= SRES_MAX_SEARCH(SRES_MAX_SEARCH); i++) |
1820 | if (search_results[i]) |
1821 | for (j = 0; search_results[i][j]; j++) |
1822 | found++; |
1823 | |
1824 | combined_result = su_alloc((su_home_t *)res->res_cache, |
1825 | (found + 1) * (sizeof combined_result[0])); |
1826 | if (combined_result) { |
1827 | for (i = 0, found = 0; i <= SRES_MAX_SEARCH(SRES_MAX_SEARCH); i++) |
1828 | if (search_results[i]) |
1829 | for (j = 0; search_results[i][j]; j++) { |
1830 | combined_result[found++] = search_results[i][j]; |
1831 | search_results[i][j] = NULL((void*)0); |
1832 | } |
1833 | |
1834 | combined_result[found] = NULL((void*)0); |
1835 | sres_sort_answers(res, combined_result); |
1836 | } |
1837 | |
1838 | for (i = 0; i <= SRES_MAX_SEARCH(SRES_MAX_SEARCH); i++) |
1839 | if (search_results[i]) |
1840 | sres_free_answers(res, search_results[i]), search_results[i] = NULL((void*)0); |
1841 | |
1842 | return combined_result; |
1843 | } |
1844 | |
1845 | static |
1846 | int |
1847 | sres_sockaddr2string(sres_resolver_t *res, |
1848 | char name[], |
1849 | size_t namelen, |
1850 | struct sockaddr const *addr) |
1851 | { |
1852 | name[0] = '\0'; |
1853 | |
1854 | if (addr->sa_family == AF_INET2) { |
1855 | struct sockaddr_in const *sin = (struct sockaddr_in *)addr; |
1856 | uint8_t const *in_addr = (uint8_t*)&sin->sin_addr; |
1857 | return snprintf(name, namelen, "%u.%u.%u.%u.in-addr.arpa.", |
1858 | in_addr[3], in_addr[2], in_addr[1], in_addr[0]); |
1859 | } |
1860 | #if HAVE_SIN61 |
1861 | else if (addr->sa_family == AF_INET610) { |
1862 | struct sockaddr_in6 const *sin6 = (struct sockaddr_in6 *)addr; |
1863 | size_t addrsize = sizeof(sin6->sin6_addr.s6_addr__in6_u.__u6_addr8); |
1864 | char *postfix; |
1865 | size_t required; |
1866 | size_t i; |
1867 | |
1868 | if (res->res_config->c_opt.ip6int) |
1869 | postfix = "ip6.int."; |
1870 | else |
1871 | postfix = "ip6.arpa."; |
1872 | |
1873 | required = addrsize * 4 + strlen(postfix); |
1874 | |
1875 | if (namelen <= required) |
1876 | return (int)required; |
1877 | |
1878 | for (i = 0; i < addrsize; i++) { |
1879 | uint8_t byte = sin6->sin6_addr.s6_addr__in6_u.__u6_addr8[addrsize - i - 1]; |
1880 | uint8_t hex; |
1881 | |
1882 | hex = byte & 0xf; |
1883 | name[4 * i] = hex > 9 ? hex + 'a' - 10 : hex + '0'; |
1884 | name[4 * i + 1] = '.'; |
1885 | hex = (byte >> 4) & 0xf; |
1886 | name[4 * i + 2] = hex > 9 ? hex + 'a' - 10 : hex + '0'; |
1887 | name[4 * i + 3] = '.'; |
1888 | } |
1889 | |
1890 | strcpy(name + 4 * i, postfix); |
1891 | |
1892 | return (int)required; |
1893 | } |
1894 | #endif /* HAVE_SIN6 */ |
1895 | else { |
1896 | su_seterrno(EAFNOSUPPORT97); |
1897 | SU_DEBUG_3(("%s: %s\n", "sres_sockaddr2string",(((sresolv_log != ((void*)0) && sresolv_log->log_init ) == 0 ? 9 : ((sresolv_log != ((void*)0) && sresolv_log ->log_init > 1) ? sresolv_log->log_level : su_log_default ->log_level)) >= 3 ? (_su_llog(sresolv_log, 3, "sres.c" , (const char *)__func__, 1898, "%s: %s\n", "sres_sockaddr2string" , su_strerror(97))) : (void)0) |
1898 | su_strerror(EAFNOSUPPORT)))(((sresolv_log != ((void*)0) && sresolv_log->log_init ) == 0 ? 9 : ((sresolv_log != ((void*)0) && sresolv_log ->log_init > 1) ? sresolv_log->log_level : su_log_default ->log_level)) >= 3 ? (_su_llog(sresolv_log, 3, "sres.c" , (const char *)__func__, 1898, "%s: %s\n", "sres_sockaddr2string" , su_strerror(97))) : (void)0); |
1899 | return 0; |
1900 | } |
1901 | } |
1902 | |
1903 | /** Make a domain name a top level domain name. |
1904 | * |
1905 | * The function sres_toplevel() returns a copies string @a domain and |
1906 | * terminates it with a dot if it is not already terminated. |
1907 | */ |
1908 | static |
1909 | char const * |
1910 | sres_toplevel(char buf[], size_t blen, char const *domain) |
1911 | { |
1912 | size_t len; |
1913 | int already; |
1914 | |
1915 | if (!domain) |
1916 | return su_seterrno(EFAULT14), (void *)NULL((void*)0); |
1917 | |
1918 | len = strlen(domain); |
1919 | |
1920 | if (len >= blen) |
1921 | return su_seterrno(ENAMETOOLONG36), (void *)NULL((void*)0); |
1922 | |
1923 | already = len > 0 && domain[len - 1] == '.'; |
1924 | |
1925 | if (already) |
1926 | return domain; |
1927 | |
1928 | if (len + 1 >= blen) |
1929 | return su_seterrno(ENAMETOOLONG36), (void *)NULL((void*)0); |
1930 | |
1931 | strcpy(buf, domain); |
1932 | buf[len] = '.'; buf[len + 1] = '\0'; |
1933 | |
1934 | return buf; |
1935 | } |
1936 | |
1937 | /* ---------------------------------------------------------------------- */ |
1938 | |
1939 | static int sres_update_config(sres_resolver_t *res, int always, time_t now); |
1940 | static int sres_parse_config(sres_config_t *, FILE *); |
1941 | static int sres_parse_options(sres_config_t *c, char const *value); |
1942 | static int sres_parse_nameserver(sres_config_t *c, char const *server); |
1943 | static time_t sres_config_timestamp(sres_config_t const *c); |
1944 | |
1945 | /** Update configuration |
1946 | * |
1947 | * @retval 0 when successful |
1948 | * @retval -1 upon an error |
1949 | */ |
1950 | int sres_resolver_update(sres_resolver_t *res, int always) |
1951 | { |
1952 | sres_server_t **servers, **old_servers; |
1953 | int updated; |
1954 | |
1955 | updated = sres_update_config(res, always, time(&res->res_now)); |
1956 | if (updated < 0) |
1957 | return -1; |
1958 | |
1959 | if (!res->res_servers || always || updated) { |
1960 | servers = sres_servers_new(res, res->res_config); |
1961 | old_servers = res->res_servers; |
1962 | |
1963 | res->res_i_server = 0; |
1964 | res->res_n_servers = sres_servers_count(servers); |
1965 | res->res_servers = servers; |
1966 | |
1967 | sres_servers_close(res, old_servers); |
1968 | su_free(res->res_home, old_servers); |
1969 | |
1970 | if (!servers) |
1971 | return -1; |
1972 | } |
1973 | |
1974 | return 0; |
1975 | } |
1976 | |
1977 | /** Update config file. |
1978 | * |
1979 | * @retval 1 if DNS server list is different from old one. |
1980 | * @retval 0 when otherwise successful |
1981 | * @retval -1 upon an error |
1982 | */ |
1983 | static |
1984 | int sres_update_config(sres_resolver_t *res, int always, time_t now) |
1985 | { |
1986 | sres_config_t *c = NULL((void*)0); |
1987 | sres_config_t const *previous; |
1988 | int retval; |
1989 | |
1990 | previous = res->res_config; |
1991 | |
1992 | if (!always && previous && now < res->res_checked) |
1993 | return 0; |
1994 | /* Try avoid checking for changes too often. */ |
1995 | res->res_checked = now + SRES_UPDATE_INTERVAL_SECS5; |
1996 | |
1997 | if (!always && previous && |
1998 | sres_config_timestamp(previous) == previous->c_modified) |
1999 | return 0; |
2000 | |
2001 | c = sres_parse_resolv_conf(res, res->res_options); |
2002 | if (!c) |
2003 | return -1; |
2004 | |
2005 | res->res_config = c; |
2006 | |
2007 | retval = sres_config_changed_servers(c, previous); |
2008 | |
2009 | su_home_unref((su_home_t *)previous->c_home); |
2010 | |
2011 | return retval; |
2012 | } |
2013 | |
2014 | #if HAVE_WIN32 |
2015 | |
2016 | /** Number of octets to read from a registry key at a time */ |
2017 | #define QUERY_DATALEN 1024 |
2018 | #define MAX_DATALEN 65535 |
2019 | |
2020 | /** |
2021 | * Uses IP Helper IP to get DNS servers list. |
2022 | */ |
2023 | static int sres_parse_win32_ip(sres_config_t *c) |
2024 | { |
2025 | int ret = -1; |
2026 | |
2027 | #if HAVE_IPHLPAPI_H |
2028 | DWORD dw; |
2029 | su_home_t *home = c->c_home; |
2030 | ULONG size = sizeof(FIXED_INFO); |
2031 | |
2032 | do { |
2033 | FIXED_INFO *info = (FIXED_INFO *)su_alloc(home, size); |
2034 | dw = GetNetworkParams(info, &size); |
2035 | if (dw == ERROR_SUCCESS) { |
2036 | IP_ADDR_STRING* addr = &info->DnsServerList; |
2037 | for (; addr; addr = addr->Next) { |
2038 | SU_DEBUG_3(("Adding nameserver: %s\n", addr->IpAddress.String))(((sresolv_log != ((void*)0) && sresolv_log->log_init ) == 0 ? 9 : ((sresolv_log != ((void*)0) && sresolv_log ->log_init > 1) ? sresolv_log->log_level : su_log_default ->log_level)) >= 3 ? (_su_llog(sresolv_log, 3, "sres.c" , (const char *)__func__, 2038, "Adding nameserver: %s\n", addr ->IpAddress.String)) : (void)0); |
2039 | sres_parse_nameserver(c, addr->IpAddress.String); |
2040 | } |
2041 | ret = 0; |
2042 | } |
2043 | su_free(home, info); |
2044 | } while (dw == ERROR_BUFFER_OVERFLOW); |
2045 | #endif |
2046 | |
2047 | return ret; |
2048 | } |
2049 | |
2050 | /** |
2051 | * Parses name servers listed in registry key 'key+lpValueName'. The |
2052 | * key is expected to contain a whitespace separate list of |
2053 | * name server IP addresses. |
2054 | * |
2055 | * @return number of server addresses added |
2056 | */ |
2057 | static int sres_parse_win32_reg_parse_dnsserver(sres_config_t *c, HKEY key, LPCTSTR lpValueName) |
2058 | { |
2059 | su_home_t *home = c->c_home; |
2060 | su_strlst_t *reg_dns_list; |
2061 | BYTE *name_servers = su_alloc(home, QUERY_DATALEN); |
2062 | DWORD name_servers_length = QUERY_DATALEN; |
2063 | int ret, servers_added = 0; |
2064 | |
2065 | /* get name servers and ... */ |
2066 | while((ret = RegQueryValueEx(key, |
2067 | lpValueName, |
2068 | NULL((void*)0), NULL((void*)0), |
2069 | name_servers, |
2070 | &name_servers_length)) == ERROR_MORE_DATA) { |
2071 | name_servers_length += QUERY_DATALEN; |
2072 | |
2073 | /* sanity check, upper limit for memallocs */ |
2074 | if (name_servers_length > MAX_DATALEN) break; |
2075 | |
2076 | name_servers = su_realloc(home, name_servers, name_servers_length); |
2077 | if (name_servers == NULL((void*)0)) { |
2078 | ret = ERROR_BUFFER_OVERFLOW; |
2079 | break; |
2080 | } |
2081 | } |
2082 | |
2083 | /* if reading the key was succesful, continue */ |
2084 | if (ret == ERROR_SUCCESS) { |
2085 | if (name_servers[0]){ |
2086 | int i; |
2087 | |
2088 | /* add to list */ |
2089 | reg_dns_list = su_strlst_split(home, (char *)name_servers, " "); |
2090 | |
2091 | for(i = 0 ; i < su_strlst_len(reg_dns_list); i++) { |
2092 | const char *item = su_strlst_item(reg_dns_list, i); |
2093 | SU_DEBUG_3(("Adding nameserver: %s (key=%s)\n", item, (char*)lpValueName))(((sresolv_log != ((void*)0) && sresolv_log->log_init ) == 0 ? 9 : ((sresolv_log != ((void*)0) && sresolv_log ->log_init > 1) ? sresolv_log->log_level : su_log_default ->log_level)) >= 3 ? (_su_llog(sresolv_log, 3, "sres.c" , (const char *)__func__, 2093, "Adding nameserver: %s (key=%s)\n" , item, (char*)lpValueName)) : (void)0); |
2094 | sres_parse_nameserver(c, item); |
2095 | ++servers_added; |
2096 | } |
2097 | |
2098 | su_strlst_destroy(reg_dns_list); |
2099 | |
2100 | } |
2101 | } |
2102 | |
2103 | su_free(home, name_servers); |
2104 | |
2105 | return servers_added; |
2106 | } |
2107 | |
2108 | /** |
2109 | * Discover system nameservers from Windows registry. |
2110 | * |
2111 | * Refs: |
2112 | * - http://msdn.microsoft.com/library/default.asp?url=/library/en-us/sysinfo/base/regqueryvalueex.asp |
2113 | * - http://support.microsoft.com/default.aspx?scid=kb;en-us;120642 |
2114 | * - http://support.microsoft.com/kb/314053/EN-US/ |
2115 | * - IP Helper API (possibly better way than current registry-based impl.) |
2116 | * http://msdn.microsoft.com/library/default.asp?url=/library/en-us/iphlp/iphlp/ip_helper_start_page.asp |
2117 | */ |
2118 | static int sres_parse_win32_reg(sres_config_t *c) |
2119 | { |
2120 | int ret = -1; |
2121 | |
2122 | #define MAX_KEY_LEN 255 |
2123 | #define MAX_VALUE_NAME_LEN 16383 |
2124 | |
2125 | su_home_t *home = c->c_home; |
2126 | HKEY key_handle; |
2127 | #if 0 |
2128 | HKEY interface_key_handle; |
2129 | FILETIME ftime; |
2130 | int index, i; |
2131 | #endif |
2132 | int found = 0; |
2133 | char *interface_guid = su_alloc(home, MAX_VALUE_NAME_LEN); |
2134 | |
2135 | #if 0 |
2136 | #if __MINGW32__ |
2137 | DWORD guid_size = QUERY_DATALEN; |
2138 | #else |
2139 | int guid_size = MAX_VALUE_NAME_LEN; |
2140 | #endif |
2141 | |
2142 | /* step: find interface specific nameservers |
2143 | * - this is currently disabled 2006/Jun (the current check might insert |
2144 | * multiple unnecessary nameservers to the search list) |
2145 | */ |
2146 | /* open the 'Interfaces' registry Key */ |
2147 | if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, |
2148 | "SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters\\Interfaces", |
2149 | 0, KEY_READ, &key_handle)) { |
2150 | SU_DEBUG_2(("RegOpenKeyEx failed\n"))(((sresolv_log != ((void*)0) && sresolv_log->log_init ) == 0 ? 9 : ((sresolv_log != ((void*)0) && sresolv_log ->log_init > 1) ? sresolv_log->log_level : su_log_default ->log_level)) >= 2 ? (_su_llog(sresolv_log, 2, "sres.c" , (const char *)__func__, 2150, "RegOpenKeyEx failed\n", )) : (void)0); |
2151 | } else { |
2152 | index = 0; |
2153 | /* for each interface listed ... */ |
2154 | while (RegEnumKeyEx(key_handle, index, |
2155 | interface_guid, &guid_size, |
2156 | NULL((void*)0),NULL((void*)0),0,&ftime) == ERROR_SUCCESS){ |
2157 | if (RegOpenKeyEx(key_handle, interface_guid, |
2158 | 0, KEY_READ, |
2159 | &interface_key_handle) == ERROR_SUCCESS) { |
2160 | |
2161 | /* note: 'NameServer' is preferred over 'DhcpNameServer' */ |
2162 | found += sres_parse_win32_reg_parse_dnsserver(c, interface_key_handle, "NameServer"); |
2163 | if (found == 0) |
2164 | found += sres_parse_win32_reg_parse_dnsserver(c, interface_key_handle, "DhcpNameServer"); |
2165 | |
2166 | RegCloseKey(interface_key_handle); |
2167 | } else{ |
2168 | SU_DEBUG_2(("interface RegOpenKeyEx failed\n"))(((sresolv_log != ((void*)0) && sresolv_log->log_init ) == 0 ? 9 : ((sresolv_log != ((void*)0) && sresolv_log ->log_init > 1) ? sresolv_log->log_level : su_log_default ->log_level)) >= 2 ? (_su_llog(sresolv_log, 2, "sres.c" , (const char *)__func__, 2168, "interface RegOpenKeyEx failed\n" , )) : (void)0); |
2169 | } |
2170 | index++; |
2171 | guid_size = 64; |
2172 | } |
2173 | RegCloseKey(key_handle); |
2174 | } |
2175 | #endif /* #if 0: interface-specific nameservers */ |
2176 | |
2177 | /* step: if no interface-specific nameservers are found, |
2178 | * check for system-wide nameservers */ |
2179 | if (found == 0) { |
2180 | if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, |
2181 | "SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters", |
2182 | 0, KEY_READ, &key_handle)) { |
2183 | SU_DEBUG_2(("RegOpenKeyEx failed (2)\n"))(((sresolv_log != ((void*)0) && sresolv_log->log_init ) == 0 ? 9 : ((sresolv_log != ((void*)0) && sresolv_log ->log_init > 1) ? sresolv_log->log_level : su_log_default ->log_level)) >= 2 ? (_su_llog(sresolv_log, 2, "sres.c" , (const char *)__func__, 2183, "RegOpenKeyEx failed (2)\n", ) ) : (void)0); |
2184 | } else { |
2185 | found += sres_parse_win32_reg_parse_dnsserver(c, key_handle, "NameServer"); |
2186 | if (found == 0) |
2187 | found += sres_parse_win32_reg_parse_dnsserver(c, key_handle, "DhcpNameServer"); |
2188 | RegCloseKey(key_handle); |
2189 | } |
2190 | } |
2191 | |
2192 | SU_DEBUG_3(("Total of %d name servers found from win32 registry.\n", found))(((sresolv_log != ((void*)0) && sresolv_log->log_init ) == 0 ? 9 : ((sresolv_log != ((void*)0) && sresolv_log ->log_init > 1) ? sresolv_log->log_level : su_log_default ->log_level)) >= 3 ? (_su_llog(sresolv_log, 3, "sres.c" , (const char *)__func__, 2192, "Total of %d name servers found from win32 registry.\n" , found)) : (void)0); |
2193 | |
2194 | /* return success if servers found */ |
2195 | if (found) ret = 0; |
2196 | |
2197 | su_free(home, interface_guid); |
2198 | |
2199 | return ret; |
2200 | } |
2201 | |
2202 | #endif /* HAVE_WIN32 */ |
2203 | |
2204 | /** Parse /etc/resolv.conf file. |
2205 | * |
2206 | * @retval #sres_config_t structure when successful |
2207 | * @retval NULL upon an error |
2208 | * |
2209 | * @todo The resolv.conf directives @b sortlist and most of the options |
2210 | * are currently ignored. |
2211 | */ |
2212 | static |
2213 | sres_config_t *sres_parse_resolv_conf(sres_resolver_t *res, |
2214 | char const **options) |
2215 | { |
2216 | sres_config_t *c = su_home_new(sizeof *c); |
2217 | |
2218 | if (c) { |
2219 | FILE *f; |
2220 | int i; |
2221 | |
2222 | f = fopen(c->c_filename = res->res_cnffile, "r"); |
2223 | |
2224 | sres_parse_config(c, f); |
2225 | |
2226 | if (f) |
2227 | fclose(f); |
2228 | |
2229 | #if HAVE_WIN32 |
2230 | /* note: no 127.0.0.1 on win32 systems */ |
2231 | /* on win32, query the registry for nameservers */ |
2232 | if (sres_parse_win32_ip(c) == 0 || sres_parse_win32_reg(c) == 0) |
2233 | /* success */; |
2234 | else |
2235 | /* now what? */; |
2236 | #else |
2237 | /* Use local nameserver by default */ |
2238 | if (c->c_nameservers[0] == NULL((void*)0)) |
2239 | sres_parse_nameserver(c, "127.0.0.1"); |
2240 | #endif |
2241 | |
2242 | for (i = 0; c->c_nameservers[i] && i < SRES_MAX_NAMESERVERS(SRES_MAX_NAMESERVERS); i++) { |
2243 | struct sockaddr_in *sin = (void *)c->c_nameservers[i]->ns_addr; |
2244 | sin->sin_port = htons(c->c_port); |
2245 | } |
2246 | |
2247 | sres_parse_options(c, getenv("RES_OPTIONS")); |
2248 | |
2249 | if (options) |
2250 | for (i = 0; options[i]; i++) |
2251 | sres_parse_options(c, options[i]); |
2252 | |
2253 | sres_parse_options(c, getenv("SRES_OPTIONS")); |
2254 | |
2255 | su_home_threadsafe(c->c_home); |
2256 | } |
2257 | |
2258 | return c; |
2259 | } |
2260 | |
2261 | uint16_t _sres_default_port = 53; |
2262 | |
2263 | /** Parse config file. |
2264 | * |
2265 | * @return Number of search domains, if successful. |
2266 | * @retval -1 upon an error (never happens). |
2267 | */ |
2268 | static |
2269 | int sres_parse_config(sres_config_t *c, FILE *f) |
2270 | { |
2271 | su_home_t *home = c->c_home; |
2272 | int line; |
2273 | char const *localdomain; |
2274 | char *search = NULL((void*)0), *domain = NULL((void*)0); |
2275 | char buf[1025]; |
2276 | int i = 0; |
2277 | |
2278 | localdomain = getenv("LOCALDOMAIN"); |
2279 | |
2280 | /* Default values */ |
2281 | c->c_opt.ndots = 1; |
2282 | c->c_opt.check_names = 1; |
2283 | c->c_opt.timeout = SRES_RETRY_INTERVAL(SRES_RETRY_INTERVAL); |
2284 | c->c_opt.attempts = SRES_MAX_RETRY_COUNT(SRES_MAX_RETRY_COUNT); |
2285 | c->c_port = _sres_default_port; |
2286 | |
2287 | if (f != NULL((void*)0)) { |
2288 | for (line = 1; fgets(buf, sizeof(buf), f); line++) { |
2289 | size_t len; |
2290 | char *value, *b; |
2291 | |
2292 | /* Skip whitespace at the beginning ...*/ |
2293 | b = buf + strspn(buf, " \t"); |
2294 | |
2295 | /* ... and comments + whitespace at the end */ |
2296 | for (len = strcspn(b, "#;"); len > 0 && strchr(" \t\r\n", b[len - 1]); len--) |
2297 | ; |
2298 | |
2299 | if (len == 0) /* Empty line or comment */ |
2300 | continue; |
2301 | |
2302 | b[len] = '\0'; |
2303 | |
2304 | len = strcspn(b, " \t"); |
2305 | value = b + len; value += strspn(value, " \t"); |
2306 | |
2307 | #define MATCH(token)(len == strlen(token) && su_casenmatch(token, b, len) ) (len == strlen(token) && su_casenmatch(token, b, len)) |
2308 | |
2309 | if (MATCH("nameserver")(len == strlen("nameserver") && su_casenmatch("nameserver" , b, len))) { |
2310 | if (sres_parse_nameserver(c, value) < 0) |
2311 | return -1; |
2312 | } |
2313 | else if (MATCH("domain")(len == strlen("domain") && su_casenmatch("domain", b , len))) { |
2314 | if (localdomain) /* LOCALDOMAIN overrides */ |
2315 | continue; |
2316 | if (search) |
2317 | su_free(home, search), search = NULL((void*)0); |
2318 | if (domain) |
2319 | su_free(home, domain), domain = NULL((void*)0); |
2320 | domain = su_strdup(home, value); |
2321 | if (!domain) |
2322 | return -1; |
2323 | } |
2324 | else if (MATCH("search")(len == strlen("search") && su_casenmatch("search", b , len))) { |
2325 | if (localdomain) /* LOCALDOMAIN overrides */ |
2326 | continue; |
2327 | if (search) su_free(home, search), search = NULL((void*)0); |
2328 | if (domain) su_free(home, domain), domain = NULL((void*)0); |
2329 | search = su_strdup(home, value); |
2330 | if (!search) |
2331 | return -1; |
2332 | } |
2333 | else if (MATCH("port")(len == strlen("port") && su_casenmatch("port", b, len ))) { |
2334 | unsigned long port = strtoul(value, NULL((void*)0), 10); |
2335 | if (port < 65536) |
2336 | c->c_port = port; |
2337 | } |
2338 | else if (MATCH("options")(len == strlen("options") && su_casenmatch("options", b, len))) { |
2339 | sres_parse_options(c, value); |
2340 | } |
2341 | } |
2342 | } |
2343 | |
2344 | if (f) |
2345 | c->c_modified = sres_config_timestamp(c); |
2346 | |
2347 | if (localdomain) |
2348 | c->c_search[0] = localdomain; |
2349 | else if (domain) |
2350 | c->c_search[0] = domain; |
2351 | else if (search) { |
2352 | for (i = 0; search[0] && i < SRES_MAX_SEARCH(SRES_MAX_SEARCH); i++) { |
2353 | c->c_search[i] = search; |
2354 | search += strcspn(search, " \t"); |
2355 | if (*search) { |
2356 | *search++ = '\0'; |
2357 | search += strspn(search, " \t"); |
2358 | } |
2359 | } |
2360 | } |
2361 | |
2362 | return i; |
2363 | } |
2364 | |
2365 | #if DOXYGEN_ONLY |
2366 | /**@ingroup sresolv_env |
2367 | * |
2368 | * Environment variable containing options for Sofia resolver. The options |
2369 | * recognized by Sofia resolver are as follows: |
2370 | * - @b debug turn on debugging (no effect) |
2371 | * - @b ndots:<i>n</i> when searching, try first to query name as absolute |
2372 | * domain if it contains at least <i>n</i> dots |
2373 | * - @b timeout:<i>secs</i> timeout in seconds |
2374 | * - @b attempts:<i>n</i> fail after <i>n</i> retries |
2375 | * - @b rotate use round robin selection of nameservers |
2376 | * - @b no-check-names do not check names for invalid characters |
2377 | * - @b inet6 (no effect) |
2378 | * - @b ip6-dotint IPv6 addresses are resolved using suffix ".ip6.int" |
2379 | * instead of the standard ".ip6.arpa" suffix |
2380 | * - @b ip6-bytestring (no effect) |
2381 | * The following option is a Sofia-specific extension: |
2382 | * - @b no-edns0 do not try to use EDNS0 extension (@RFC2671) |
2383 | * |
2384 | * The same options can be listed in @b options directive in resolv.conf, or |
2385 | * in #RES_OPTIONS environment variable. Note that options given in |
2386 | * #SRES_OPTIONS override those specified in #RES_OPTIONS which in turn |
2387 | * override options specified in the @b options directive of resolve.conf. |
2388 | * |
2389 | * The meaning of an option can be reversed with prefix "no-". |
2390 | * |
2391 | * @sa Manual page for resolv.conf, #RES_OPTIONS. |
2392 | */ |
2393 | extern SRES_OPTIONS; |
2394 | |
2395 | /**@ingroup sresolv_env |
2396 | * |
2397 | * Environment variable containing resolver options. This environment |
2398 | * variable is also used by standard BIND resolver. |
2399 | * |
2400 | * @sa Manual page for resolv.conf, #SRES_OPTIONS. |
2401 | */ |
2402 | extern RES_OPTIONS; |
2403 | |
2404 | /**@ingroup sresolv_env |
2405 | * |
2406 | * Environment variable containing search domain. This environment |
2407 | * variable is also used by standard BIND resolver. |
2408 | * |
2409 | * @sa Manual page for resolv.conf, #RES_OPTIONS, #SRES_OPTIONS. |
2410 | */ |
2411 | extern LOCALDOMAIN; |
2412 | #endif |
2413 | |
2414 | /* Parse options line or #SRES_OPTIONS or #RES_OPTIONS environment variable. */ |
2415 | static int |
2416 | sres_parse_options(sres_config_t *c, char const *value) |
2417 | { |
2418 | if (!value) |
2419 | return -1; |
2420 | |
2421 | while (value[0]) { |
2422 | char const *b; |
2423 | size_t len, extra = 0; |
2424 | unsigned long n = 0; |
2425 | |
2426 | b = value; len = strcspn(value, " \t:"); |
2427 | value += len; |
2428 | |
2429 | if (value[0] == ':') { |
2430 | len++; |
2431 | n = strtoul(++value, NULL((void*)0), 10); |
2432 | value += extra = strcspn(value, " \t"); |
2433 | } |
2434 | |
2435 | if (*value) |
2436 | value += strspn(value, " \t"); |
2437 | |
2438 | if (n > 65536) { |
2439 | SU_DEBUG_3(("sres: %s: invalid %*.0s\n", c->c_filename,(((sresolv_log != ((void*)0) && sresolv_log->log_init ) == 0 ? 9 : ((sresolv_log != ((void*)0) && sresolv_log ->log_init > 1) ? sresolv_log->log_level : su_log_default ->log_level)) >= 3 ? (_su_llog(sresolv_log, 3, "sres.c" , (const char *)__func__, 2440, "sres: %s: invalid %*.0s\n", c ->c_filename, (int)(len + extra), b)) : (void)0) |
2440 | (int)(len + extra), b))(((sresolv_log != ((void*)0) && sresolv_log->log_init ) == 0 ? 9 : ((sresolv_log != ((void*)0) && sresolv_log ->log_init > 1) ? sresolv_log->log_level : su_log_default ->log_level)) >= 3 ? (_su_llog(sresolv_log, 3, "sres.c" , (const char *)__func__, 2440, "sres: %s: invalid %*.0s\n", c ->c_filename, (int)(len + extra), b)) : (void)0); |
2441 | continue; |
2442 | } |
2443 | |
2444 | /* Documented by BIND9 resolv.conf */ |
2445 | if (MATCH("no-debug")(len == strlen("no-debug") && su_casenmatch("no-debug" , b, len))) c->c_opt.debug = 0; |
2446 | else if (MATCH("debug")(len == strlen("debug") && su_casenmatch("debug", b, len ))) c->c_opt.debug = 1; |
2447 | else if (MATCH("ndots:")(len == strlen("ndots:") && su_casenmatch("ndots:", b , len))) c->c_opt.ndots = n; |
2448 | else if (MATCH("timeout:")(len == strlen("timeout:") && su_casenmatch("timeout:" , b, len))) c->c_opt.timeout = n; |
2449 | else if (MATCH("attempts:")(len == strlen("attempts:") && su_casenmatch("attempts:" , b, len))) c->c_opt.attempts = n; |
2450 | else if (MATCH("no-rotate")(len == strlen("no-rotate") && su_casenmatch("no-rotate" , b, len))) c->c_opt.rotate = 0; |
2451 | else if (MATCH("rotate")(len == strlen("rotate") && su_casenmatch("rotate", b , len))) c->c_opt.rotate = 1; |
2452 | else if (MATCH("no-check-names")(len == strlen("no-check-names") && su_casenmatch("no-check-names" , b, len))) c->c_opt.check_names = 0; |
2453 | else if (MATCH("check-names")(len == strlen("check-names") && su_casenmatch("check-names" , b, len))) c->c_opt.check_names = 1; |
2454 | else if (MATCH("no-inet6")(len == strlen("no-inet6") && su_casenmatch("no-inet6" , b, len))) c->c_opt.ip6int = 0; |
2455 | else if (MATCH("inet6")(len == strlen("inet6") && su_casenmatch("inet6", b, len ))) c->c_opt.inet6 = 1; |
2456 | else if (MATCH("no-ip6-dotint")(len == strlen("no-ip6-dotint") && su_casenmatch("no-ip6-dotint" , b, len))) c->c_opt.ip6int = 0; |
2457 | else if (MATCH("ip6-dotint")(len == strlen("ip6-dotint") && su_casenmatch("ip6-dotint" , b, len))) c->c_opt.ip6int = 1; |
2458 | else if (MATCH("no-ip6-bytestring")(len == strlen("no-ip6-bytestring") && su_casenmatch( "no-ip6-bytestring", b, len))) c->c_opt.ip6bytestring = 0; |
2459 | else if (MATCH("ip6-bytestring")(len == strlen("ip6-bytestring") && su_casenmatch("ip6-bytestring" , b, len))) c->c_opt.ip6bytestring = 1; |
2460 | /* Sofia-specific extensions: */ |
2461 | else if (MATCH("no-edns0")(len == strlen("no-edns0") && su_casenmatch("no-edns0" , b, len))) c->c_opt.edns = edns_not_supported; |
2462 | else if (MATCH("edns0")(len == strlen("edns0") && su_casenmatch("edns0", b, len ))) c->c_opt.edns = edns0_configured; |
2463 | else { |
2464 | SU_DEBUG_3(("sres: %s: unknown option %*.0s\n",(((sresolv_log != ((void*)0) && sresolv_log->log_init ) == 0 ? 9 : ((sresolv_log != ((void*)0) && sresolv_log ->log_init > 1) ? sresolv_log->log_level : su_log_default ->log_level)) >= 3 ? (_su_llog(sresolv_log, 3, "sres.c" , (const char *)__func__, 2465, "sres: %s: unknown option %*.0s\n" , c->c_filename, (int)(len + extra), b)) : (void)0) |
2465 | c->c_filename, (int)(len + extra), b))(((sresolv_log != ((void*)0) && sresolv_log->log_init ) == 0 ? 9 : ((sresolv_log != ((void*)0) && sresolv_log ->log_init > 1) ? sresolv_log->log_level : su_log_default ->log_level)) >= 3 ? (_su_llog(sresolv_log, 3, "sres.c" , (const char *)__func__, 2465, "sres: %s: unknown option %*.0s\n" , c->c_filename, (int)(len + extra), b)) : (void)0); |
2466 | } |
2467 | } |
2468 | |
2469 | return 0; |
2470 | } |
2471 | |
2472 | static |
2473 | int sres_parse_nameserver(sres_config_t *c, char const *server) |
2474 | { |
2475 | sres_nameserver_t *ns; |
2476 | struct sockaddr *sa; |
2477 | int err, i; |
2478 | |
2479 | for (i = 0; i < SRES_MAX_NAMESERVERS(SRES_MAX_NAMESERVERS); i++) |
2480 | if (c->c_nameservers[i] == NULL((void*)0)) |
2481 | break; |
2482 | |
2483 | if (i >= SRES_MAX_NAMESERVERS(SRES_MAX_NAMESERVERS)) |
2484 | return 0 /* Silently discard extra nameservers */; |
2485 | |
2486 | ns = su_zalloc(c->c_home, (sizeof *ns) + strlen(server) + 1); |
2487 | if (!ns) |
2488 | return -1; |
2489 | |
2490 | sa = (void *)ns->ns_addr; |
2491 | |
2492 | #if HAVE_SIN61 |
2493 | if (strchr(server, ':')) { |
2494 | struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sa; |
2495 | memset(sa, 0, ns->ns_addrlen = sizeof *sin6); |
2496 | err = su_inet_ptoninet_pton(sa->sa_family = AF_INET610, server, &sin6->sin6_addr); |
2497 | } |
2498 | else |
2499 | #endif |
2500 | { |
2501 | struct sockaddr_in *sin = (struct sockaddr_in *)sa; |
2502 | memset(sa, 0, ns->ns_addrlen = sizeof *sin); |
2503 | err = su_inet_ptoninet_pton(sa->sa_family = AF_INET2, server, &sin->sin_addr); |
2504 | } |
2505 | |
2506 | if (err <= 0) { |
2507 | SU_DEBUG_3(("sres: nameserver %s: invalid address\n", server))(((sresolv_log != ((void*)0) && sresolv_log->log_init ) == 0 ? 9 : ((sresolv_log != ((void*)0) && sresolv_log ->log_init > 1) ? sresolv_log->log_level : su_log_default ->log_level)) >= 3 ? (_su_llog(sresolv_log, 3, "sres.c" , (const char *)__func__, 2507, "sres: nameserver %s: invalid address\n" , server)) : (void)0); |
2508 | su_free(c->c_home, ns); |
2509 | return 0; |
2510 | } |
2511 | |
2512 | #if HAVE_SA_LEN |
2513 | sa->sa_len = ns->ns_addrlen; |
2514 | #endif |
2515 | |
2516 | c->c_nameservers[i] = ns; |
2517 | |
2518 | return 1; |
2519 | } |
2520 | |
2521 | /** Get current timestamp of resolv.conf file */ |
2522 | static |
2523 | time_t sres_config_timestamp(sres_config_t const *c) |
2524 | { |
2525 | #ifndef HAVE_WIN32 |
2526 | struct stat st; |
2527 | |
2528 | if (stat(c->c_filename, &st) == 0) |
2529 | return st.st_mtimest_mtim.tv_sec; |
2530 | |
2531 | /** @return If the resolv.conf file does not exists, return old timestamp. */ |
2532 | return c->c_modified; |
2533 | #else |
2534 | /** On WIN32, return always different timestamp */ |
2535 | return c->c_modified + SRES_UPDATE_INTERVAL_SECS5; |
2536 | #endif |
2537 | } |
2538 | |
2539 | |
2540 | /* ---------------------------------------------------------------------- */ |
2541 | |
2542 | /** Check if the new configuration has different servers than the old */ |
2543 | static |
2544 | int sres_config_changed_servers(sres_config_t const *new_c, |
2545 | sres_config_t const *old_c) |
2546 | { |
2547 | int i; |
2548 | sres_nameserver_t const *new_ns, *old_ns; |
2549 | |
2550 | if (old_c == NULL((void*)0)) |
2551 | return 1; |
2552 | |
2553 | for (i = 0; i < SRES_MAX_NAMESERVERS(SRES_MAX_NAMESERVERS); i++) { |
2554 | new_ns = new_c->c_nameservers[i]; |
2555 | old_ns = old_c->c_nameservers[i]; |
2556 | |
2557 | if (!new_ns != !old_ns) |
2558 | return 1; |
2559 | if (!new_ns) |
2560 | return 0; |
2561 | if (new_ns->ns_addrlen != old_ns->ns_addrlen) |
2562 | return 1; |
2563 | if (memcmp(new_ns->ns_addr, old_ns->ns_addr, new_ns->ns_addrlen)) |
2564 | return 1; |
2565 | } |
2566 | |
2567 | return 0; |
2568 | } |
2569 | |
2570 | /** Allocate new servers structure */ |
2571 | static |
2572 | sres_server_t **sres_servers_new(sres_resolver_t *res, |
2573 | sres_config_t const *c) |
2574 | { |
2575 | sres_server_t **servers, *dns; |
2576 | sres_nameserver_t *ns; |
2577 | int N, i; |
2578 | size_t size; |
2579 | |
2580 | for (N = 0; c->c_nameservers[N] && N < SRES_MAX_NAMESERVERS(SRES_MAX_NAMESERVERS); N++) |
2581 | ; |
2582 | |
2583 | size = (N + 1) * (sizeof *servers) + N * (sizeof **servers); |
2584 | |
2585 | servers = su_zalloc(res->res_home, size); if (!servers) return servers; |
2586 | dns = (void *)(servers + N + 1); |
2587 | for (i = 0; i < N; i++) { |
2588 | dns->dns_socket = INVALID_SOCKET((sres_socket_t)-1); |
2589 | ns = c->c_nameservers[i]; |
2590 | memcpy(dns->dns_addr, ns->ns_addr, dns->dns_addrlen = ns->ns_addrlen); |
2591 | su_inet_ntopinet_ntop(dns->dns_addr->ss_family, SS_ADDR(dns->dns_addr)((dns->dns_addr)->ss_family == 2 ? (void *)&((struct sockaddr_in *)dns->dns_addr)->sin_addr : ((dns->dns_addr )->ss_family == 10 ? (void *)&((struct sockaddr_in6 *) dns->dns_addr)->sin6_addr : (void *)&((struct sockaddr *)dns->dns_addr)->sa_data)), |
2592 | dns->dns_name, sizeof dns->dns_name); |
2593 | dns->dns_edns = c->c_opt.edns; |
2594 | servers[i] = dns++; |
2595 | } |
2596 | |
2597 | return servers; |
2598 | } |
2599 | |
2600 | static |
2601 | void sres_servers_close(sres_resolver_t *res, |
2602 | sres_server_t **servers) |
2603 | { |
2604 | int i; |
2605 | |
2606 | if (res == NULL((void*)0) || servers == NULL((void*)0)) |
2607 | return; |
2608 | |
2609 | for (i = 0; i < SRES_MAX_NAMESERVERS(SRES_MAX_NAMESERVERS); i++) { |
2610 | if (!servers[i]) |
2611 | break; |
2612 | |
2613 | if (servers[i]->dns_socket != INVALID_SOCKET((sres_socket_t)-1)) { |
2614 | if (res->res_updcb) |
2615 | res->res_updcb(res->res_async, INVALID_SOCKET((sres_socket_t)-1), servers[i]->dns_socket); |
2616 | sres_close(servers[i]->dns_socket)close((servers[i]->dns_socket)); |
2617 | } |
2618 | } |
2619 | } |
2620 | |
2621 | static |
2622 | int sres_servers_count(sres_server_t *const *servers) |
2623 | { |
2624 | int i; |
2625 | |
2626 | if (!servers) |
2627 | return 0; |
2628 | |
2629 | for (i = 0; i < SRES_MAX_NAMESERVERS(SRES_MAX_NAMESERVERS); i++) { |
2630 | if (!servers[i]) |
2631 | break; |
2632 | } |
2633 | |
2634 | return i; |
2635 | } |
2636 | |
2637 | static |
2638 | sres_socket_t sres_server_socket(sres_resolver_t *res, sres_server_t *dns) |
2639 | { |
2640 | int family = dns->dns_addr->ss_family; |
2641 | sres_socket_t s; |
2642 | |
2643 | if (dns->dns_socket != INVALID_SOCKET((sres_socket_t)-1)) |
2644 | return dns->dns_socket; |
2645 | |
2646 | s = socket(family, SOCK_DGRAMSOCK_DGRAM, IPPROTO_UDPIPPROTO_UDP); |
2647 | if (s == -1) { |
2648 | SU_DEBUG_1(("%s: %s: %s\n", "sres_server_socket", "socket",(((sresolv_log != ((void*)0) && sresolv_log->log_init ) == 0 ? 9 : ((sresolv_log != ((void*)0) && sresolv_log ->log_init > 1) ? sresolv_log->log_level : su_log_default ->log_level)) >= 1 ? (_su_llog(sresolv_log, 1, "sres.c" , (const char *)__func__, 2649, "%s: %s: %s\n", "sres_server_socket" , "socket", su_strerror(su_errno()))) : (void)0) |
2649 | su_strerror(su_errno())))(((sresolv_log != ((void*)0) && sresolv_log->log_init ) == 0 ? 9 : ((sresolv_log != ((void*)0) && sresolv_log ->log_init > 1) ? sresolv_log->log_level : su_log_default ->log_level)) >= 1 ? (_su_llog(sresolv_log, 1, "sres.c" , (const char *)__func__, 2649, "%s: %s: %s\n", "sres_server_socket" , "socket", su_strerror(su_errno()))) : (void)0); |
2650 | return s; |
2651 | } |
2652 | |
2653 | #if HAVE_IP_RECVERR1 |
2654 | if (family == AF_INET2 || family == AF_INET610) { |
2655 | int const one = 1; |
2656 | if (setsockopt(s, SOL_IP0, IP_RECVERR11, &one, sizeof(one)) < 0) { |
2657 | if (family == AF_INET2) |
2658 | SU_DEBUG_3(("setsockopt(IPVRECVERR): %s\n", su_strerror(su_errno())))(((sresolv_log != ((void*)0) && sresolv_log->log_init ) == 0 ? 9 : ((sresolv_log != ((void*)0) && sresolv_log ->log_init > 1) ? sresolv_log->log_level : su_log_default ->log_level)) >= 3 ? (_su_llog(sresolv_log, 3, "sres.c" , (const char *)__func__, 2658, "setsockopt(IPVRECVERR): %s\n" , su_strerror(su_errno()))) : (void)0); |
2659 | } |
2660 | } |
2661 | #endif |
2662 | #if HAVE_IPV6_RECVERR1 |
2663 | if (family == AF_INET610) { |
2664 | int const one = 1; |
2665 | if (setsockopt(s, SOL_IPV641, IPV6_RECVERR25, &one, sizeof(one)) < 0) |
2666 | SU_DEBUG_3(("setsockopt(IPV6_RECVERR): %s\n", su_strerror(su_errno())))(((sresolv_log != ((void*)0) && sresolv_log->log_init ) == 0 ? 9 : ((sresolv_log != ((void*)0) && sresolv_log ->log_init > 1) ? sresolv_log->log_level : su_log_default ->log_level)) >= 3 ? (_su_llog(sresolv_log, 3, "sres.c" , (const char *)__func__, 2666, "setsockopt(IPV6_RECVERR): %s\n" , su_strerror(su_errno()))) : (void)0); |
2667 | } |
2668 | #endif |
2669 | |
2670 | if (connect(s, (void *)dns->dns_addr, dns->dns_addrlen) < 0) { |
2671 | char ipaddr[64]; |
2672 | char const *lb = "", *rb = ""; |
2673 | |
2674 | if (family == AF_INET2) { |
2675 | void *addr = &((struct sockaddr_in *)dns->dns_addr)->sin_addr; |
2676 | su_inet_ntopinet_ntop(family, addr, ipaddr, sizeof ipaddr); |
2677 | } |
2678 | #if HAVE_SIN61 |
2679 | else if (family == AF_INET610) { |
2680 | void *addr = &((struct sockaddr_in6 *)dns->dns_addr)->sin6_addr; |
2681 | su_inet_ntopinet_ntop(family, addr, ipaddr, sizeof ipaddr); |
2682 | lb = "[", rb = "]"; |
2683 | } |
2684 | #endif |
2685 | else |
2686 | snprintf(ipaddr, sizeof ipaddr, "<af=%u>", family); |
2687 | |
2688 | SU_DEBUG_1(("%s: %s: %s: %s%s%s:%u\n", "sres_server_socket", "connect",(((sresolv_log != ((void*)0) && sresolv_log->log_init ) == 0 ? 9 : ((sresolv_log != ((void*)0) && sresolv_log ->log_init > 1) ? sresolv_log->log_level : su_log_default ->log_level)) >= 1 ? (_su_llog(sresolv_log, 1, "sres.c" , (const char *)__func__, 2690, "%s: %s: %s: %s%s%s:%u\n", "sres_server_socket" , "connect", su_strerror(su_errno()), lb, ipaddr, rb, ntohs(( (struct sockaddr_in *)dns->dns_addr)->sin_port))) : (void )0) |
2689 | su_strerror(su_errno()), lb, ipaddr, rb,(((sresolv_log != ((void*)0) && sresolv_log->log_init ) == 0 ? 9 : ((sresolv_log != ((void*)0) && sresolv_log ->log_init > 1) ? sresolv_log->log_level : su_log_default ->log_level)) >= 1 ? (_su_llog(sresolv_log, 1, "sres.c" , (const char *)__func__, 2690, "%s: %s: %s: %s%s%s:%u\n", "sres_server_socket" , "connect", su_strerror(su_errno()), lb, ipaddr, rb, ntohs(( (struct sockaddr_in *)dns->dns_addr)->sin_port))) : (void )0) |
2690 | ntohs(((struct sockaddr_in *)dns->dns_addr)->sin_port)))(((sresolv_log != ((void*)0) && sresolv_log->log_init ) == 0 ? 9 : ((sresolv_log != ((void*)0) && sresolv_log ->log_init > 1) ? sresolv_log->log_level : su_log_default ->log_level)) >= 1 ? (_su_llog(sresolv_log, 1, "sres.c" , (const char *)__func__, 2690, "%s: %s: %s: %s%s%s:%u\n", "sres_server_socket" , "connect", su_strerror(su_errno()), lb, ipaddr, rb, ntohs(( (struct sockaddr_in *)dns->dns_addr)->sin_port))) : (void )0); |
2691 | sres_close(s)close((s)); |
2692 | return INVALID_SOCKET((sres_socket_t)-1); |
2693 | } |
2694 | |
2695 | if (res->res_updcb) { |
2696 | if (res->res_updcb(res->res_async, s, INVALID_SOCKET((sres_socket_t)-1)) < 0) { |
2697 | SU_DEBUG_1(("%s: %s: %s\n", "sres_server_socket", "update callback",(((sresolv_log != ((void*)0) && sresolv_log->log_init ) == 0 ? 9 : ((sresolv_log != ((void*)0) && sresolv_log ->log_init > 1) ? sresolv_log->log_level : su_log_default ->log_level)) >= 1 ? (_su_llog(sresolv_log, 1, "sres.c" , (const char *)__func__, 2698, "%s: %s: %s\n", "sres_server_socket" , "update callback", su_strerror(su_errno()))) : (void)0) |
2698 | su_strerror(su_errno())))(((sresolv_log != ((void*)0) && sresolv_log->log_init ) == 0 ? 9 : ((sresolv_log != ((void*)0) && sresolv_log ->log_init > 1) ? sresolv_log->log_level : su_log_default ->log_level)) >= 1 ? (_su_llog(sresolv_log, 1, "sres.c" , (const char *)__func__, 2698, "%s: %s: %s\n", "sres_server_socket" , "update callback", su_strerror(su_errno()))) : (void)0); |
2699 | sres_close(s)close((s)); |
2700 | return INVALID_SOCKET((sres_socket_t)-1); |
2701 | } |
2702 | } |
2703 | |
2704 | dns->dns_socket = s; |
2705 | |
2706 | return s; |
2707 | } |
2708 | |
2709 | /* ---------------------------------------------------------------------- */ |
2710 | |
2711 | /** Send a query packet */ |
2712 | static |
2713 | int |
2714 | sres_send_dns_query(sres_resolver_t *res, |
2715 | sres_query_t *q) |
2716 | { |
2717 | sres_message_t m[1]; |
2718 | uint8_t i, i0, N = res->res_n_servers; |
2719 | sres_socket_t s; |
2720 | int error = 0; |
2721 | ssize_t size, no_edns_size, edns_size; |
2722 | uint16_t id = q->q_id; |
2723 | uint16_t type = q->q_type; |
2724 | char const *domain = q->q_name; |
2725 | time_t now = res->res_now; |
2726 | sres_server_t **servers = res->res_servers, *dns; |
2727 | char b[8]; |
2728 | |
2729 | if (now == 0) time(&now); |
2730 | |
2731 | SU_DEBUG_9(("sres_send_dns_query(%p, %p) called\n", (void *)res, (void *)q))(((sresolv_log != ((void*)0) && sresolv_log->log_init ) == 0 ? 9 : ((sresolv_log != ((void*)0) && sresolv_log ->log_init > 1) ? sresolv_log->log_level : su_log_default ->log_level)) >= 9 ? (_su_llog(sresolv_log, 9, "sres.c" , (const char *)__func__, 2731, "sres_send_dns_query(%p, %p) called\n" , (void *)res, (void *)q)) : (void)0); |
2732 | |
2733 | if (domain == NULL((void*)0)) |
2734 | return -1; |
2735 | if (servers == NULL((void*)0)) |
2736 | return -1; |
2737 | if (N == 0) |
2738 | return -1; |
2739 | |
2740 | memset(m, 0, offsetof(sres_message_t, m_data[sizeof m->m_packet.mp_header])__builtin_offsetof(sres_message_t, m_packet.mp_data[sizeof m-> m_packet.mp_header])); |
2741 | |
2742 | /* Create a DNS message */ |
2743 | size = sizeof(m->m_packet.mp_header); |
2744 | m->m_size = (uint16_t)sizeof(m->m_datam_packet.mp_data); |
2745 | m->m_offset = (uint16_t)size; |
2746 | |
2747 | m->m_idm_packet.mp_header.mh_id = id; |
2748 | m->m_flagsm_packet.mp_header.mh_flags = htons(SRES_HDR_QUERY | SRES_HDR_RD); |
2749 | |
2750 | /* Query record */ |
2751 | m->m_qdcountm_packet.mp_header.mh_qdcount = htons(1); |
2752 | m_put_domain(m, domain, 0, NULL((void*)0)); |
2753 | m_put_uint16(m, type); |
2754 | m_put_uint16(m, sres_class_in); |
2755 | |
2756 | no_edns_size = m->m_offset; |
2757 | |
2758 | /* EDNS0 record (optional) */ |
2759 | m_put_domain(m, ".", 0, NULL((void*)0)); |
2760 | m_put_uint16(m, sres_type_opt); |
2761 | m_put_uint16(m, sizeof(m->m_packet)); /* Class: our UDP payload size */ |
2762 | m_put_uint32(m, 0); /* TTL: extended RCODE & flags */ |
2763 | m_put_uint16(m, 0); |
2764 | |
2765 | edns_size = m->m_offset; |
2766 | |
2767 | if (m->m_error) { |
2768 | SU_DEBUG_3(("%s(): encoding: %s\n", "sres_send_dns_query", m->m_error))(((sresolv_log != ((void*)0) && sresolv_log->log_init ) == 0 ? 9 : ((sresolv_log != ((void*)0) && sresolv_log ->log_init > 1) ? sresolv_log->log_level : su_log_default ->log_level)) >= 3 ? (_su_llog(sresolv_log, 3, "sres.c" , (const char *)__func__, 2768, "%s(): encoding: %s\n", "sres_send_dns_query" , m->m_error)) : (void)0); |
2769 | su_seterrno(EIO5); |
2770 | return -1; |
2771 | } |
2772 | |
2773 | i0 = q->q_i_server; |
2774 | if (i0 > N) i0 = 0; /* Number of DNS servers reduced */ |
2775 | dns = servers[i = i0]; |
2776 | |
2777 | error = EIO5; |
2778 | |
2779 | if (res->res_config->c_opt.rotate || dns->dns_error || dns->dns_icmp) |
2780 | dns = sres_next_server(res, &q->q_i_server, 1), i = q->q_i_server; |
2781 | |
2782 | for (; dns; dns = sres_next_server(res, &i, 1)) { |
2783 | /* If server supports EDNS, include EDNS0 record */ |
2784 | q->q_edns = dns->dns_edns; |
2785 | /* 0 (no EDNS) or 1 (EDNS supported) additional data records */ |
2786 | m->m_arcountm_packet.mp_header.mh_arcount = htons(q->q_edns != 0); |
2787 | /* Size with or without EDNS record */ |
2788 | size = q->q_edns ? edns_size : no_edns_size; |
2789 | |
2790 | s = sres_server_socket(res, dns); |
2791 | |
2792 | if (s == INVALID_SOCKET((sres_socket_t)-1)) { |
2793 | dns->dns_icmp = now; |
2794 | dns->dns_error = SRES_TIME_MAX((time_t)9223372036854775807L); |
2795 | continue; |
2796 | } |
2797 | |
2798 | /* Send the DNS message via the UDP socket */ |
2799 | if (sres_send(s, m->m_data, size, 0)send((s),(m->m_packet.mp_data),(size),(0)) == size) |
2800 | break; |
2801 | error = su_errno(); |
2802 | |
2803 | dns->dns_icmp = now; |
2804 | dns->dns_error = now; /* Mark as a bad destination */ |
2805 | } |
2806 | |
2807 | if (!dns) { |
2808 | /* All servers have reported errors */ |
2809 | SU_DEBUG_5(("%s(): sendto: %s\n", "sres_send_dns_query",(((sresolv_log != ((void*)0) && sresolv_log->log_init ) == 0 ? 9 : ((sresolv_log != ((void*)0) && sresolv_log ->log_init > 1) ? sresolv_log->log_level : su_log_default ->log_level)) >= 5 ? (_su_llog(sresolv_log, 5, "sres.c" , (const char *)__func__, 2810, "%s(): sendto: %s\n", "sres_send_dns_query" , su_strerror(error))) : (void)0) |
2810 | su_strerror(error)))(((sresolv_log != ((void*)0) && sresolv_log->log_init ) == 0 ? 9 : ((sresolv_log != ((void*)0) && sresolv_log ->log_init > 1) ? sresolv_log->log_level : su_log_default ->log_level)) >= 5 ? (_su_llog(sresolv_log, 5, "sres.c" , (const char *)__func__, 2810, "%s(): sendto: %s\n", "sres_send_dns_query" , su_strerror(error))) : (void)0); |
2811 | return su_seterrno(error); |
2812 | } |
2813 | |
2814 | q->q_i_server = i; |
2815 | |
2816 | SU_DEBUG_5(("%s(%p, %p) id=%u %s %s (to [%s]:%u)\n",(((sresolv_log != ((void*)0) && sresolv_log->log_init ) == 0 ? 9 : ((sresolv_log != ((void*)0) && sresolv_log ->log_init > 1) ? sresolv_log->log_level : su_log_default ->log_level)) >= 5 ? (_su_llog(sresolv_log, 5, "sres.c" , (const char *)__func__, 2820, "%s(%p, %p) id=%u %s %s (to [%s]:%u)\n" , "sres_send_dns_query", (void *)res, (void *)q, id, sres_record_type (type, b), domain, dns->dns_name, htons(((struct sockaddr_in *)dns->dns_addr)->sin_port))) : (void)0) |
2817 | "sres_send_dns_query",(((sresolv_log != ((void*)0) && sresolv_log->log_init ) == 0 ? 9 : ((sresolv_log != ((void*)0) && sresolv_log ->log_init > 1) ? sresolv_log->log_level : su_log_default ->log_level)) >= 5 ? (_su_llog(sresolv_log, 5, "sres.c" , (const char *)__func__, 2820, "%s(%p, %p) id=%u %s %s (to [%s]:%u)\n" , "sres_send_dns_query", (void *)res, (void *)q, id, sres_record_type (type, b), domain, dns->dns_name, htons(((struct sockaddr_in *)dns->dns_addr)->sin_port))) : (void)0) |
2818 | (void *)res, (void *)q, id, sres_record_type(type, b), domain,(((sresolv_log != ((void*)0) && sresolv_log->log_init ) == 0 ? 9 : ((sresolv_log != ((void*)0) && sresolv_log ->log_init > 1) ? sresolv_log->log_level : su_log_default ->log_level)) >= 5 ? (_su_llog(sresolv_log, 5, "sres.c" , (const char *)__func__, 2820, "%s(%p, %p) id=%u %s %s (to [%s]:%u)\n" , "sres_send_dns_query", (void *)res, (void *)q, id, sres_record_type (type, b), domain, dns->dns_name, htons(((struct sockaddr_in *)dns->dns_addr)->sin_port))) : (void)0) |
2819 | dns->dns_name,(((sresolv_log != ((void*)0) && sresolv_log->log_init ) == 0 ? 9 : ((sresolv_log != ((void*)0) && sresolv_log ->log_init > 1) ? sresolv_log->log_level : su_log_default ->log_level)) >= 5 ? (_su_llog(sresolv_log, 5, "sres.c" , (const char *)__func__, 2820, "%s(%p, %p) id=%u %s %s (to [%s]:%u)\n" , "sres_send_dns_query", (void *)res, (void *)q, id, sres_record_type (type, b), domain, dns->dns_name, htons(((struct sockaddr_in *)dns->dns_addr)->sin_port))) : (void)0) |
2820 | htons(((struct sockaddr_in *)dns->dns_addr)->sin_port)))(((sresolv_log != ((void*)0) && sresolv_log->log_init ) == 0 ? 9 : ((sresolv_log != ((void*)0) && sresolv_log ->log_init > 1) ? sresolv_log->log_level : su_log_default ->log_level)) >= 5 ? (_su_llog(sresolv_log, 5, "sres.c" , (const char *)__func__, 2820, "%s(%p, %p) id=%u %s %s (to [%s]:%u)\n" , "sres_send_dns_query", (void *)res, (void *)q, id, sres_record_type (type, b), domain, dns->dns_name, htons(((struct sockaddr_in *)dns->dns_addr)->sin_port))) : (void)0); |
2821 | |
2822 | return 0; |
2823 | } |
2824 | |
2825 | /** Retry time after ICMP error */ |
2826 | #define DNS_ICMP_TIMEOUT60 60 |
2827 | |
2828 | /** Retry time after immediate error */ |
2829 | #define DNS_ERROR_TIMEOUT10 10 |
2830 | |
2831 | /** Select next server. |
2832 | * |
2833 | * @param res resolver object |
2834 | * @param[in,out] in_out_i index to DNS server table |
2835 | * @param always return always a server |
2836 | */ |
2837 | static |
2838 | sres_server_t *sres_next_server(sres_resolver_t *res, |
2839 | uint8_t *in_out_i, |
2840 | int always) |
2841 | { |
2842 | int i, j, N; |
2843 | sres_server_t *dns, **servers; |
2844 | time_t now = res->res_now; |
2845 | |
2846 | N = res->res_n_servers; |
2847 | servers = res->res_servers; |
2848 | i = *in_out_i; |
2849 | |
2850 | assert(res->res_servers && res->res_servers[i])((void) sizeof ((res->res_servers && res->res_servers [i]) ? 1 : 0), __extension__ ({ if (res->res_servers && res->res_servers[i]) ; else __assert_fail ("res->res_servers && res->res_servers[i]" , "sres.c", 2850, __extension__ __PRETTY_FUNCTION__); })); |
2851 | |
2852 | for (j=0; j < N; j++) { |
2853 | dns = servers[j]; if (!dns) continue; |
2854 | if (dns->dns_icmp + DNS_ICMP_TIMEOUT60 < now) |
2855 | dns->dns_icmp = 0; |
2856 | if (dns->dns_error + DNS_ERROR_TIMEOUT10 < now && |
2857 | dns->dns_error != SRES_TIME_MAX((time_t)9223372036854775807L)) |
2858 | dns->dns_error = 0; |
2859 | } |
2860 | |
2861 | /* Retry using another server? */ |
2862 | for (j = (i + 1) % N; (j != i); j = (j + 1) % N) { |
2863 | dns = servers[j]; if (!dns) continue; |
2864 | if (dns->dns_icmp == 0) { |
2865 | return *in_out_i = j, dns; |
2866 | } |
2867 | } |
2868 | |
2869 | for (j = (i + 1) % N; (j != i); j = (j + 1) % N) { |
2870 | dns = servers[j]; if (!dns) continue; |
2871 | if (dns->dns_error == 0) { |
2872 | return *in_out_i = j, dns; |
2873 | } |
2874 | } |
2875 | |
2876 | if (!always) |
2877 | return NULL((void*)0); |
2878 | |
2879 | dns = servers[i]; |
2880 | if (dns && dns->dns_error < now && dns->dns_error != SRES_TIME_MAX((time_t)9223372036854775807L)) |
2881 | return dns; |
2882 | |
2883 | for (j = (i + 1) % N; j != i; j = (j + 1) % N) { |
2884 | dns = servers[j]; if (!dns) continue; |
2885 | if (dns->dns_error < now && dns->dns_error != SRES_TIME_MAX((time_t)9223372036854775807L)) |
2886 | return *in_out_i = j, dns; |
2887 | } |
2888 | |
2889 | return NULL((void*)0); |
2890 | } |
2891 | |
2892 | /** |
2893 | * Callback function for subqueries |
2894 | */ |
2895 | static |
2896 | void sres_answer_subquery(sres_context_t *context, |
2897 | sres_query_t *query, |
2898 | sres_record_t **answers) |
2899 | { |
2900 | sres_resolver_t *res; |
2901 | sres_query_t *top = (sres_query_t *)context; |
2902 | int i; |
2903 | assert(top)((void) sizeof ((top) ? 1 : 0), __extension__ ({ if (top) ; else __assert_fail ("top", "sres.c", 2903, __extension__ __PRETTY_FUNCTION__ ); })); assert(top->q_n_subs > 0)((void) sizeof ((top->q_n_subs > 0) ? 1 : 0), __extension__ ({ if (top->q_n_subs > 0) ; else __assert_fail ("top->q_n_subs > 0" , "sres.c", 2903, __extension__ __PRETTY_FUNCTION__); })); assert(query)((void) sizeof ((query) ? 1 : 0), __extension__ ({ if (query) ; else __assert_fail ("query", "sres.c", 2903, __extension__ __PRETTY_FUNCTION__); })); |
2904 | |
2905 | res = query->q_res; |
2906 | |
2907 | for (i = 0; i <= SRES_MAX_SEARCH(SRES_MAX_SEARCH); i++) { |
2908 | if (top->q_subqueries[i] == query) |
2909 | break; |
2910 | } |
2911 | assert(i <= SRES_MAX_SEARCH)((void) sizeof ((i <= (SRES_MAX_SEARCH)) ? 1 : 0), __extension__ ({ if (i <= (SRES_MAX_SEARCH)) ; else __assert_fail ("i <= SRES_MAX_SEARCH" , "sres.c", 2911, __extension__ __PRETTY_FUNCTION__); })); |
2912 | if (i > SRES_MAX_SEARCH(SRES_MAX_SEARCH) || top->q_n_subs == 0) { |
2913 | sres_free_answers(res, answers); |
2914 | return; |
2915 | } |
2916 | |
2917 | if (answers) { |
2918 | int j, k; |
2919 | for (j = 0, k = 0; answers[j]; j++) { |
2920 | if (answers[j]->sr_statussr_record->r_status) |
2921 | sres_free_answer(query->q_res, answers[j]); |
2922 | else |
2923 | answers[k++] = answers[j]; |
2924 | } |
2925 | answers[k] = NULL((void*)0); |
2926 | if (!answers[0]) |
2927 | sres_free_answers(query->q_res, answers), answers = NULL((void*)0); |
2928 | } |
2929 | |
2930 | top->q_subqueries[i] = NULL((void*)0); |
2931 | top->q_subanswers[i] = answers; |
2932 | top->q_n_subs--; |
2933 | |
2934 | if (answers && top->q_callback) { |
2935 | sres_answer_f *callback = top->q_callback; |
2936 | |
2937 | top->q_callback = NULL((void*)0); |
2938 | sres_remove_query(top->q_res, top, 1); |
2939 | callback(top->q_context, top, answers); |
2940 | } |
2941 | else if (top->q_n_subs == 0 && top->q_id == 0) { |
2942 | sres_query_report_error(top, NULL((void*)0)); |
2943 | }; |
2944 | } |
2945 | |
2946 | /** Report sres error */ |
2947 | static void |
2948 | sres_query_report_error(sres_query_t *q, |
2949 | sres_record_t **answers) |
2950 | { |
2951 | int i; |
2952 | |
2953 | if (q->q_callback) { |
2954 | char sbuf[8], tbuf[8]; |
2955 | int status = 0; |
2956 | |
2957 | for (i = 0; i <= SRES_MAX_SEARCH(SRES_MAX_SEARCH); i++) { |
2958 | if (q->q_subqueries[i]) /* a pending query... */ |
2959 | return; |
2960 | |
2961 | if (q->q_subanswers[i]) { |
2962 | answers = q->q_subanswers[i]; |
2963 | q->q_subanswers[i] = NULL((void*)0); |
2964 | break; |
2965 | } |
2966 | } |
2967 | |
2968 | if (answers == NULL((void*)0)) { |
2969 | sres_cache_t *cache = q->q_res->res_cache; |
2970 | |
2971 | status = q->q_retry_count ? SRES_TIMEOUT_ERR : SRES_NETWORK_ERR; |
2972 | |
2973 | answers = su_zalloc(CHOME(cache)((su_home_t *)(cache)), 2 * sizeof *answers); |
2974 | if (answers) |
2975 | answers[0] = sres_create_error_rr(cache, q, status); |
2976 | } |
2977 | else { |
2978 | for (i = 0; answers[i]; i++) { |
2979 | status = answers[i]->sr_record->r_status; |
2980 | if (status) |
2981 | break; |
2982 | } |
2983 | } |
2984 | |
2985 | SU_DEBUG_5(("sres(q=%p): reporting error %s for %s %s\n",(((sresolv_log != ((void*)0) && sresolv_log->log_init ) == 0 ? 9 : ((sresolv_log != ((void*)0) && sresolv_log ->log_init > 1) ? sresolv_log->log_level : su_log_default ->log_level)) >= 5 ? (_su_llog(sresolv_log, 5, "sres.c" , (const char *)__func__, 2988, "sres(q=%p): reporting error %s for %s %s\n" , (void *)q, sres_record_status(status, sbuf), sres_record_type (q->q_type, tbuf), q->q_name)) : (void)0) |
2986 | (void *)q,(((sresolv_log != ((void*)0) && sresolv_log->log_init ) == 0 ? 9 : ((sresolv_log != ((void*)0) && sresolv_log ->log_init > 1) ? sresolv_log->log_level : su_log_default ->log_level)) >= 5 ? (_su_llog(sresolv_log, 5, "sres.c" , (const char *)__func__, 2988, "sres(q=%p): reporting error %s for %s %s\n" , (void *)q, sres_record_status(status, sbuf), sres_record_type (q->q_type, tbuf), q->q_name)) : (void)0) |
2987 | sres_record_status(status, sbuf),(((sresolv_log != ((void*)0) && sresolv_log->log_init ) == 0 ? 9 : ((sresolv_log != ((void*)0) && sresolv_log ->log_init > 1) ? sresolv_log->log_level : su_log_default ->log_level)) >= 5 ? (_su_llog(sresolv_log, 5, "sres.c" , (const char *)__func__, 2988, "sres(q=%p): reporting error %s for %s %s\n" , (void *)q, sres_record_status(status, sbuf), sres_record_type (q->q_type, tbuf), q->q_name)) : (void)0) |
2988 | sres_record_type(q->q_type, tbuf), q->q_name))(((sresolv_log != ((void*)0) && sresolv_log->log_init ) == 0 ? 9 : ((sresolv_log != ((void*)0) && sresolv_log ->log_init > 1) ? sresolv_log->log_level : su_log_default ->log_level)) >= 5 ? (_su_llog(sresolv_log, 5, "sres.c" , (const char *)__func__, 2988, "sres(q=%p): reporting error %s for %s %s\n" , (void *)q, sres_record_status(status, sbuf), sres_record_type (q->q_type, tbuf), q->q_name)) : (void)0); |
2989 | |
2990 | sres_remove_query(q->q_res, q, 1); |
2991 | (q->q_callback)(q->q_context, q, answers); |
2992 | } |
2993 | |
2994 | sres_free_query(q->q_res, q); |
2995 | } |
2996 | |
2997 | /** Resolver timer function. |
2998 | * |
2999 | * The function sresolver_timer() should be called in regular intervals. We |
3000 | * recommend calling it in 500 ms intervals. |
3001 | * |
3002 | * @param res pointer to resolver object |
3003 | * @param dummy argument for compatibility |
3004 | */ |
3005 | void sres_resolver_timer(sres_resolver_t *res, int dummy) |
3006 | { |
3007 | size_t i; |
3008 | sres_query_t *q; |
3009 | time_t now, retry_time; |
3010 | |
3011 | if (res == NULL((void*)0)) |
3012 | return; |
3013 | |
3014 | now = time(&res->res_now); |
3015 | |
3016 | if (res->res_queries->qt_used) { |
3017 | SU_DEBUG_9(("sres_resolver_timer() called at %lu\n", (long) now))(((sresolv_log != ((void*)0) && sresolv_log->log_init ) == 0 ? 9 : ((sresolv_log != ((void*)0) && sresolv_log ->log_init > 1) ? sresolv_log->log_level : su_log_default ->log_level)) >= 9 ? (_su_llog(sresolv_log, 9, "sres.c" , (const char *)__func__, 3017, "sres_resolver_timer() called at %lu\n" , (long) now)) : (void)0); |
3018 | |
3019 | /** Every time it is called it goes through all query structures, and |
3020 | * retransmits all the query messages, which have not been answered yet. |
3021 | */ |
3022 | for (i = 0; i < res->res_queries->qt_size; i++) { |
3023 | q = res->res_queries->qt_table[i]; |
3024 | |
3025 | if (!q) |
3026 | continue; |
3027 | |
3028 | /* Exponential backoff */ |
3029 | retry_time = q->q_timestamp + ((time_t)1 << q->q_retry_count); |
3030 | |
3031 | if (now < retry_time) |
3032 | continue; |
3033 | |
3034 | sres_resend_dns_query(res, q, 1); |
3035 | |
3036 | if (q != res->res_queries->qt_table[i]) |
3037 | i--; |
3038 | } |
3039 | |
3040 | if (res->res_schedulecb && res->res_queries->qt_used) |
3041 | res->res_schedulecb(res->res_async, SRES_RETRANSMIT_INTERVAL(SRES_RETRANSMIT_INTERVAL)); |
3042 | } |
3043 | |
3044 | sres_cache_clean(res->res_cache, res->res_now); |
3045 | } |
3046 | |
3047 | /** Resend DNS query, report error if cannot resend any more. |
3048 | * |
3049 | * @param res resolver object |
3050 | * @param q query object |
3051 | * @param timeout true if resent because of timeout |
3052 | * (false if because icmp error report) |
3053 | */ |
3054 | static void |
3055 | sres_resend_dns_query(sres_resolver_t *res, sres_query_t *q, int timeout) |
3056 | { |
3057 | uint8_t i, N; |
3058 | sres_server_t *dns; |
3059 | |
3060 | SU_DEBUG_9(("sres_resend_dns_query(%p, %p, %s) called\n",(((sresolv_log != ((void*)0) && sresolv_log->log_init ) == 0 ? 9 : ((sresolv_log != ((void*)0) && sresolv_log ->log_init > 1) ? sresolv_log->log_level : su_log_default ->log_level)) >= 9 ? (_su_llog(sresolv_log, 9, "sres.c" , (const char *)__func__, 3061, "sres_resend_dns_query(%p, %p, %s) called\n" , (void *)res, (void *)q, timeout ? "timeout" : "error")) : ( void)0) |
3061 | (void *)res, (void *)q, timeout ? "timeout" : "error"))(((sresolv_log != ((void*)0) && sresolv_log->log_init ) == 0 ? 9 : ((sresolv_log != ((void*)0) && sresolv_log ->log_init > 1) ? sresolv_log->log_level : su_log_default ->log_level)) >= 9 ? (_su_llog(sresolv_log, 9, "sres.c" , (const char *)__func__, 3061, "sres_resend_dns_query(%p, %p, %s) called\n" , (void *)res, (void *)q, timeout ? "timeout" : "error")) : ( void)0); |
3062 | |
3063 | N = res->res_n_servers; |
3064 | |
3065 | if (N > 0 && q->q_retry_count < SRES_MAX_RETRY_COUNT(SRES_MAX_RETRY_COUNT)) { |
3066 | i = q->q_i_server; |
3067 | dns = sres_next_server(res, &i, timeout); |
3068 | |
3069 | if (dns) { |
3070 | res->res_i_server = q->q_i_server = i; |
3071 | |
3072 | if (q->q_retry_count > res->res_n_servers + 1 && |
3073 | dns->dns_edns == edns_not_tried) |
3074 | q->q_edns = edns_not_supported; |
3075 | |
3076 | sres_send_dns_query(res, q); |
3077 | |
3078 | if (timeout) |
3079 | q->q_retry_count++; |
3080 | |
3081 | return; |
3082 | } |
3083 | } |
3084 | |
3085 | /* report timeout/network error */ |
3086 | q->q_id = 0; |
3087 | |
3088 | if (q->q_n_subs) |
3089 | return; /* let subqueries also timeout */ |
3090 | |
3091 | sres_query_report_error(q, NULL((void*)0)); |
3092 | } |
3093 | |
3094 | static void |
3095 | sres_resolve_cname(sres_resolver_t *res, |
3096 | sres_query_t *orig_query, |
3097 | char const *cname) |
3098 | { |
3099 | sres_query_t *query; |
3100 | |
3101 | query = sres_query_alloc(res, |
3102 | sres_resolving_cname, |
3103 | (sres_context_t *)orig_query, |
3104 | orig_query->q_type, |
3105 | cname); |
3106 | |
3107 | if (query) |
3108 | sres_send_dns_query(res, query); |
3109 | else |
3110 | sres_query_report_error(orig_query, NULL((void*)0)); |
3111 | } |
3112 | |
3113 | static void |
3114 | sres_resolving_cname(sres_context_t *original_query, |
3115 | sres_query_t *query, |
3116 | sres_record_t **answers) |
3117 | { |
3118 | sres_query_t *orig = (sres_query_t *)original_query; |
3119 | |
3120 | /* Notify the listener */ |
3121 | if (orig->q_callback != NULL((void*)0)) |
3122 | (orig->q_callback)(orig->q_context, orig, answers); |
3123 | |
3124 | sres_free_query(orig->q_res, orig); |
3125 | } |
3126 | |
3127 | /** Get a server by socket */ |
3128 | static |
3129 | sres_server_t * |
3130 | sres_server_by_socket(sres_resolver_t const *res, sres_socket_t socket) |
3131 | { |
3132 | int i; |
3133 | |
3134 | if (socket == -1) |
3135 | return NULL((void*)0); |
3136 | |
3137 | for (i = 0; i < res->res_n_servers; i++) { |
3138 | if (socket == res->res_servers[i]->dns_socket) |
3139 | return res->res_servers[i]; |
3140 | } |
3141 | |
3142 | return NULL((void*)0); |
3143 | } |
3144 | |
3145 | static |
3146 | void |
3147 | sres_canonize_sockaddr(struct sockaddr_storage *from, socklen_t *fromlen) |
3148 | { |
3149 | #if HAVE_SIN61 |
3150 | struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)from; |
3151 | |
3152 | size_t sin6_addrsize = |
3153 | offsetof(struct sockaddr_in6, sin6_addr)__builtin_offsetof(struct sockaddr_in6, sin6_addr) + |
3154 | (sizeof sin6->sin6_addr); |
3155 | |
3156 | if (from->ss_family == AF_INET610) { |
3157 | struct in6_addr const *ip6 = &sin6->sin6_addr; |
3158 | |
3159 | if (IN6_IS_ADDR_V4MAPPED(ip6)(__extension__ ({ const struct in6_addr *__a = (const struct in6_addr *) (ip6); __a->__in6_u.__u6_addr32[0] == 0 && __a ->__in6_u.__u6_addr32[1] == 0 && __a->__in6_u.__u6_addr32 [2] == htonl (0xffff); })) || IN6_IS_ADDR_V4COMPAT(ip6)(__extension__ ({ const struct in6_addr *__a = (const struct in6_addr *) (ip6); __a->__in6_u.__u6_addr32[0] == 0 && __a ->__in6_u.__u6_addr32[1] == 0 && __a->__in6_u.__u6_addr32 [2] == 0 && ntohl (__a->__in6_u.__u6_addr32[3]) > 1; }))) { |
3160 | /* Convert to a IPv4 address */ |
3161 | struct sockaddr_in *sin = (struct sockaddr_in *)from; |
3162 | memcpy(&sin->sin_addr, ip6->s6_addr__in6_u.__u6_addr8 + 12, sizeof sin->sin_addr); |
3163 | sin->sin_family = AF_INET2; |
3164 | *fromlen = sizeof (*sin); |
3165 | #if HAVE_SA_LEN |
3166 | sin->sin_len = sizeof (*sin); |
3167 | #endif |
3168 | } |
3169 | else if (sin6_addrsize < *fromlen) { |
3170 | /* Zero extra sin6 members like sin6_flowinfo or sin6_scope_id */ |
3171 | memset((char *)from + sin6_addrsize, 0, *fromlen - sin6_addrsize); |
3172 | } |
3173 | } |
3174 | #endif |
3175 | |
3176 | if (from->ss_family == AF_INET2) { |
3177 | struct sockaddr_in *sin = (struct sockaddr_in *)from; |
3178 | memset(sin->sin_zero, 0, sizeof (sin->sin_zero)); |
3179 | } |
3180 | } |
3181 | |
3182 | static |
3183 | int sres_no_update(sres_async_t *async, |
3184 | sres_socket_t new_socket, |
3185 | sres_socket_t old_socket) |
3186 | { |
3187 | return 0; |
3188 | } |
3189 | |
3190 | /** Create connected sockets for resolver. |
3191 | */ |
3192 | int sres_resolver_sockets(sres_resolver_t *res, |
3193 | sres_socket_t *return_sockets, |
3194 | int n) |
3195 | { |
3196 | sres_socket_t s = INVALID_SOCKET((sres_socket_t)-1); |
3197 | int i, retval; |
3198 | |
3199 | if (!sres_resolver_set_async(res, sres_no_update, |
3200 | (sres_async_t *)-1, 1)) |
3201 | return -1; |
3202 | |
3203 | retval = res->res_n_servers; assert(retval <= SRES_MAX_NAMESERVERS)((void) sizeof ((retval <= (SRES_MAX_NAMESERVERS)) ? 1 : 0 ), __extension__ ({ if (retval <= (SRES_MAX_NAMESERVERS)) ; else __assert_fail ("retval <= SRES_MAX_NAMESERVERS", "sres.c" , 3203, __extension__ __PRETTY_FUNCTION__); })); |
3204 | |
3205 | if (!return_sockets || n == 0) |
3206 | return retval; |
3207 | |
3208 | for (i = 0; i < retval && i < n;) { |
3209 | sres_server_t *dns = res->res_servers[i]; |
3210 | |
3211 | s = sres_server_socket(res, dns); |
3212 | |
3213 | if (s == INVALID_SOCKET((sres_socket_t)-1)) { /* Mark as a bad destination */ |
3214 | dns->dns_icmp = SRES_TIME_MAX((time_t)9223372036854775807L); |
3215 | dns->dns_error = SRES_TIME_MAX((time_t)9223372036854775807L); |
3216 | } |
3217 | |
3218 | return_sockets[i++] = s; |
3219 | } |
3220 | |
3221 | return retval; |
3222 | } |
3223 | |
3224 | #if 0 |
3225 | /** Get a server by socket address */ |
3226 | static |
3227 | sres_server_t * |
3228 | sres_server_by_sockaddr(sres_resolver_t const *res, |
3229 | void const *from, socklen_t fromlen) |
3230 | { |
3231 | int i; |
3232 | |
3233 | for (i = 0; i < res->res_n_servers; i++) { |
3234 | sres_server_t *dns = res->res_servers[i]; |
3235 | if (dns->dns_addrlen == fromlen && |
3236 | memcmp(dns->dns_addr, from, fromlen) == 0) |
3237 | return dns; |
3238 | } |
3239 | |
3240 | return NULL((void*)0); |
3241 | } |
3242 | #endif |
3243 | |
3244 | /** Receive error message from socket. */ |
3245 | #if HAVE_IP_RECVERR1 || HAVE_IPV6_RECVERR1 |
3246 | int sres_resolver_error(sres_resolver_t *res, int socket) |
3247 | { |
3248 | int errcode = 0; |
3249 | struct cmsghdr *c; |
3250 | struct sock_extended_err *ee; |
3251 | struct sockaddr_storage *from; |
3252 | char control[512]; |
3253 | char errmsg[64 + 768]; |
3254 | struct iovec iov[1]; |
3255 | struct msghdr msg[1] = {{ 0 }}; |
3256 | struct sockaddr_storage name[1] = {{ 0 }}; |
3257 | int n; |
3258 | char info[128] = ""; |
3259 | |
3260 | SU_DEBUG_9(("%s(%p, %u) called\n", "sres_resolver_error",(((sresolv_log != ((void*)0) && sresolv_log->log_init ) == 0 ? 9 : ((sresolv_log != ((void*)0) && sresolv_log ->log_init > 1) ? sresolv_log->log_level : su_log_default ->log_level)) >= 9 ? (_su_llog(sresolv_log, 9, "sres.c" , (const char *)__func__, 3261, "%s(%p, %u) called\n", "sres_resolver_error" , (void *)res, socket)) : (void)0) |
3261 | (void *)res, socket))(((sresolv_log != ((void*)0) && sresolv_log->log_init ) == 0 ? 9 : ((sresolv_log != ((void*)0) && sresolv_log ->log_init > 1) ? sresolv_log->log_level : su_log_default ->log_level)) >= 9 ? (_su_llog(sresolv_log, 9, "sres.c" , (const char *)__func__, 3261, "%s(%p, %u) called\n", "sres_resolver_error" , (void *)res, socket)) : (void)0); |
3262 | |
3263 | msg->msg_name = name, msg->msg_namelen = sizeof(name); |
3264 | msg->msg_iov = iov, msg->msg_iovlen = 1; |
3265 | iov->iov_base = errmsg, iov->iov_len = sizeof(errmsg); |
3266 | msg->msg_control = control, msg->msg_controllen = sizeof(control); |
3267 | |
3268 | n = recvmsg(socket, msg, MSG_ERRQUEUEMSG_ERRQUEUE); |
3269 | |
3270 | if (n < 0) { |
3271 | int error = su_errno(); |
3272 | if (error != EAGAIN11 && error != EWOULDBLOCK11) |
3273 | SU_DEBUG_1(("%s: recvmsg: %s\n", __func__, su_strerror(error)))(((sresolv_log != ((void*)0) && sresolv_log->log_init ) == 0 ? 9 : ((sresolv_log != ((void*)0) && sresolv_log ->log_init > 1) ? sresolv_log->log_level : su_log_default ->log_level)) >= 1 ? (_su_llog(sresolv_log, 1, "sres.c" , (const char *)__func__, 3273, "%s: recvmsg: %s\n", __func__ , su_strerror(error))) : (void)0); |
3274 | return n; |
3275 | } |
3276 | |
3277 | if ((msg->msg_flags & MSG_ERRQUEUEMSG_ERRQUEUE) != MSG_ERRQUEUEMSG_ERRQUEUE) { |
3278 | SU_DEBUG_1(("%s: recvmsg: no errqueue\n", __func__))(((sresolv_log != ((void*)0) && sresolv_log->log_init ) == 0 ? 9 : ((sresolv_log != ((void*)0) && sresolv_log ->log_init > 1) ? sresolv_log->log_level : su_log_default ->log_level)) >= 1 ? (_su_llog(sresolv_log, 1, "sres.c" , (const char *)__func__, 3278, "%s: recvmsg: no errqueue\n", __func__)) : (void)0); |
3279 | return su_seterrno(EIO5); |
3280 | } |
3281 | |
3282 | if (msg->msg_flags & MSG_CTRUNCMSG_CTRUNC) { |
3283 | SU_DEBUG_1(("%s: extended error was truncated\n", __func__))(((sresolv_log != ((void*)0) && sresolv_log->log_init ) == 0 ? 9 : ((sresolv_log != ((void*)0) && sresolv_log ->log_init > 1) ? sresolv_log->log_level : su_log_default ->log_level)) >= 1 ? (_su_llog(sresolv_log, 1, "sres.c" , (const char *)__func__, 3283, "%s: extended error was truncated\n" , __func__)) : (void)0); |
3284 | return su_seterrno(EIO5); |
3285 | } |
3286 | |
3287 | if (msg->msg_flags & MSG_TRUNCMSG_TRUNC) { |
3288 | /* ICMP message may contain original message... */ |
3289 | SU_DEBUG_5(("%s: icmp(6) message was truncated (at %d)\n", __func__, n))(((sresolv_log != ((void*)0) && sresolv_log->log_init ) == 0 ? 9 : ((sresolv_log != ((void*)0) && sresolv_log ->log_init > 1) ? sresolv_log->log_level : su_log_default ->log_level)) >= 5 ? (_su_llog(sresolv_log, 5, "sres.c" , (const char *)__func__, 3289, "%s: icmp(6) message was truncated (at %d)\n" , __func__, n)) : (void)0); |
3290 | } |
3291 | |
3292 | /* Go through the ancillary data */ |
3293 | for (c = CMSG_FIRSTHDR(msg)((size_t) (msg)->msg_controllen >= sizeof (struct cmsghdr ) ? (struct cmsghdr *) (msg)->msg_control : (struct cmsghdr *) 0); c; c = CMSG_NXTHDR(msg, c)__cmsg_nxthdr (msg, c)) { |
3294 | if (0 |
3295 | #if HAVE_IP_RECVERR1 |
3296 | || (c->cmsg_level == SOL_IP0 && c->cmsg_type == IP_RECVERR11) |
3297 | #endif |
3298 | #if HAVE_IPV6_RECVERR1 |
3299 | || (c->cmsg_level == SOL_IPV641 && c->cmsg_type == IPV6_RECVERR25) |
3300 | #endif |
3301 | ) { |
3302 | char const *origin; |
3303 | |
3304 | ee = (struct sock_extended_err *)CMSG_DATA(c)((c)->__cmsg_data); |
3305 | from = (void *)SO_EE_OFFENDER(ee)((struct sockaddr*)((ee)+1)); |
3306 | info[0] = '\0'; |
3307 | |
3308 | switch (ee->ee_origin) { |
3309 | case SO_EE_ORIGIN_LOCAL1: |
3310 | strcpy(info, origin = "local"); |
3311 | break; |
3312 | case SO_EE_ORIGIN_ICMP2: |
3313 | snprintf(info, sizeof(info), "%s type=%u code=%u", |
3314 | origin = "icmp", ee->ee_type, ee->ee_code); |
3315 | break; |
3316 | case SO_EE_ORIGIN_ICMP63: |
3317 | snprintf(info, sizeof(info), "%s type=%u code=%u", |
3318 | origin = "icmp6", ee->ee_type, ee->ee_code); |
3319 | break; |
3320 | case SO_EE_ORIGIN_NONE0: |
3321 | strcpy(info, origin = "none"); |
3322 | break; |
3323 | default: |
3324 | strcpy(info, origin = "unknown"); |
3325 | break; |
3326 | } |
3327 | |
3328 | if (ee->ee_info) |
3329 | snprintf(info + strlen(info), sizeof(info) - strlen(info), |
3330 | " info=%08x", ee->ee_info); |
3331 | errcode = ee->ee_errno; |
3332 | |
3333 | if (from->ss_family != AF_UNSPEC0) { |
3334 | socklen_t fromlen = ((char *)c + c->cmsg_len) - (char *)from; |
3335 | |
3336 | sres_canonize_sockaddr(from, &fromlen); |
3337 | |
3338 | snprintf(info + strlen(info), sizeof(info) - strlen(info), |
3339 | " reported by "); |
3340 | su_inet_ntopinet_ntop(from->ss_family, SS_ADDR(from)((from)->ss_family == 2 ? (void *)&((struct sockaddr_in *)from)->sin_addr : ((from)->ss_family == 10 ? (void * )&((struct sockaddr_in6 *)from)->sin6_addr : (void *)& ((struct sockaddr *)from)->sa_data)), |
3341 | info + strlen(info), sizeof(info) - strlen(info)); |
3342 | } |
3343 | |
3344 | if (msg->msg_namelen <= 0) |
3345 | break; |
3346 | |
3347 | { |
3348 | int error; |
3349 | socklen_t errorlen = sizeof error; |
3350 | /* Get error, if any */ |
3351 | getsockopt(socket, SOL_SOCKET1, SO_ERROR4, (void *)&error, &errorlen); |
3352 | } |
3353 | |
3354 | if (sres_resolver_report_error(res, socket, errcode, |
3355 | msg->msg_name, msg->msg_namelen, |
3356 | info)) |
3357 | return errcode; |
3358 | break; |
3359 | } |
3360 | } |
3361 | |
3362 | if (errcode) |
3363 | sres_resolver_report_error(res, socket, errcode, NULL((void*)0), 0, info); |
3364 | |
3365 | return errcode; |
3366 | } |
3367 | |
3368 | #else |
3369 | int sres_resolver_error(sres_resolver_t *res, int socket) |
3370 | { |
3371 | int errcode = 0; |
3372 | socklen_t errorlen = sizeof(errcode); |
3373 | |
3374 | SU_DEBUG_9(("%s(%p, %u) called\n", "sres_resolver_error",(((sresolv_log != ((void*)0) && sresolv_log->log_init ) == 0 ? 9 : ((sresolv_log != ((void*)0) && sresolv_log ->log_init > 1) ? sresolv_log->log_level : su_log_default ->log_level)) >= 9 ? (_su_llog(sresolv_log, 9, "sres.c" , (const char *)__func__, 3375, "%s(%p, %u) called\n", "sres_resolver_error" , (void *)res, socket)) : (void)0) |
3375 | (void *)res, socket))(((sresolv_log != ((void*)0) && sresolv_log->log_init ) == 0 ? 9 : ((sresolv_log != ((void*)0) && sresolv_log ->log_init > 1) ? sresolv_log->log_level : su_log_default ->log_level)) >= 9 ? (_su_llog(sresolv_log, 9, "sres.c" , (const char *)__func__, 3375, "%s(%p, %u) called\n", "sres_resolver_error" , (void *)res, socket)) : (void)0); |
3376 | |
3377 | getsockopt(socket, SOL_SOCKET1, SO_ERROR4, (void *)&errcode, &errorlen); |
3378 | |
3379 | return sres_resolver_report_error(res, socket, errcode, NULL((void*)0), 0, ""); |
3380 | } |
3381 | #endif |
3382 | |
3383 | |
3384 | /** Report error */ |
3385 | static |
3386 | int |
3387 | sres_resolver_report_error(sres_resolver_t *res, |
3388 | sres_socket_t socket, |
3389 | int errcode, |
3390 | struct sockaddr_storage *remote, |
3391 | socklen_t remotelen, |
3392 | char const *info) |
3393 | { |
3394 | char buf[80]; |
3395 | |
3396 | buf[0] = '\0'; |
3397 | |
3398 | if (remote) { |
3399 | sres_canonize_sockaddr(remote, &remotelen); |
3400 | |
3401 | if (remote->ss_family == AF_INET2) { |
3402 | struct sockaddr_in const *sin = (struct sockaddr_in *)remote; |
3403 | uint8_t const *in_addr = (uint8_t*)&sin->sin_addr; |
3404 | su_inet_ntopinet_ntop(AF_INET2, in_addr, buf, sizeof(buf)); |
3405 | } |
3406 | #if HAVE_SIN61 |
3407 | else if (remote->ss_family == AF_INET610) { |
3408 | struct sockaddr_in6 const *sin6 = (struct sockaddr_in6 *)remote; |
3409 | uint8_t const *in_addr = (uint8_t*)&sin6->sin6_addr; |
3410 | su_inet_ntopinet_ntop(AF_INET610, in_addr, buf, sizeof(buf)); |
3411 | } |
3412 | #endif |
3413 | } |
3414 | |
3415 | SU_DEBUG_5(("sres: network error %u (%s)%s%s%s%s\n",(((sresolv_log != ((void*)0) && sresolv_log->log_init ) == 0 ? 9 : ((sresolv_log != ((void*)0) && sresolv_log ->log_init > 1) ? sresolv_log->log_level : su_log_default ->log_level)) >= 5 ? (_su_llog(sresolv_log, 5, "sres.c" , (const char *)__func__, 3419, "sres: network error %u (%s)%s%s%s%s\n" , errcode, su_strerror(errcode), buf[0] ? " from " : "", buf, info ? " by " : "", info ? info : "")) : (void)0) |
3416 | errcode, su_strerror(errcode),(((sresolv_log != ((void*)0) && sresolv_log->log_init ) == 0 ? 9 : ((sresolv_log != ((void*)0) && sresolv_log ->log_init > 1) ? sresolv_log->log_level : su_log_default ->log_level)) >= 5 ? (_su_llog(sresolv_log, 5, "sres.c" , (const char *)__func__, 3419, "sres: network error %u (%s)%s%s%s%s\n" , errcode, su_strerror(errcode), buf[0] ? " from " : "", buf, info ? " by " : "", info ? info : "")) : (void)0) |
3417 | buf[0] ? " from " : "", buf,(((sresolv_log != ((void*)0) && sresolv_log->log_init ) == 0 ? 9 : ((sresolv_log != ((void*)0) && sresolv_log ->log_init > 1) ? sresolv_log->log_level : su_log_default ->log_level)) >= 5 ? (_su_llog(sresolv_log, 5, "sres.c" , (const char *)__func__, 3419, "sres: network error %u (%s)%s%s%s%s\n" , errcode, su_strerror(errcode), buf[0] ? " from " : "", buf, info ? " by " : "", info ? info : "")) : (void)0) |
3418 | info ? " by " : "",(((sresolv_log != ((void*)0) && sresolv_log->log_init ) == 0 ? 9 : ((sresolv_log != ((void*)0) && sresolv_log ->log_init > 1) ? sresolv_log->log_level : su_log_default ->log_level)) >= 5 ? (_su_llog(sresolv_log, 5, "sres.c" , (const char *)__func__, 3419, "sres: network error %u (%s)%s%s%s%s\n" , errcode, su_strerror(errcode), buf[0] ? " from " : "", buf, info ? " by " : "", info ? info : "")) : (void)0) |
3419 | info ? info : ""))(((sresolv_log != ((void*)0) && sresolv_log->log_init ) == 0 ? 9 : ((sresolv_log != ((void*)0) && sresolv_log ->log_init > 1) ? sresolv_log->log_level : su_log_default ->log_level)) >= 5 ? (_su_llog(sresolv_log, 5, "sres.c" , (const char *)__func__, 3419, "sres: network error %u (%s)%s%s%s%s\n" , errcode, su_strerror(errcode), buf[0] ? " from " : "", buf, info ? " by " : "", info ? info : "")) : (void)0); |
3420 | |
3421 | if (res->res_queries->qt_used) { |
3422 | /* Report error to queries */ |
3423 | sres_server_t *dns; |
3424 | sres_query_t *q; |
3425 | size_t i; |
3426 | |
3427 | dns = sres_server_by_socket(res, socket); |
3428 | |
3429 | if (dns) { |
3430 | time(&res->res_now); |
3431 | dns->dns_icmp = res->res_now; |
3432 | |
3433 | for (i = 0; i < res->res_queries->qt_size; i++) { |
3434 | q = res->res_queries->qt_table[i]; |
3435 | |
3436 | if (!q || dns != res->res_servers[q->q_i_server]) |
3437 | continue; |
3438 | |
3439 | /* Resend query/report error to application */ |
3440 | sres_resend_dns_query(res, q, 0); |
3441 | |
3442 | if (q != res->res_queries->qt_table[i]) |
3443 | i--; |
3444 | } |
3445 | } |
3446 | } |
3447 | |
3448 | return 1; |
3449 | } |
3450 | |
3451 | |
3452 | /** Receive a response packet from socket. */ |
3453 | int |
3454 | sres_resolver_receive(sres_resolver_t *res, int socket) |
3455 | { |
3456 | ssize_t num_bytes; |
3457 | int error; |
3458 | sres_message_t m[1]; |
3459 | |
3460 | sres_query_t *query = NULL((void*)0); |
3461 | sres_record_t **reply; |
3462 | sres_server_t *dns; |
3463 | |
3464 | struct sockaddr_storage from[1] = {{0}}; |
3465 | socklen_t fromlen = sizeof from; |
3466 | |
3467 | SU_DEBUG_9(("%s(%p, %u) called\n", "sres_resolver_receive",(((sresolv_log != ((void*)0) && sresolv_log->log_init ) == 0 ? 9 : ((sresolv_log != ((void*)0) && sresolv_log ->log_init > 1) ? sresolv_log->log_level : su_log_default ->log_level)) >= 9 ? (_su_llog(sresolv_log, 9, "sres.c" , (const char *)__func__, 3468, "%s(%p, %u) called\n", "sres_resolver_receive" , (void *)res, socket)) : (void)0) |
3468 | (void *)res, socket))(((sresolv_log != ((void*)0) && sresolv_log->log_init ) == 0 ? 9 : ((sresolv_log != ((void*)0) && sresolv_log ->log_init > 1) ? sresolv_log->log_level : su_log_default ->log_level)) >= 9 ? (_su_llog(sresolv_log, 9, "sres.c" , (const char *)__func__, 3468, "%s(%p, %u) called\n", "sres_resolver_receive" , (void *)res, socket)) : (void)0); |
3469 | |
3470 | memset(m, 0, offsetof(sres_message_t, m_data)__builtin_offsetof(sres_message_t, m_packet.mp_data)); |
3471 | |
3472 | num_bytes = sres_recvfrom(socket, m->m_data, sizeof (m->m_data), 0,recvfrom((socket),(m->m_packet.mp_data),(sizeof (m->m_packet .mp_data)),(0),((void *)from),(&fromlen)) |
3473 | (void *)from, &fromlen)recvfrom((socket),(m->m_packet.mp_data),(sizeof (m->m_packet .mp_data)),(0),((void *)from),(&fromlen)); |
3474 | |
3475 | if (num_bytes <= 0) { |
3476 | SU_DEBUG_5(("%s: %s\n", "sres_resolver_receive", su_strerror(su_errno())))(((sresolv_log != ((void*)0) && sresolv_log->log_init ) == 0 ? 9 : ((sresolv_log != ((void*)0) && sresolv_log ->log_init > 1) ? sresolv_log->log_level : su_log_default ->log_level)) >= 5 ? (_su_llog(sresolv_log, 5, "sres.c" , (const char *)__func__, 3476, "%s: %s\n", "sres_resolver_receive" , su_strerror(su_errno()))) : (void)0); |
3477 | return 0; |
3478 | } |
3479 | |
3480 | if (num_bytes > 65535) |
3481 | num_bytes = 65535; |
3482 | |
3483 | dns = sres_server_by_socket(res, socket); |
3484 | if (!dns) |
3485 | return 0; |
3486 | |
3487 | m->m_size = (uint16_t)num_bytes; |
3488 | |
3489 | /* Decode the received message and get the matching query object */ |
3490 | error = sres_decode_msg(res, m, &query, &reply); |
3491 | |
3492 | sres_log_response(res, m, from, query, reply); |
3493 | |
3494 | if (query == NULL((void*)0)) |
3495 | ; |
3496 | else if (error == SRES_EDNS0_ERR) { |
3497 | dns->dns_edns = edns_not_supported; |
3498 | assert(query->q_id)((void) sizeof ((query->q_id) ? 1 : 0), __extension__ ({ if (query->q_id) ; else __assert_fail ("query->q_id", "sres.c" , 3498, __extension__ __PRETTY_FUNCTION__); })); |
3499 | sres_remove_query(res, query, 0); |
3500 | sres_gen_id(res, query); |
3501 | sres_qtable_append(res->res_queries, query); |
3502 | sres_send_dns_query(res, query); |
3503 | query->q_retry_count++; |
3504 | } |
3505 | else if (!error && reply) { |
3506 | /* Remove the query from the pending list */ |
3507 | sres_remove_query(res, query, 1); |
3508 | |
3509 | /* Resolve the CNAME alias, if necessary */ |
3510 | if (query->q_type != sres_type_cname && query->q_type != sres_qtype_any && |
3511 | reply[0] && reply[0]->sr_typesr_record->r_type == sres_type_cname) { |
3512 | const char *alias = reply[0]->sr_cname[0].cn_cname; |
3513 | sres_record_t **cached = NULL((void*)0); |
3514 | |
3515 | /* Check for the aliased results in the cache */ |
3516 | if (sres_cache_get(res->res_cache, query->q_type, alias, &cached) |
3517 | > 0) { |
3518 | reply = cached; |
3519 | } |
3520 | else { |
3521 | /* Submit a query with the aliased name, dropping this result */ |
3522 | sres_resolve_cname(res, query, alias); |
3523 | return 1; |
3524 | } |
3525 | } |
3526 | |
3527 | /* Notify the listener */ |
3528 | if (query->q_callback != NULL((void*)0)) |
3529 | (query->q_callback)(query->q_context, query, reply); |
3530 | |
3531 | sres_free_query(res, query); |
3532 | } |
3533 | else { |
3534 | sres_query_report_error(query, reply); |
3535 | } |
3536 | |
3537 | return 1; |
3538 | } |
3539 | |
3540 | static |
3541 | void sres_log_response(sres_resolver_t const *res, |
3542 | sres_message_t const *m, |
3543 | struct sockaddr_storage const *from, |
3544 | sres_query_t const *query, |
3545 | sres_record_t * const *reply) |
3546 | { |
3547 | if (SU_LOGsresolv_log->log_level >= 5) { |
3548 | #ifndef ADDRSIZE48 |
3549 | #define ADDRSIZE48 48 |
3550 | #endif |
3551 | char host[ADDRSIZE48] = "*"; |
3552 | uint16_t port = 0; |
3553 | |
3554 | if (from == NULL((void*)0)) |
3555 | ; |
3556 | else if (from->ss_family == AF_INET2) { |
3557 | struct sockaddr_in sin; |
3558 | memcpy(&sin, from, sizeof sin); |
3559 | su_inet_ntopinet_ntop(AF_INET2, &sin.sin_addr, host, sizeof host); |
3560 | port = sin.sin_port; |
3561 | } |
3562 | #if HAVE_SIN61 |
3563 | else if (from->ss_family == AF_INET610) { |
3564 | struct sockaddr_in6 sin6; |
3565 | memcpy(&sin6, from, sizeof sin6); |
3566 | su_inet_ntopinet_ntop(AF_INET610, &sin6.sin6_addr, host, sizeof host); |
3567 | port = sin6.sin6_port; |
3568 | } |
3569 | #endif |
3570 | |
3571 | SU_DEBUG_5(("sres_resolver_receive(%p, %p) id=%u (from [%s]:%u)\n",(((sresolv_log != ((void*)0) && sresolv_log->log_init ) == 0 ? 9 : ((sresolv_log != ((void*)0) && sresolv_log ->log_init > 1) ? sresolv_log->log_level : su_log_default ->log_level)) >= 5 ? (_su_llog(sresolv_log, 5, "sres.c" , (const char *)__func__, 3573, "sres_resolver_receive(%p, %p) id=%u (from [%s]:%u)\n" , (void *)res, (void *)query, m->m_packet.mp_header.mh_id, host, ntohs(port))) : (void)0) |
3572 | (void *)res, (void *)query, m->m_id,(((sresolv_log != ((void*)0) && sresolv_log->log_init ) == 0 ? 9 : ((sresolv_log != ((void*)0) && sresolv_log ->log_init > 1) ? sresolv_log->log_level : su_log_default ->log_level)) >= 5 ? (_su_llog(sresolv_log, 5, "sres.c" , (const char *)__func__, 3573, "sres_resolver_receive(%p, %p) id=%u (from [%s]:%u)\n" , (void *)res, (void *)query, m->m_packet.mp_header.mh_id, host, ntohs(port))) : (void)0) |
3573 | host, ntohs(port)))(((sresolv_log != ((void*)0) && sresolv_log->log_init ) == 0 ? 9 : ((sresolv_log != ((void*)0) && sresolv_log ->log_init > 1) ? sresolv_log->log_level : su_log_default ->log_level)) >= 5 ? (_su_llog(sresolv_log, 5, "sres.c" , (const char *)__func__, 3573, "sres_resolver_receive(%p, %p) id=%u (from [%s]:%u)\n" , (void *)res, (void *)query, m->m_packet.mp_header.mh_id, host, ntohs(port))) : (void)0); |
3574 | } |
3575 | } |
3576 | |
3577 | /** Decode DNS message. |
3578 | * |
3579 | * |
3580 | * @retval 0 if successful |
3581 | * @retval >0 if message indicated error |
3582 | * @retval -1 if decoding error |
3583 | */ |
3584 | static |
3585 | int |
3586 | sres_decode_msg(sres_resolver_t *res, |
3587 | sres_message_t *m, |
3588 | sres_query_t **qq, |
3589 | sres_record_t ***return_answers) |
3590 | { |
3591 | sres_record_t *rr = NULL((void*)0), **answers = NULL((void*)0), *error = NULL((void*)0); |
3592 | sres_query_t *query = NULL((void*)0), **hq; |
3593 | su_home_t *chome; |
3594 | hash_value_t hash; |
3595 | int err; |
3596 | unsigned i, total, errorcount = 0; |
3597 | |
3598 | assert(res && m && return_answers)((void) sizeof ((res && m && return_answers) ? 1 : 0), __extension__ ({ if (res && m && return_answers ) ; else __assert_fail ("res && m && return_answers" , "sres.c", 3598, __extension__ __PRETTY_FUNCTION__); })); |
3599 | |
3600 | time(&res->res_now); |
3601 | chome = CHOME(res->res_cache)((su_home_t *)(res->res_cache)); |
3602 | |
3603 | *qq = NULL((void*)0); |
3604 | *return_answers = NULL((void*)0); |
3605 | |
3606 | m->m_offset = sizeof(m->m_packet.mp_header); |
3607 | |
3608 | if (m->m_size < m->m_offset) { |
3609 | SU_DEBUG_5(("sres_decode_msg: truncated message\n" VA_NONE))(((sresolv_log != ((void*)0) && sresolv_log->log_init ) == 0 ? 9 : ((sresolv_log != ((void*)0) && sresolv_log ->log_init > 1) ? sresolv_log->log_level : su_log_default ->log_level)) >= 5 ? (_su_llog(sresolv_log, 5, "sres.c" , (const char *)__func__, 3609, "sres_decode_msg: truncated message\n" "%s", "")) : (void)0); |
3610 | return -1; |
3611 | } |
3612 | |
3613 | m->m_flagsm_packet.mp_header.mh_flags = ntohs(m->m_flagsm_packet.mp_header.mh_flags); |
3614 | m->m_qdcountm_packet.mp_header.mh_qdcount = ntohs(m->m_qdcountm_packet.mp_header.mh_qdcount); |
3615 | m->m_ancountm_packet.mp_header.mh_ancount = ntohs(m->m_ancountm_packet.mp_header.mh_ancount); |
3616 | m->m_nscountm_packet.mp_header.mh_nscount = ntohs(m->m_nscountm_packet.mp_header.mh_nscount); |
3617 | m->m_arcountm_packet.mp_header.mh_arcount = ntohs(m->m_arcountm_packet.mp_header.mh_arcount); |
3618 | |
3619 | hash = Q_PRIME3571 * m->m_idm_packet.mp_header.mh_id; |
3620 | |
3621 | /* Search for query with this ID */ |
3622 | for (hq = sres_qtable_hash(res->res_queries, hash); |
3623 | *hq; |
3624 | hq = sres_qtable_next(res->res_queries, hq)) |
3625 | if (hash == (*hq)->q_hash) |
3626 | break; |
3627 | |
3628 | *qq = query = *hq; |
3629 | |
3630 | if (!query) { |
3631 | SU_DEBUG_5(("sres_decode_msg: matching query for id=%u\n", m->m_id))(((sresolv_log != ((void*)0) && sresolv_log->log_init ) == 0 ? 9 : ((sresolv_log != ((void*)0) && sresolv_log ->log_init > 1) ? sresolv_log->log_level : su_log_default ->log_level)) >= 5 ? (_su_llog(sresolv_log, 5, "sres.c" , (const char *)__func__, 3631, "sres_decode_msg: matching query for id=%u\n" , m->m_packet.mp_header.mh_id)) : (void)0); |
3632 | return -1; |
3633 | } |
3634 | |
3635 | assert(query && m->m_id == query->q_id)((void) sizeof ((query && m->m_packet.mp_header.mh_id == query->q_id) ? 1 : 0), __extension__ ({ if (query && m->m_packet.mp_header.mh_id == query->q_id) ; else __assert_fail ("query && m->m_id == query->q_id", "sres.c", 3635 , __extension__ __PRETTY_FUNCTION__); })); |
3636 | |
3637 | if ((m->m_flagsm_packet.mp_header.mh_flags & 15) == SRES_FORMAT_ERR && query->q_edns) |
3638 | return SRES_EDNS0_ERR; |
3639 | |
3640 | /* Scan question section. |
3641 | * XXX: never mind the useless result values, this is done |
3642 | * for the side effects in m */ |
3643 | for (i = 0; i < m->m_qdcountm_packet.mp_header.mh_qdcount; i++) { |
3644 | char name[1024]; |
3645 | uint16_t qtype, qclass; |
3646 | m_get_domain(name, sizeof(name), m, 0); /* Query domain */ |
3647 | qtype = m_get_uint16(m); /* Query type */ |
3648 | qclass = m_get_uint16(m); /* Query class */ |
3649 | if (qtype && qclass) { |
3650 | /* XXX: never mind these useless check, this is done to make compiler happy about unused value */ |
3651 | } |
3652 | |
3653 | } |
3654 | |
3655 | if (m->m_error) { |
3656 | SU_DEBUG_5(("sres_decode_msg: %s\n", m->m_error))(((sresolv_log != ((void*)0) && sresolv_log->log_init ) == 0 ? 9 : ((sresolv_log != ((void*)0) && sresolv_log ->log_init > 1) ? sresolv_log->log_level : su_log_default ->log_level)) >= 5 ? (_su_llog(sresolv_log, 5, "sres.c" , (const char *)__func__, 3656, "sres_decode_msg: %s\n", m-> m_error)) : (void)0); |
3657 | return -1; |
3658 | } |
3659 | |
3660 | err = m->m_flagsm_packet.mp_header.mh_flags & SRES_HDR_RCODE; |
3661 | |
3662 | if (m->m_ancountm_packet.mp_header.mh_ancount == 0 && err == 0) |
3663 | err = SRES_RECORD_ERR; |
3664 | |
3665 | if (err == SRES_RECORD_ERR || |
3666 | err == SRES_NAME_ERR || |
3667 | err == SRES_UNIMPL_ERR || |
3668 | err == SRES_AUTH_ERR) |
3669 | errorcount = 1; |
3670 | |
3671 | total = errorcount + m->m_ancountm_packet.mp_header.mh_ancount + m->m_nscountm_packet.mp_header.mh_nscount + m->m_arcountm_packet.mp_header.mh_arcount; |
3672 | |
3673 | answers = su_zalloc(chome, (total + 2) * sizeof answers[0]); |
3674 | if (!answers) |
3675 | return -1; |
3676 | |
3677 | /* Scan resource records */ |
3678 | for (i = 0; i < total; i++) { |
3679 | if (i < errorcount) |
3680 | rr = error = sres_create_error_rr(res->res_cache, query, err); |
3681 | else |
3682 | rr = sres_create_record(res, m, i - errorcount); |
3683 | |
3684 | if (!rr) { |
3685 | SU_DEBUG_5(("sres_create_record: %s\n", m->m_error))(((sresolv_log != ((void*)0) && sresolv_log->log_init ) == 0 ? 9 : ((sresolv_log != ((void*)0) && sresolv_log ->log_init > 1) ? sresolv_log->log_level : su_log_default ->log_level)) >= 5 ? (_su_llog(sresolv_log, 5, "sres.c" , (const char *)__func__, 3685, "sres_create_record: %s\n", m ->m_error)) : (void)0); |
3686 | break; |
3687 | } |
3688 | |
3689 | if (error && rr->sr_typesr_record->r_type == sres_type_soa) { |
3690 | sres_soa_record_t *soa = (sres_soa_record_t *)rr; |
3691 | if (error->sr_ttlsr_record->r_ttl > soa->soa_minimum && soa->soa_minimum > 10) |
3692 | error->sr_ttlsr_record->r_ttl = soa->soa_minimum; |
3693 | } |
3694 | |
3695 | answers[i] = rr; |
3696 | } |
3697 | |
3698 | if (i < total) { |
3699 | SU_DEBUG_5(("sres_decode_msg: got %u but expected "(((sresolv_log != ((void*)0) && sresolv_log->log_init ) == 0 ? 9 : ((sresolv_log != ((void*)0) && sresolv_log ->log_init > 1) ? sresolv_log->log_level : su_log_default ->log_level)) >= 5 ? (_su_llog(sresolv_log, 5, "sres.c" , (const char *)__func__, 3701, "sres_decode_msg: got %u but expected " "errors=%u an=%u ar=%u ns=%u\n", i, errorcount, m->m_packet .mp_header.mh_ancount, m->m_packet.mp_header.mh_arcount, m ->m_packet.mp_header.mh_nscount)) : (void)0) |
3700 | "errors=%u an=%u ar=%u ns=%u\n", i, errorcount,(((sresolv_log != ((void*)0) && sresolv_log->log_init ) == 0 ? 9 : ((sresolv_log != ((void*)0) && sresolv_log ->log_init > 1) ? sresolv_log->log_level : su_log_default ->log_level)) >= 5 ? (_su_llog(sresolv_log, 5, "sres.c" , (const char *)__func__, 3701, "sres_decode_msg: got %u but expected " "errors=%u an=%u ar=%u ns=%u\n", i, errorcount, m->m_packet .mp_header.mh_ancount, m->m_packet.mp_header.mh_arcount, m ->m_packet.mp_header.mh_nscount)) : (void)0) |
3701 | m->m_ancount, m->m_arcount, m->m_nscount))(((sresolv_log != ((void*)0) && sresolv_log->log_init ) == 0 ? 9 : ((sresolv_log != ((void*)0) && sresolv_log ->log_init > 1) ? sresolv_log->log_level : su_log_default ->log_level)) >= 5 ? (_su_llog(sresolv_log, 5, "sres.c" , (const char *)__func__, 3701, "sres_decode_msg: got %u but expected " "errors=%u an=%u ar=%u ns=%u\n", i, errorcount, m->m_packet .mp_header.mh_ancount, m->m_packet.mp_header.mh_arcount, m ->m_packet.mp_header.mh_nscount)) : (void)0); |
3702 | for (i = 0; i < total; i++) |
3703 | sres_cache_free_record(res->res_cache, answers[i]); |
3704 | su_free(chome, answers); |
3705 | return -1; |
3706 | } |
3707 | |
3708 | if (m->m_ancountm_packet.mp_header.mh_ancount > 0 && errorcount == 0 && query->q_type < sres_qtype_tsig |
3709 | && (query->q_callback == sres_resolving_cname || |
3710 | answers[0]->sr_typesr_record->r_type != sres_type_cname)) { |
3711 | |
3712 | for (i = 0; i < m->m_ancountm_packet.mp_header.mh_ancount; i++) { |
3713 | if (query->q_type == answers[i]->sr_typesr_record->r_type) |
3714 | break; |
3715 | } |
3716 | |
3717 | if (i == m->m_ancountm_packet.mp_header.mh_ancount) { |
3718 | char b0[8], b1[8]; |
3719 | /* The queried request was not found */ |
3720 | SU_DEBUG_5(("sres_decode_msg: sent query %s, got %s\n",(((sresolv_log != ((void*)0) && sresolv_log->log_init ) == 0 ? 9 : ((sresolv_log != ((void*)0) && sresolv_log ->log_init > 1) ? sresolv_log->log_level : su_log_default ->log_level)) >= 5 ? (_su_llog(sresolv_log, 5, "sres.c" , (const char *)__func__, 3722, "sres_decode_msg: sent query %s, got %s\n" , sres_record_type(query->q_type, b0), sres_record_type(answers [0]->sr_record->r_type, b1))) : (void)0) |
3721 | sres_record_type(query->q_type, b0),(((sresolv_log != ((void*)0) && sresolv_log->log_init ) == 0 ? 9 : ((sresolv_log != ((void*)0) && sresolv_log ->log_init > 1) ? sresolv_log->log_level : su_log_default ->log_level)) >= 5 ? (_su_llog(sresolv_log, 5, "sres.c" , (const char *)__func__, 3722, "sres_decode_msg: sent query %s, got %s\n" , sres_record_type(query->q_type, b0), sres_record_type(answers [0]->sr_record->r_type, b1))) : (void)0) |
3722 | sres_record_type(answers[0]->sr_type, b1)))(((sresolv_log != ((void*)0) && sresolv_log->log_init ) == 0 ? 9 : ((sresolv_log != ((void*)0) && sresolv_log ->log_init > 1) ? sresolv_log->log_level : su_log_default ->log_level)) >= 5 ? (_su_llog(sresolv_log, 5, "sres.c" , (const char *)__func__, 3722, "sres_decode_msg: sent query %s, got %s\n" , sres_record_type(query->q_type, b0), sres_record_type(answers [0]->sr_record->r_type, b1))) : (void)0); |
3723 | rr = sres_create_error_rr(res->res_cache, query, err = SRES_RECORD_ERR); |
3724 | memmove(answers + 1, answers, (sizeof answers[0]) * total++); |
3725 | answers[0] = rr; |
3726 | errorcount = 1; |
3727 | } |
3728 | } |
3729 | |
3730 | for (i = 0; i < total; i++) { |
3731 | rr = answers[i]; |
3732 | |
3733 | if (i < m->m_ancountm_packet.mp_header.mh_ancount + errorcount) |
3734 | /* Increase reference count of entry passed in answers */ |
3735 | rr->sr_refcountsr_record->r_refcount++; |
3736 | else |
3737 | /* Do not pass extra records to user */ |
3738 | answers[i] = NULL((void*)0); |
3739 | |
3740 | sres_cache_store(res->res_cache, rr, res->res_now); |
3741 | } |
3742 | |
3743 | *return_answers = answers; |
3744 | |
3745 | return err; |
3746 | } |
3747 | |
3748 | static |
3749 | sres_record_t * |
3750 | sres_create_record(sres_resolver_t *res, sres_message_t *m, int nth) |
3751 | { |
3752 | sres_cache_t *cache = res->res_cache; |
3753 | sres_record_t *sr, sr0[1]; |
3754 | |
3755 | uint16_t m_size; |
3756 | char name[1025]; |
3757 | unsigned len; |
3758 | char btype[8], bclass[8]; |
3759 | |
3760 | sr = memset(sr0, 0, sizeof sr0); |
3761 | |
3762 | len = m_get_domain(sr->sr_namesr_record->r_name = name, sizeof(name) - 1, m, 0); /* Name */ |
3763 | sr->sr_typesr_record->r_type = m_get_uint16(m); /* Type */ |
3764 | sr->sr_classsr_record->r_class = m_get_uint16(m); /* Class */ |
3765 | sr->sr_ttlsr_record->r_ttl = m_get_uint32(m); /* TTL */ |
3766 | sr->sr_rdlensr_record->r_rdlen = m_get_uint16(m); /* rdlength */ |
3767 | sr->sr_parsedsr_record->r_parsed = 1; |
3768 | if (m->m_error) |
3769 | goto error; |
3770 | if (len >= (sizeof name)) { |
3771 | m->m_error = "too long domain name in record"; |
3772 | goto error; |
3773 | } |
3774 | name[len] = 0; |
3775 | |
3776 | SU_DEBUG_9(("%s RR received %s %s %s %d rdlen=%d\n",(((sresolv_log != ((void*)0) && sresolv_log->log_init ) == 0 ? 9 : ((sresolv_log != ((void*)0) && sresolv_log ->log_init > 1) ? sresolv_log->log_level : su_log_default ->log_level)) >= 9 ? (_su_llog(sresolv_log, 9, "sres.c" , (const char *)__func__, 3783, "%s RR received %s %s %s %d rdlen=%d\n" , nth < m->m_packet.mp_header.mh_ancount ? "ANSWER" : nth < m->m_packet.mp_header.mh_ancount + m->m_packet.mp_header .mh_nscount ? "AUTHORITY" : "ADDITIONAL", name, sres_record_type (sr->sr_record->r_type, btype), sres_record_class(sr-> sr_record->r_class, bclass), sr->sr_record->r_ttl, sr ->sr_record->r_rdlen)) : (void)0) |
3777 | nth < m->m_ancount ? "ANSWER" :(((sresolv_log != ((void*)0) && sresolv_log->log_init ) == 0 ? 9 : ((sresolv_log != ((void*)0) && sresolv_log ->log_init > 1) ? sresolv_log->log_level : su_log_default ->log_level)) >= 9 ? (_su_llog(sresolv_log, 9, "sres.c" , (const char *)__func__, 3783, "%s RR received %s %s %s %d rdlen=%d\n" , nth < m->m_packet.mp_header.mh_ancount ? "ANSWER" : nth < m->m_packet.mp_header.mh_ancount + m->m_packet.mp_header .mh_nscount ? "AUTHORITY" : "ADDITIONAL", name, sres_record_type (sr->sr_record->r_type, btype), sres_record_class(sr-> sr_record->r_class, bclass), sr->sr_record->r_ttl, sr ->sr_record->r_rdlen)) : (void)0) |
3778 | nth < m->m_ancount + m->m_nscount ? "AUTHORITY" :(((sresolv_log != ((void*)0) && sresolv_log->log_init ) == 0 ? 9 : ((sresolv_log != ((void*)0) && sresolv_log ->log_init > 1) ? sresolv_log->log_level : su_log_default ->log_level)) >= 9 ? (_su_llog(sresolv_log, 9, "sres.c" , (const char *)__func__, 3783, "%s RR received %s %s %s %d rdlen=%d\n" , nth < m->m_packet.mp_header.mh_ancount ? "ANSWER" : nth < m->m_packet.mp_header.mh_ancount + m->m_packet.mp_header .mh_nscount ? "AUTHORITY" : "ADDITIONAL", name, sres_record_type (sr->sr_record->r_type, btype), sres_record_class(sr-> sr_record->r_class, bclass), sr->sr_record->r_ttl, sr ->sr_record->r_rdlen)) : (void)0) |
3779 | "ADDITIONAL",(((sresolv_log != ((void*)0) && sresolv_log->log_init ) == 0 ? 9 : ((sresolv_log != ((void*)0) && sresolv_log ->log_init > 1) ? sresolv_log->log_level : su_log_default ->log_level)) >= 9 ? (_su_llog(sresolv_log, 9, "sres.c" , (const char *)__func__, 3783, "%s RR received %s %s %s %d rdlen=%d\n" , nth < m->m_packet.mp_header.mh_ancount ? "ANSWER" : nth < m->m_packet.mp_header.mh_ancount + m->m_packet.mp_header .mh_nscount ? "AUTHORITY" : "ADDITIONAL", name, sres_record_type (sr->sr_record->r_type, btype), sres_record_class(sr-> sr_record->r_class, bclass), sr->sr_record->r_ttl, sr ->sr_record->r_rdlen)) : (void)0) |
3780 | name,(((sresolv_log != ((void*)0) && sresolv_log->log_init ) == 0 ? 9 : ((sresolv_log != ((void*)0) && sresolv_log ->log_init > 1) ? sresolv_log->log_level : su_log_default ->log_level)) >= 9 ? (_su_llog(sresolv_log, 9, "sres.c" , (const char *)__func__, 3783, "%s RR received %s %s %s %d rdlen=%d\n" , nth < m->m_packet.mp_header.mh_ancount ? "ANSWER" : nth < m->m_packet.mp_header.mh_ancount + m->m_packet.mp_header .mh_nscount ? "AUTHORITY" : "ADDITIONAL", name, sres_record_type (sr->sr_record->r_type, btype), sres_record_class(sr-> sr_record->r_class, bclass), sr->sr_record->r_ttl, sr ->sr_record->r_rdlen)) : (void)0) |
3781 | sres_record_type(sr->sr_type, btype),(((sresolv_log != ((void*)0) && sresolv_log->log_init ) == 0 ? 9 : ((sresolv_log != ((void*)0) && sresolv_log ->log_init > 1) ? sresolv_log->log_level : su_log_default ->log_level)) >= 9 ? (_su_llog(sresolv_log, 9, "sres.c" , (const char *)__func__, 3783, "%s RR received %s %s %s %d rdlen=%d\n" , nth < m->m_packet.mp_header.mh_ancount ? "ANSWER" : nth < m->m_packet.mp_header.mh_ancount + m->m_packet.mp_header .mh_nscount ? "AUTHORITY" : "ADDITIONAL", name, sres_record_type (sr->sr_record->r_type, btype), sres_record_class(sr-> sr_record->r_class, bclass), sr->sr_record->r_ttl, sr ->sr_record->r_rdlen)) : (void)0) |
3782 | sres_record_class(sr->sr_class, bclass),(((sresolv_log != ((void*)0) && sresolv_log->log_init ) == 0 ? 9 : ((sresolv_log != ((void*)0) && sresolv_log ->log_init > 1) ? sresolv_log->log_level : su_log_default ->log_level)) >= 9 ? (_su_llog(sresolv_log, 9, "sres.c" , (const char *)__func__, 3783, "%s RR received %s %s %s %d rdlen=%d\n" , nth < m->m_packet.mp_header.mh_ancount ? "ANSWER" : nth < m->m_packet.mp_header.mh_ancount + m->m_packet.mp_header .mh_nscount ? "AUTHORITY" : "ADDITIONAL", name, sres_record_type (sr->sr_record->r_type, btype), sres_record_class(sr-> sr_record->r_class, bclass), sr->sr_record->r_ttl, sr ->sr_record->r_rdlen)) : (void)0) |
3783 | sr->sr_ttl, sr->sr_rdlen))(((sresolv_log != ((void*)0) && sresolv_log->log_init ) == 0 ? 9 : ((sresolv_log != ((void*)0) && sresolv_log ->log_init > 1) ? sresolv_log->log_level : su_log_default ->log_level)) >= 9 ? (_su_llog(sresolv_log, 9, "sres.c" , (const char *)__func__, 3783, "%s RR received %s %s %s %d rdlen=%d\n" , nth < m->m_packet.mp_header.mh_ancount ? "ANSWER" : nth < m->m_packet.mp_header.mh_ancount + m->m_packet.mp_header .mh_nscount ? "AUTHORITY" : "ADDITIONAL", name, sres_record_type (sr->sr_record->r_type, btype), sres_record_class(sr-> sr_record->r_class, bclass), sr->sr_record->r_ttl, sr ->sr_record->r_rdlen)) : (void)0); |
3784 | |
3785 | if (m->m_offset + sr->sr_rdlensr_record->r_rdlen > m->m_size) { |
3786 | m->m_error = "truncated message"; |
3787 | goto error; |
3788 | } |
3789 | |
3790 | m_size = m->m_size; |
3791 | /* limit m_size to indicated rdlen, check whether record is truncated */ |
3792 | m->m_size = m->m_offset + sr->sr_rdlensr_record->r_rdlen; |
3793 | |
3794 | switch (sr->sr_typesr_record->r_type) { |
3795 | case sres_type_soa: |
3796 | sr = sres_init_rr_soa(cache, sr->sr_soa, m); |
3797 | break; |
3798 | case sres_type_a: |
3799 | sr = sres_init_rr_a(cache, sr->sr_a, m); |
3800 | break; |
3801 | case sres_type_a6: |
3802 | sr = sres_init_rr_a6(cache, sr->sr_a6, m); |
3803 | break; |
3804 | case sres_type_aaaa: |
3805 | sr = sres_init_rr_aaaa(cache, sr->sr_aaaa, m); |
3806 | break; |
3807 | case sres_type_cname: |
3808 | sr = sres_init_rr_cname(cache, sr->sr_cname, m); |
3809 | break; |
3810 | case sres_type_ptr: |
3811 | sr = sres_init_rr_ptr(cache, sr->sr_ptr, m); |
3812 | break; |
3813 | case sres_type_srv: |
3814 | sr = sres_init_rr_srv(cache, sr->sr_srv, m); |
3815 | break; |
3816 | case sres_type_naptr: |
3817 | sr = sres_init_rr_naptr(cache, sr->sr_naptr, m); |
3818 | break; |
3819 | default: |
3820 | sr = sres_init_rr_unknown(cache, sr->sr_record, m); |
3821 | break; |
3822 | } |
3823 | |
3824 | if (m->m_error) |
3825 | goto error; |
3826 | |
3827 | if (sr == sr0) |
3828 | sr = sres_cache_alloc_record(cache, sr, 0); |
3829 | |
3830 | if (sr == NULL((void*)0)) { |
3831 | m->m_error = "memory exhausted"; |
3832 | goto error; |
3833 | } |
3834 | |
3835 | /* Fill in the common fields */ |
3836 | m->m_size = m_size; |
3837 | |
3838 | return sr; |
3839 | |
3840 | error: |
3841 | if (sr && sr != sr0) |
3842 | sres_cache_free_record(cache, sr); |
3843 | SU_DEBUG_5(("%s: %s\n", "sres_create_record", m->m_error))(((sresolv_log != ((void*)0) && sresolv_log->log_init ) == 0 ? 9 : ((sresolv_log != ((void*)0) && sresolv_log ->log_init > 1) ? sresolv_log->log_level : su_log_default ->log_level)) >= 5 ? (_su_llog(sresolv_log, 5, "sres.c" , (const char *)__func__, 3843, "%s: %s\n", "sres_create_record" , m->m_error)) : (void)0); |
3844 | return NULL((void*)0); |
3845 | } |
3846 | |
3847 | /** Decode SOA record */ |
3848 | static sres_record_t *sres_init_rr_soa(sres_cache_t *cache, |
3849 | sres_soa_record_t *soa, |
3850 | sres_message_t *m) |
3851 | { |
3852 | uint16_t moffset, roffset; |
3853 | unsigned mnamelen, rnamelen; |
3854 | |
3855 | soa->soa_record->r_size = sizeof *soa; |
3856 | |
3857 | moffset = m->m_offset, mnamelen = m_get_domain(NULL((void*)0), 0, m, 0) + 1; |
3858 | roffset = m->m_offset, rnamelen = m_get_domain(NULL((void*)0), 0, m, 0) + 1; |
3859 | |
3860 | soa->soa_serial = m_get_uint32(m); |
3861 | soa->soa_refresh = m_get_uint32(m); |
3862 | soa->soa_retry = m_get_uint32(m); |
3863 | soa->soa_expire = m_get_uint32(m); |
3864 | soa->soa_minimum = m_get_uint32(m); |
3865 | |
3866 | if (m->m_error) |
3867 | return NULL((void*)0); |
3868 | |
3869 | soa = (void *)sres_cache_alloc_record(cache, (void *)soa, |
3870 | mnamelen + rnamelen); |
3871 | |
3872 | if (soa) { |
3873 | char *mname, *rname; |
3874 | |
3875 | assert(moffset > 0 && roffset > 0 && mnamelen > 1 && rnamelen > 1)((void) sizeof ((moffset > 0 && roffset > 0 && mnamelen > 1 && rnamelen > 1) ? 1 : 0), __extension__ ({ if (moffset > 0 && roffset > 0 && mnamelen > 1 && rnamelen > 1) ; else __assert_fail ("moffset > 0 && roffset > 0 && mnamelen > 1 && rnamelen > 1" , "sres.c", 3875, __extension__ __PRETTY_FUNCTION__); })); |
3876 | |
3877 | m_get_domain(mname = (char *)(soa + 1), mnamelen, m, moffset); |
3878 | soa->soa_mname = mname; |
3879 | |
3880 | m_get_domain(rname = mname + mnamelen, rnamelen, m, roffset); |
3881 | soa->soa_rname = rname; |
3882 | } |
3883 | |
3884 | return (sres_record_t *)soa; |
3885 | } |
3886 | |
3887 | /** Decode A record */ |
3888 | static sres_record_t *sres_init_rr_a(sres_cache_t *cache, |
3889 | sres_a_record_t *a, |
3890 | sres_message_t *m) |
3891 | { |
3892 | a->a_record->r_size = sizeof *a; |
3893 | |
3894 | a->a_addr.s_addr = htonl(m_get_uint32(m)); |
3895 | |
3896 | return (sres_record_t *)a; |
3897 | } |
3898 | |
3899 | /** Decode A6 record. See @RFC2874 */ |
3900 | static sres_record_t *sres_init_rr_a6(sres_cache_t *cache, |
3901 | sres_a6_record_t *a6, |
3902 | sres_message_t *m) |
3903 | { |
3904 | |
3905 | unsigned suffixlen = 0, i; |
3906 | unsigned prefixlen = 0; |
3907 | uint16_t offset; |
3908 | |
3909 | a6->a6_record->r_size = sizeof *a6; |
3910 | |
3911 | a6->a6_prelen = m_get_uint8(m); |
3912 | |
3913 | if (a6->a6_prelen > 128) { |
3914 | m->m_error = "Invalid prefix length in A6 record"; |
3915 | return NULL((void*)0); |
3916 | } |
3917 | |
3918 | suffixlen = (128 + 7 - a6->a6_prelen) / 8; |
3919 | for (i = 16 - suffixlen; i < 16; i++) |
3920 | a6->a6_suffix.u6_addr[i] = m_get_uint8(m); |
3921 | |
3922 | if (a6->a6_prelen > 0) { |
3923 | if (suffixlen > 0) |
3924 | /* Zero pad bits */ |
3925 | a6->a6_suffix.u6_addr[16 - suffixlen] &= 0xff >> (a6->a6_prelen & 7); |
3926 | |
3927 | offset = m->m_offset, prefixlen = m_get_domain(NULL((void*)0), 0, m, 0) + 1; |
3928 | |
3929 | if (m->m_error) |
3930 | return NULL((void*)0); |
3931 | |
3932 | a6 = (void *)sres_cache_alloc_record(cache, (void *)a6, prefixlen); |
3933 | if (a6) |
3934 | m_get_domain(a6->a6_prename = (char *)(a6 + 1), prefixlen, m, offset); |
3935 | } |
3936 | |
3937 | return (sres_record_t *)a6; |
3938 | } |
3939 | |
3940 | /** Decode AAAA record */ |
3941 | static sres_record_t *sres_init_rr_aaaa(sres_cache_t *cache, |
3942 | sres_aaaa_record_t *aaaa, |
3943 | sres_message_t *m) |
3944 | { |
3945 | aaaa->aaaa_record->r_size = sizeof *aaaa; |
3946 | |
3947 | if (m->m_offset + sizeof(aaaa->aaaa_addr) <= m->m_size) { |
3948 | memcpy(&aaaa->aaaa_addr, m->m_datam_packet.mp_data + m->m_offset, sizeof(aaaa->aaaa_addr)); |
3949 | m->m_offset += sizeof(aaaa->aaaa_addr); |
3950 | } |
3951 | else |
3952 | m->m_error = "truncated AAAA record"; |
3953 | |
3954 | return (sres_record_t *)aaaa; |
3955 | } |
3956 | |
3957 | /** Decode CNAME record */ |
3958 | static sres_record_t *sres_init_rr_cname(sres_cache_t *cache, |
3959 | sres_cname_record_t *cn, |
3960 | sres_message_t *m) |
3961 | { |
3962 | uint16_t offset; |
3963 | unsigned dlen; |
3964 | |
3965 | cn->cn_record->r_size = sizeof *cn; |
3966 | |
3967 | offset = m->m_offset, dlen = m_get_domain(NULL((void*)0), 0, m, 0) + 1; |
3968 | |
3969 | if (m->m_error) |
3970 | return NULL((void*)0); |
3971 | |
3972 | cn = (void *)sres_cache_alloc_record(cache, (void *)cn, dlen); |
3973 | if (cn) |
3974 | m_get_domain(cn->cn_cname = (char *)(cn + 1), dlen, m, offset); |
3975 | |
3976 | return (sres_record_t *)cn; |
3977 | } |
3978 | |
3979 | /** Decode PTR record */ |
3980 | static sres_record_t *sres_init_rr_ptr(sres_cache_t *cache, |
3981 | sres_ptr_record_t *ptr, |
3982 | sres_message_t *m) |
3983 | { |
3984 | uint16_t offset; |
3985 | unsigned dlen; |
3986 | |
3987 | ptr->ptr_record->r_size = sizeof *ptr; |
3988 | |
3989 | offset = m->m_offset, dlen = m_get_domain(NULL((void*)0), 0, m, 0) + 1; |
3990 | |
3991 | if (m->m_error) |
3992 | return NULL((void*)0); |
3993 | |
3994 | ptr = (void *)sres_cache_alloc_record(cache, (void *)ptr, dlen); |
3995 | if (ptr) |
3996 | m_get_domain(ptr->ptr_domain = (char *)(ptr + 1), dlen, m, offset); |
3997 | |
3998 | return (sres_record_t *)ptr; |
3999 | } |
4000 | |
4001 | /** Decode SRV record */ |
4002 | static sres_record_t *sres_init_rr_srv(sres_cache_t *cache, |
4003 | sres_srv_record_t *srv, |
4004 | sres_message_t *m) |
4005 | { |
4006 | uint16_t offset; |
4007 | unsigned dlen; |
4008 | |
4009 | srv->srv_record->r_size = sizeof *srv; |
4010 | |
4011 | srv->srv_priority = m_get_uint16(m); |
4012 | srv->srv_weight = m_get_uint16(m); |
4013 | srv->srv_port = m_get_uint16(m); |
4014 | offset = m->m_offset, dlen = m_get_domain(NULL((void*)0), 0, m, 0) + 1; |
4015 | if (m->m_error) |
4016 | return NULL((void*)0); |
4017 | |
4018 | srv = (void *)sres_cache_alloc_record(cache, (void *)srv, dlen); |
4019 | if (srv) |
4020 | m_get_domain(srv->srv_target = (char *)(srv + 1), dlen, m, offset); |
4021 | |
4022 | return (sres_record_t *)srv; |
4023 | } |
4024 | |
4025 | /** Decode NAPTR record */ |
4026 | static sres_record_t *sres_init_rr_naptr(sres_cache_t *cache, |
4027 | sres_naptr_record_t *na, |
4028 | sres_message_t *m) |
4029 | { |
4030 | uint16_t offset[4]; |
4031 | unsigned len[4]; |
4032 | |
4033 | na->na_record->r_size = sizeof *na; |
4034 | |
4035 | na->na_order = m_get_uint16(m); |
4036 | na->na_prefer = m_get_uint16(m); |
4037 | |
4038 | offset[0] = m->m_offset, len[0] = m_get_string(NULL((void*)0), 0, m, 0) + 1; |
4039 | offset[1] = m->m_offset, len[1] = m_get_string(NULL((void*)0), 0, m, 0) + 1; |
4040 | offset[2] = m->m_offset, len[2] = m_get_string(NULL((void*)0), 0, m, 0) + 1; |
4041 | offset[3] = m->m_offset, len[3] = m_get_domain(NULL((void*)0), 0, m, 0) + 1; |
4042 | |
4043 | if (m->m_error) |
4044 | return NULL((void*)0); |
4045 | |
4046 | na = (void *)sres_cache_alloc_record(cache, (void *)na, |
4047 | len[0] + len[1] + len[2] + len[3]); |
4048 | if (na) { |
4049 | char *s = (char *)(na + 1); |
4050 | m_get_string(na->na_flags = s, len[0], m, offset[0]), s += len[0]; |
4051 | m_get_string(na->na_services = s, len[1], m, offset[1]), s += len[1]; |
4052 | m_get_string(na->na_regexp = s, len[2], m, offset[2]), s += len[2]; |
4053 | m_get_domain(na->na_replace = s, len[3], m, offset[3]), s += len[3]; |
Although the value stored to 's' is used in the enclosing expression, the value is never actually read from 's' | |
4054 | } |
4055 | |
4056 | return (sres_record_t *)na; |
4057 | } |
4058 | |
4059 | /** Decode unknown record */ |
4060 | static sres_record_t *sres_init_rr_unknown(sres_cache_t *cache, |
4061 | sres_common_t *r, |
4062 | sres_message_t *m) |
4063 | { |
4064 | if (m->m_offset + r->r_rdlen > m->m_size) |
4065 | m->m_error = "truncated record"; |
4066 | |
4067 | if (m->m_error) |
4068 | return NULL((void*)0); |
4069 | |
4070 | r->r_size = sizeof *r; |
4071 | |
4072 | r = (void *)sres_cache_alloc_record(cache, (void *)r, r->r_rdlen + 1); |
4073 | if (r) { |
4074 | char *data = (char *)(r + 1); |
4075 | |
4076 | r->r_parsed = 0; |
4077 | |
4078 | memcpy(data, m->m_datam_packet.mp_data + m->m_offset, r->r_rdlen); |
4079 | m->m_offset += r->r_rdlen; |
4080 | data[r->r_rdlen] = 0; |
4081 | } |
4082 | |
4083 | return (sres_record_t *)r; |
4084 | } |
4085 | |
4086 | static |
4087 | sres_record_t *sres_create_error_rr(sres_cache_t *cache, |
4088 | sres_query_t const *q, |
4089 | uint16_t errcode) |
4090 | { |
4091 | sres_record_t *sr, r[1]; |
4092 | char buf[SRES_MAXDNAME(SRES_MAXDNAME)]; |
4093 | |
4094 | sr = memset(r, 0, sizeof *sr); |
4095 | |
4096 | sr->sr_namesr_record->r_name = (char *)sres_toplevel(buf, sizeof buf, q->q_name); |
4097 | sr->sr_sizesr_record->r_size = sizeof *sr; |
4098 | sr->sr_statussr_record->r_status = errcode; |
4099 | sr->sr_typesr_record->r_type = q->q_type; |
4100 | sr->sr_classsr_record->r_class = q->q_class; |
4101 | sr->sr_ttlsr_record->r_ttl = 10 * 60; |
4102 | |
4103 | return sres_cache_alloc_record(cache, sr, 0); |
4104 | } |
4105 | |
4106 | /* Message processing primitives */ |
4107 | |
4108 | static |
4109 | void |
4110 | m_put_uint16(sres_message_t *m, |
4111 | uint16_t h) |
4112 | { |
4113 | uint8_t *p; |
4114 | |
4115 | if (m->m_error) |
4116 | return; |
4117 | |
4118 | p = m->m_datam_packet.mp_data + m->m_offset; |
4119 | m->m_offset += sizeof h; |
4120 | |
4121 | if (m->m_offset > m->m_size) { |
4122 | m->m_error = "message size overflow"; |
4123 | return; |
4124 | } |
4125 | |
4126 | p[0] = h >> 8; p[1] = h; |
4127 | } |
4128 | |
4129 | static |
4130 | void |
4131 | m_put_uint32(sres_message_t *m, |
4132 | uint32_t w) |
4133 | { |
4134 | uint8_t *p; |
4135 | |
4136 | if (m->m_error) |
4137 | return; |
4138 | |
4139 | p = m->m_datam_packet.mp_data + m->m_offset; |
4140 | m->m_offset += sizeof w; |
4141 | |
4142 | if (m->m_offset > m->m_size) { |
4143 | m->m_error = "message size overflow"; |
4144 | return; |
4145 | } |
4146 | |
4147 | p[0] = w >> 24; p[1] = w >> 16; p[2] = w >> 8; p[3] = w; |
4148 | } |
4149 | |
4150 | /* |
4151 | * Put domain into query |
4152 | */ |
4153 | static |
4154 | uint16_t |
4155 | m_put_domain(sres_message_t *m, |
4156 | char const *domain, |
4157 | uint16_t top, |
4158 | char const *topdomain) |
4159 | { |
4160 | char const *label; |
4161 | size_t llen; |
4162 | |
4163 | if (m->m_error) |
4164 | return top; |
4165 | |
4166 | /* Copy domain into query label at a time */ |
4167 | for (label = domain; label && label[0]; label += llen) { |
4168 | if (label[0] == '.' && label[1] != '\0') { |
4169 | m->m_error = "empty label"; |
4170 | return 0; |
4171 | } |
4172 | |
4173 | llen = strcspn(label, "."); |
4174 | |
4175 | if (llen >= 64) { |
4176 | m->m_error = "too long label"; |
4177 | return 0; |
4178 | } |
4179 | if (m->m_offset + llen + 1 > m->m_size) { |
4180 | m->m_error = "message size overflow"; |
4181 | return 0; |
4182 | } |
4183 | |
4184 | m->m_datam_packet.mp_data[m->m_offset++] = (uint8_t)llen; |
4185 | memcpy(m->m_datam_packet.mp_data + m->m_offset, label, llen); |
4186 | m->m_offset += (uint8_t)llen; |
4187 | |
4188 | if (label[llen] == '\0') |
4189 | break; |
4190 | if (llen == 0) |
4191 | return top; |
4192 | if (label[llen + 1]) |
4193 | llen++; |
4194 | } |
4195 | |
4196 | if (top) { |
4197 | m_put_uint16(m, 0xc000 | top); |
4198 | return top; |
4199 | } |
4200 | else if (topdomain) { |
4201 | uint16_t retval = m->m_offset; |
4202 | m_put_domain(m, topdomain, 0, NULL((void*)0)); |
4203 | return retval; |
4204 | } |
4205 | else if (m->m_offset < m->m_size) |
4206 | m->m_datam_packet.mp_data[m->m_offset++] = '\0'; |
4207 | else |
4208 | m->m_error = "message size overflow"; |
4209 | |
4210 | return 0; |
4211 | } |
4212 | |
4213 | static |
4214 | uint32_t |
4215 | m_get_uint32(sres_message_t *m) |
4216 | { |
4217 | uint8_t const *p = m->m_datam_packet.mp_data + m->m_offset; |
4218 | |
4219 | if (m->m_error) |
4220 | return 0; |
4221 | |
4222 | m->m_offset += 4; |
4223 | |
4224 | if (m->m_offset > m->m_size) { |
4225 | m->m_error = "truncated message"; |
4226 | return 0; |
4227 | } |
4228 | |
4229 | return (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]; |
4230 | } |
4231 | |
4232 | static |
4233 | uint16_t |
4234 | m_get_uint16(sres_message_t *m) |
4235 | { |
4236 | uint8_t const *p = m->m_datam_packet.mp_data + m->m_offset; |
4237 | |
4238 | if (m->m_error) |
4239 | return 0; |
4240 | |
4241 | m->m_offset += 2; |
4242 | |
4243 | if (m->m_offset > m->m_size) { |
4244 | m->m_error = "truncated message"; |
4245 | return 0; |
4246 | } |
4247 | |
4248 | return (p[0] << 8) | p[1]; |
4249 | } |
4250 | |
4251 | static |
4252 | uint8_t |
4253 | m_get_uint8(sres_message_t *m) |
4254 | { |
4255 | uint8_t const *p = m->m_datam_packet.mp_data + m->m_offset; |
4256 | |
4257 | if (m->m_error) |
4258 | return 0; |
4259 | |
4260 | m->m_offset += 1; |
4261 | |
4262 | if (m->m_offset > m->m_size) { |
4263 | m->m_error = "truncated message"; |
4264 | return 0; |
4265 | } |
4266 | |
4267 | return p[0]; |
4268 | } |
4269 | |
4270 | /** |
4271 | * Get a string. |
4272 | */ |
4273 | static unsigned |
4274 | m_get_string(char *d, |
4275 | unsigned n, |
4276 | sres_message_t *m, |
4277 | uint16_t offset) |
4278 | { |
4279 | uint8_t size; |
4280 | uint8_t *p = m->m_datam_packet.mp_data; |
4281 | int save_offset; |
4282 | |
4283 | if (m->m_error) |
4284 | return 0; |
4285 | |
4286 | if (offset == 0) |
4287 | offset = m->m_offset, save_offset = 1; |
4288 | else |
4289 | save_offset = 0; |
4290 | |
4291 | size = p[offset++]; |
4292 | |
4293 | if (size + offset >= m->m_size) { |
4294 | m->m_error = "truncated message"; |
4295 | return size; |
4296 | } |
4297 | |
4298 | offset += size; |
4299 | |
4300 | if (save_offset) |
4301 | m->m_offset = offset; |
4302 | |
4303 | if (n == 0 || d == NULL((void*)0)) |
4304 | return size; /* Just return the size (without NUL). */ |
4305 | |
4306 | memcpy(d, p + offset - size, size < n ? size : n); |
4307 | |
4308 | if (size < n) |
4309 | d[size] = '\0'; /* NUL terminate */ |
4310 | |
4311 | return size; |
4312 | } |
4313 | |
4314 | /** |
4315 | * Uncompress a domain. |
4316 | * |
4317 | * @param offset start uncompression from this point in message |
4318 | */ |
4319 | static unsigned |
4320 | m_get_domain(char *d, |
4321 | unsigned n, |
4322 | sres_message_t *m, |
4323 | uint16_t offset) |
4324 | { |
4325 | uint8_t cnt; |
4326 | unsigned i = 0; |
4327 | uint8_t *p = m->m_datam_packet.mp_data; |
4328 | uint16_t new_offset; |
4329 | int save_offset; |
4330 | |
4331 | if (m->m_error) |
4332 | return 0; |
4333 | |
4334 | if (d == NULL((void*)0)) |
4335 | n = 0; |
4336 | |
4337 | if (offset == 0) |
4338 | offset = m->m_offset, save_offset = 1; |
4339 | else |
4340 | save_offset = 0; |
4341 | |
4342 | while ((cnt = p[offset++])) { |
4343 | if (cnt >= 0xc0) { |
4344 | if (offset >= m->m_size) { |
4345 | m->m_error = "truncated message"; |
4346 | return 0; |
4347 | } |
4348 | |
4349 | new_offset = ((cnt & 0x3F) << 8) + p[offset++]; |
4350 | |
4351 | if (save_offset) |
4352 | m->m_offset = offset; |
4353 | |
4354 | if (new_offset <= 0 || new_offset >= m->m_size) { |
4355 | m->m_error = "invalid domain compression"; |
4356 | return 0; |
4357 | } |
4358 | |
4359 | offset = new_offset; |
4360 | save_offset = 0; |
4361 | } |
4362 | else { |
4363 | if (offset + cnt >= m->m_size) { |
4364 | m->m_error = "truncated message"; |
4365 | return 0; |
4366 | } |
4367 | if (i + cnt + 1 < n) { |
4368 | memcpy(d + i, p + offset, cnt); |
4369 | d[i + cnt] = '.'; |
4370 | } |
4371 | |
4372 | i += cnt + 1; |
4373 | offset += cnt; |
4374 | } |
4375 | } |
4376 | |
4377 | if (i == 0) { |
4378 | if (i < n) |
4379 | d[i] = '.'; |
4380 | i++; |
4381 | } |
4382 | |
4383 | if (i < n) |
4384 | d[i] = '\0'; |
4385 | |
4386 | if (save_offset) |
4387 | m->m_offset = offset; |
4388 | |
4389 | return i; |
4390 | } |