-
Notifications
You must be signed in to change notification settings - Fork 178
/
sres.c
4389 lines (3637 loc) · 110 KB
/
sres.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/*
* This file is part of the Sofia-SIP package
*
* Copyright (C) 2006 Nokia Corporation.
* Copyright (C) 2006 Dimitri E. Prado.
*
* Contact: Pekka Pessi <pekka.pessi@nokia.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* as published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*
*/
/**@CFILE sres.c
* @brief Sofia DNS Resolver implementation.
*
* @author Pekka Pessi <Pekka.Pessi@nokia.com>
* @author Teemu Jalava <Teemu.Jalava@nokia.com>
* @author Mikko Haataja
* @author Kai Vehmanen <kai.vehmanen@nokia.com>
* (work on the win32 nameserver discovery)
* @author Dimitri E. Prado
* (initial version of win32 nameserver discovery)
*
* @todo The resolver should allow handling arbitrary records, too.
*/
#include "config.h"
#if HAVE_STDINT_H
#include <stdint.h>
#elif HAVE_INTTYPES_H
#include <inttypes.h>
#else
#if defined(HAVE_WIN32)
typedef _int8 int8_t;
typedef unsigned _int8 uint8_t;
typedef unsigned _int16 uint16_t;
typedef unsigned _int32 uint32_t;
#endif
#endif
#if HAVE_NETINET_IN_H
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#endif
#if HAVE_ARPA_INET_H
#include <arpa/inet.h>
#endif
#if HAVE_WINSOCK2_H
#include <winsock2.h>
#include <ws2tcpip.h>
#ifndef IPPROTO_IPV6 /* socklen_t is used with @RFC2133 API */
typedef int socklen_t;
#endif
#endif
#if HAVE_IPHLPAPI_H
#include <iphlpapi.h>
#endif
#if HAVE_IP_RECVERR || HAVE_IPV6_RECVERR
#include <linux/types.h>
#include <linux/errqueue.h>
#include <sys/uio.h>
#endif
#include <time.h>
#include "sofia-resolv/sres.h"
#include "sofia-resolv/sres_cache.h"
#include "sofia-resolv/sres_record.h"
#include "sofia-resolv/sres_async.h"
#include <sofia-sip/su_alloc.h>
#include <sofia-sip/su_strlst.h>
#include <sofia-sip/su_string.h>
#include <sofia-sip/su_errno.h>
#include "sofia-sip/htable.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdarg.h>
#include <stddef.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
#include <limits.h>
#include <assert.h>
#if HAVE_WINSOCK2_H
/* Posix send() */
su_inline
ssize_t sres_send(sres_socket_t s, void *b, size_t length, int flags)
{
if (length > INT_MAX)
length = INT_MAX;
return (ssize_t)send(s, b, (int)length, flags);
}
/* Posix recvfrom() */
su_inline
ssize_t sres_recvfrom(sres_socket_t s, void *buffer, size_t length, int flags,
struct sockaddr *from, socklen_t *fromlen)
{
int retval, ilen = 0;
if (fromlen)
ilen = *fromlen;
if (length > INT_MAX)
length = INT_MAX;
retval = recvfrom(s, buffer, (int)length, flags,
(void *)from, fromlen ? &ilen : NULL);
if (fromlen)
*fromlen = ilen;
return (ssize_t)retval;
}
su_inline
int sres_close(sres_socket_t s)
{
return closesocket(s);
}
#if !defined(IPPROTO_IPV6) && (_WIN32_WINNT < 0x0600)
#if HAVE_SIN6
#include <tpipv6.h>
#else
#if !defined(__MINGW32__)
struct sockaddr_storage {
short ss_family;
char ss_pad[126];
};
#endif
#endif
#endif
#else
#define sres_send(s,b,len,flags) send((s),(b),(len),(flags))
#define sres_recvfrom(s,b,len,flags,a,alen) \
recvfrom((s),(b),(len),(flags),(a),(alen))
#define sres_close(s) close((s))
#define SOCKET_ERROR (-1)
#define INVALID_SOCKET ((sres_socket_t)-1)
#endif
#define SRES_TIME_MAX ((time_t)LONG_MAX)
#if !HAVE_INET_PTON
int su_inet_pton(int af, char const *src, void *dst);
#else
#define su_inet_pton inet_pton
#endif
#if !HAVE_INET_NTOP
const char *su_inet_ntop(int af, void const *src, char *dst, size_t size);
#else
#define su_inet_ntop inet_ntop
#endif
#if defined(va_copy)
#elif defined(__va_copy)
#define va_copy(dst, src) __va_copy((dst), (src))
#else
#define va_copy(dst, src) (memcpy(&(dst), &(src), sizeof (va_list)))
#endif
/*
* 3571 is a prime =>
* we hash successive id values to different parts of hash tables
*/
#define Q_PRIME 3571
#define SRES_QUERY_HASH(q) ((q)->q_hash)
/**
* How often to recheck nameserver information (seconds).
*/
#ifndef HAVE_WIN32
#define SRES_UPDATE_INTERVAL_SECS 5
#else
#define SRES_UPDATE_INTERVAL_SECS 180
#endif
void sres_cache_clean(sres_cache_t *cache, time_t now);
typedef struct sres_message sres_message_t;
typedef struct sres_config sres_config_t;
typedef struct sres_server sres_server_t;
typedef struct sres_nameserver sres_nameserver_t;
/** Default path to resolv.conf */
static char const sres_conf_file_path[] = "/etc/resolv.conf";
/** EDNS0 support. @internal */
enum edns {
edns_not_tried = -1,
edns_not_supported = 0,
edns0_configured = 1,
edns0_supported = 2,
};
struct sres_server {
sres_socket_t dns_socket;
char dns_name[48]; /**< Server name */
struct sockaddr_storage dns_addr[1]; /**< Server node address */
ssize_t dns_addrlen; /**< Size of address */
enum edns dns_edns; /**< Server supports edns. */
/** ICMP/temporary error received, zero when successful. */
time_t dns_icmp;
/** Persistent error, zero when successful or timeout.
*
* Never selected if dns_error is SRES_TIME_MAX.
*/
time_t dns_error;
};
HTABLE_DECLARE_WITH(sres_qtable, qt, sres_query_t, unsigned, size_t);
struct sres_resolver_s {
su_home_t res_home[1];
void *res_userdata;
sres_cache_t *res_cache;
time_t res_now;
sres_qtable_t res_queries[1]; /**< Table of active queries */
char const *res_cnffile; /**< Configuration file name */
char const **res_options; /**< Option strings */
sres_config_t const *res_config;
time_t res_checked;
unsigned long res_updated;
sres_update_f *res_updcb;
sres_async_t *res_async;
sres_schedule_f *res_schedulecb;
short res_update_all;
uint16_t res_id;
short res_i_server; /**< Current server to try
(when doing round-robin) */
short res_n_servers; /**< Number of servers */
sres_server_t **res_servers;
};
/* Parsed configuration. @internal */
struct sres_config {
su_home_t c_home[1];
time_t c_modified;
char const *c_filename;
/* domain and search */
char const *c_search[SRES_MAX_SEARCH + 1];
/* nameserver */
struct sres_nameserver {
struct sockaddr_storage ns_addr[1];
ssize_t ns_addrlen;
} *c_nameservers[SRES_MAX_NAMESERVERS + 1];
/* sortlist */
struct sres_sortlist {
struct sockaddr_storage addr[1];
ssize_t addrlen;
char const *name;
} *c_sortlist[SRES_MAX_SORTLIST + 1];
uint16_t c_port; /**< Server port to use */
/* options */
struct sres_options {
uint16_t timeout;
uint16_t attempts;
uint16_t ndots;
enum edns edns;
unsigned debug:1;
unsigned rotate:1;
unsigned check_names:1;
unsigned inet6:1;
unsigned ip6int:1;
unsigned ip6bytestring:1;
} c_opt;
};
struct sres_query_s {
unsigned q_hash;
sres_resolver_t*q_res;
sres_answer_f *q_callback;
sres_context_t *q_context;
char *q_name;
time_t q_timestamp;
uint16_t q_type;
uint16_t q_class;
uint16_t q_id; /**< If nonzero, not answered */
uint16_t q_retry_count;
uint8_t q_n_servers;
uint8_t q_i_server;
int8_t q_edns;
uint8_t q_n_subs;
sres_query_t *q_subqueries[1 + SRES_MAX_SEARCH];
sres_record_t **q_subanswers[1 + SRES_MAX_SEARCH];
};
struct sres_message {
uint16_t m_offset;
uint16_t m_size;
char const *m_error;
union {
struct {
/* Header defined in RFC 1035 section 4.1.1 (page 26) */
uint16_t mh_id; /* Query ID */
uint16_t mh_flags; /* Flags */
uint16_t mh_qdcount; /* Question record count */
uint16_t mh_ancount; /* Answer record count */
uint16_t mh_nscount; /* Authority records count */
uint16_t mh_arcount; /* Additional records count */
} mp_header;
uint8_t mp_data[1500 - 40]; /**< IPv6 datagram */
} m_packet;
#define m_id m_packet.mp_header.mh_id
#define m_flags m_packet.mp_header.mh_flags
#define m_qdcount m_packet.mp_header.mh_qdcount
#define m_ancount m_packet.mp_header.mh_ancount
#define m_nscount m_packet.mp_header.mh_nscount
#define m_arcount m_packet.mp_header.mh_arcount
#define m_data m_packet.mp_data
};
#define sr_refcount sr_record->r_refcount
#define sr_name sr_record->r_name
#define sr_status sr_record->r_status
#define sr_size sr_record->r_size
#define sr_type sr_record->r_type
#define sr_class sr_record->r_class
#define sr_ttl sr_record->r_ttl
#define sr_rdlen sr_record->r_rdlen
#define sr_parsed sr_record->r_parsed
#define sr_rdata sr_generic->g_data
enum {
SRES_HDR_QR = (1 << 15),
SRES_HDR_QUERY = (0 << 11),
SRES_HDR_IQUERY = (1 << 11),
SRES_HDR_STATUS = (2 << 11),
SRES_HDR_OPCODE = (15 << 11), /* mask */
SRES_HDR_AA = (1 << 10),
SRES_HDR_TC = (1 << 9),
SRES_HDR_RD = (1 << 8),
SRES_HDR_RA = (1 << 7),
SRES_HDR_RCODE = (15 << 0) /* mask of return code */
};
HTABLE_PROTOS_WITH(sres_qtable, qt, sres_query_t, unsigned, size_t);
#define CHOME(cache) ((su_home_t *)(cache))
/** Get address from sockaddr storage. */
#if HAVE_SIN6
#define SS_ADDR(ss) \
((ss)->ss_family == AF_INET ? \
(void *)&((struct sockaddr_in *)ss)->sin_addr : \
((ss)->ss_family == AF_INET6 ? \
(void *)&((struct sockaddr_in6 *)ss)->sin6_addr : \
(void *)&((struct sockaddr *)ss)->sa_data))
#else
#define SS_ADDR(ss) \
((ss)->ss_family == AF_INET ? \
(void *)&((struct sockaddr_in *)ss)->sin_addr : \
(void *)&((struct sockaddr *)ss)->sa_data)
#endif
static int sres_config_changed_servers(sres_config_t const *new_c,
sres_config_t const *old_c);
static sres_server_t **sres_servers_new(sres_resolver_t *res,
sres_config_t const *c);
static sres_answer_f sres_resolving_cname;
/** Generate new 16-bit identifier for DNS query. */
static void
sres_gen_id(sres_resolver_t *res, sres_query_t *query)
{
if (res->res_id == 0) {
res->res_id = 1;
}
query->q_id = res->res_id++;
query->q_hash = query->q_id * Q_PRIME;
}
/** Return true if we have a search list or a local domain name. */
static int
sres_has_search_domain(sres_resolver_t *res)
{
return res->res_config->c_search[0] != NULL;
}
static void sres_resolver_destructor(void *);
sres_resolver_t *
sres_resolver_new_with_cache_va(char const *conf_file_path,
sres_cache_t *cache,
char const *options,
va_list va);
static
sres_resolver_t *
sres_resolver_new_internal(sres_cache_t *cache,
sres_config_t const *config,
char const *conf_file_path,
char const **options);
static void sres_servers_close(sres_resolver_t *res,
sres_server_t **servers);
static int sres_servers_count(sres_server_t * const *servers);
static sres_socket_t sres_server_socket(sres_resolver_t *res,
sres_server_t *dns);
static sres_query_t * sres_query_alloc(sres_resolver_t *res,
sres_answer_f *callback,
sres_context_t *context,
uint16_t type,
char const * domain);
static void sres_free_query(sres_resolver_t *res, sres_query_t *q);
static
int sres_sockaddr2string(sres_resolver_t *,
char name[], size_t namelen,
struct sockaddr const *);
static
sres_config_t *sres_parse_resolv_conf(sres_resolver_t *res,
char const **options);
static
sres_server_t *sres_next_server(sres_resolver_t *res,
uint8_t *in_out_i,
int always);
static
int sres_send_dns_query(sres_resolver_t *res, sres_query_t *q);
static
void sres_answer_subquery(sres_context_t *context,
sres_query_t *query,
sres_record_t **answers);
static
sres_record_t **
sres_combine_results(sres_resolver_t *res,
sres_record_t **search_results[SRES_MAX_SEARCH + 1]);
static
void sres_query_report_error(sres_query_t *q,
sres_record_t **answers);
static void
sres_resend_dns_query(sres_resolver_t *res, sres_query_t *q, int timeout);
static
sres_server_t *sres_server_by_socket(sres_resolver_t const *ts,
sres_socket_t socket);
static
int sres_resolver_report_error(sres_resolver_t *res,
sres_socket_t socket,
int errcode,
struct sockaddr_storage *remote,
socklen_t remotelen,
char const *info);
static
void sres_log_response(sres_resolver_t const *res,
sres_message_t const *m,
struct sockaddr_storage const *from,
sres_query_t const *query,
sres_record_t * const *reply);
static int sres_decode_msg(sres_resolver_t *res,
sres_message_t *m,
sres_query_t **,
sres_record_t ***aanswers);
static char const *sres_toplevel(char buf[], size_t bsize, char const *domain);
static sres_record_t *sres_create_record(sres_resolver_t *,
sres_message_t *m,
int nth);
static sres_record_t *sres_init_rr_soa(sres_cache_t *cache,
sres_soa_record_t *,
sres_message_t *m);
static sres_record_t *sres_init_rr_a(sres_cache_t *cache,
sres_a_record_t *,
sres_message_t *m);
static sres_record_t *sres_init_rr_a6(sres_cache_t *cache,
sres_a6_record_t *,
sres_message_t *m);
static sres_record_t *sres_init_rr_aaaa(sres_cache_t *cache,
sres_aaaa_record_t *,
sres_message_t *m);
static sres_record_t *sres_init_rr_cname(sres_cache_t *cache,
sres_cname_record_t *,
sres_message_t *m);
static sres_record_t *sres_init_rr_ptr(sres_cache_t *cache,
sres_ptr_record_t *,
sres_message_t *m);
static sres_record_t *sres_init_rr_srv(sres_cache_t *cache,
sres_srv_record_t *,
sres_message_t *m);
static sres_record_t *sres_init_rr_naptr(sres_cache_t *cache,
sres_naptr_record_t *,
sres_message_t *m);
static sres_record_t *sres_init_rr_unknown(sres_cache_t *cache,
sres_common_t *r,
sres_message_t *m);
static sres_record_t *sres_create_error_rr(sres_cache_t *cache,
sres_query_t const *q,
uint16_t errcode);
static void m_put_uint16(sres_message_t *m, uint16_t h);
static void m_put_uint32(sres_message_t *m, uint32_t w);
static uint16_t m_put_domain(sres_message_t *m,
char const *domain,
uint16_t top,
char const *topdomain);
static uint32_t m_get_uint32(sres_message_t *m);
static uint16_t m_get_uint16(sres_message_t *m);
static uint8_t m_get_uint8(sres_message_t *m);
static unsigned m_get_string(char *d, unsigned n, sres_message_t *m, uint16_t offset);
static unsigned m_get_domain(char *d, unsigned n, sres_message_t *m, uint16_t offset);
/* ---------------------------------------------------------------------- */
#define SU_LOG sresolv_log
#include <sofia-sip/su_debug.h>
#ifdef HAVE_WIN32
#include <winreg.h>
#endif
/**@ingroup sresolv_env
*
* Environment variable determining the debug log level for @b sresolv
* module.
*
* The SRESOLV_DEBUG environment variable is used to determine the debug
* logging level for @b sresolv module. The default level is 3.
*
* @sa <sofia-sip/su_debug.h>, sresolv_log, SOFIA_DEBUG
*/
#ifdef DOXYGEN
extern char const SRESOLV_DEBUG[]; /* dummy declaration for Doxygen */
#endif
#ifndef SU_DEBUG
#define SU_DEBUG 3
#endif
/**Debug log for @b sresolv module.
*
* The sresolv_log is the log object used by @b sresolv module. The level of
* #sresolv_log is set using #SRESOLV_DEBUG environment variable.
*/
su_log_t sresolv_log[] = { SU_LOG_INIT("sresolv", "SRESOLV_DEBUG", SU_DEBUG) };
/** Internal errors */
enum {
SRES_EDNS0_ERR = 255 /**< Server did not support EDNS. */
};
/* ---------------------------------------------------------------------- */
/**Create a resolver.
*
* Allocate and initialize a new sres resolver object. The resolver object
* contains the parsed resolv.conf file, a cache object containing past
* answers from DNS, and a list of active queries. The default resolv.conf
* file can be overriden by giving the name of the configuration file as @a
* conf_file_path.
*
* @param conf_file_path name of the resolv.conf configuration file
*
* @return A pointer to a newly created sres resolver object, or NULL upon
* an error.
*/
sres_resolver_t *
sres_resolver_new(char const *conf_file_path)
{
return sres_resolver_new_internal(NULL, NULL, conf_file_path, NULL);
}
/** Copy a resolver.
*
* Make a copy of resolver sharing the configuration and cache with old
* resolver.
*/
sres_resolver_t *sres_resolver_copy(sres_resolver_t *res)
{
char const *cnffile;
sres_config_t *config;
sres_cache_t *cache;
char const **options;
if (!res)
return NULL;
cnffile = res->res_cnffile;
config = su_home_ref(res->res_config->c_home);
cache = res->res_cache;
options = res->res_options;
return sres_resolver_new_internal(cache, config, cnffile, options);
}
/**New resolver object.
*
* Allocate and initialize a new sres resolver object. The resolver object
* contains the parsed resolv.conf file, a cache object containing past
* answers from DNS, and a list of active queries. The default resolv.conf
* file can be overriden by giving the name of the configuration file as @a
* conf_file_path.
*
* It is also possible to override the values in the resolv.conf and
* RES_OPTIONS by giving the directives in the NULL-terminated list.
*
* @param conf_file_path name of the resolv.conf configuration file
* @param cache optional pointer to a resolver cache (may be NULL)
* @param option, ... list of resolv.conf options directives
* (overriding options in conf_file)
*
* @par Environment Variables
* - #LOCALDOMAIN overrides @c domain or @c search directives
* - #RES_OPTIONS overrides values of @a options in resolv.conf
* - #SRES_OPTIONS overrides values of @a options in resolv.conf, #RES_OPTIONS,
* and @a options, ... list given as argument for this function
*
* @return A pointer to a newly created sres resolver object, or NULL upon
* an error.
*/
sres_resolver_t *
sres_resolver_new_with_cache(char const *conf_file_path,
sres_cache_t *cache,
char const *option, ...)
{
sres_resolver_t *retval;
va_list va;
va_start(va, option);
retval = sres_resolver_new_with_cache_va(conf_file_path, cache, option, va);
va_end(va);
return retval;
}
/**Create a resolver.
*
* Allocate and initialize a new sres resolver object.
*
* This is a stdarg version of sres_resolver_new_with_cache().
*/
sres_resolver_t *
sres_resolver_new_with_cache_va(char const *conf_file_path,
sres_cache_t *cache,
char const *option,
va_list va)
{
va_list va0;
size_t i;
char const *o, *oarray[16], **olist = oarray;
sres_resolver_t *res;
va_copy(va0, va);
for (i = 0, o = option; o; o = va_arg(va0, char const *)) {
if (i < 16)
olist[i] = o;
i++;
}
if (i >= 16) {
olist = malloc((i + 1) * sizeof *olist);
if (!olist)
return NULL;
for (i = 0, o = option; o; o = va_arg(va, char const *)) {
olist[i++] = o;
i++;
}
}
olist[i] = NULL;
res = sres_resolver_new_internal(cache, NULL, conf_file_path, olist);
if (olist != oarray)
free(olist);
va_end(va0);
return res;
}
sres_resolver_t *
sres_resolver_new_internal(sres_cache_t *cache,
sres_config_t const *config,
char const *conf_file_path,
char const **options)
{
sres_resolver_t *res;
size_t i, n, len;
char **array, *o, *end;
for (n = 0, len = 0; options && options[n]; n++)
len += strlen(options[n]) + 1;
res = su_home_new(sizeof(*res) + (n + 1) * (sizeof *options) + len);
if (res == NULL)
return NULL;
array = (void *)(res + 1);
o = (void *)(array + n + 1);
end = o + len;
for (i = 0; options && options[i]; i++)
o = memccpy(array[i] = o, options[i], '\0', len - (end - o));
assert(o == end);
su_home_destructor(res->res_home, sres_resolver_destructor);
while (res->res_id == 0) {
#if HAVE_DEV_URANDOM
int fd;
if ((fd = open("/dev/urandom", O_RDONLY, 0)) != -1) {
size_t len = read(fd, &res->res_id, (sizeof res->res_id)); (void)len;
close(fd);
}
else
#endif
res->res_id = time(NULL);
}
time(&res->res_now);
if (cache)
res->res_cache = sres_cache_ref(cache);
else
res->res_cache = sres_cache_new(0);
res->res_config = config;
if (conf_file_path && conf_file_path != sres_conf_file_path)
res->res_cnffile = su_strdup(res->res_home, conf_file_path);
else
res->res_cnffile = sres_conf_file_path;
if (!res->res_cache || !res->res_cnffile) {
perror("sres: malloc");
}
else if (sres_qtable_resize(res->res_home, res->res_queries, 0) < 0) {
perror("sres: res_qtable_resize");
}
else if (sres_resolver_update(res, config == NULL) < 0) {
perror("sres: sres_resolver_update");
}
else {
return res;
}
sres_resolver_unref(res);
return NULL;
}
/** Increase reference count on a resolver object. */
sres_resolver_t *
sres_resolver_ref(sres_resolver_t *res)
{
return su_home_ref(res->res_home);
}
/** Decrease the reference count on a resolver object. */
void
sres_resolver_unref(sres_resolver_t *res)
{
su_home_unref(res->res_home);
}
/** Set userdata pointer.
*
* @return New userdata pointer.
*
* @ERRORS
* @ERROR EFAULT @a res points outside the address space
*/
void *
sres_resolver_set_userdata(sres_resolver_t *res,
void *userdata)
{
void *old;
if (!res)
return su_seterrno(EFAULT), (void *)NULL;
old = res->res_userdata, res->res_userdata = userdata;
return old;
}
/**Get userdata pointer.
*
* @return Userdata pointer.
*
* @ERRORS
* @ERROR EFAULT @a res points outside the address space
*/
void *
sres_resolver_get_userdata(sres_resolver_t const *res)
{
if (res == NULL)
return su_seterrno(EFAULT), (void *)NULL;
else
return res->res_userdata;
}
/** Set async object.
*
* @return Set async object.
*
* @ERRORS
* @ERROR EFAULT @a res points outside the address space
* @ERROR EALREADY different async callback already set
*/
sres_async_t *
sres_resolver_set_async(sres_resolver_t *res,
sres_update_f *callback,
sres_async_t *async,
int update_all)
{
if (!res)
return su_seterrno(EFAULT), (void *)NULL;
if (res->res_updcb && res->res_updcb != callback)
return su_seterrno(EALREADY), (void *)NULL;
res->res_async = async;
res->res_updcb = callback;
res->res_update_all = callback && update_all != 0;
return async;
}
/** Get async object */
sres_async_t *
sres_resolver_get_async(sres_resolver_t const *res,
sres_update_f *callback)
{
if (res == NULL)
return su_seterrno(EFAULT), (void *)NULL;
else if (callback == NULL)
return res->res_async ? (sres_async_t *)-1 : 0;
else if (res->res_updcb != callback)
return NULL;
else
return res->res_async;
}
/** Register resolver timer callback. */
int sres_resolver_set_timer_cb(sres_resolver_t *res,
sres_schedule_f *callback,
sres_async_t *async)
{
if (res == NULL)
return su_seterrno(EFAULT);
if (res->res_async != async)
return su_seterrno(EALREADY);
res->res_schedulecb = callback;
return 0;
}
/**Send a DNS query.
*
* Sends a DNS query with specified @a type and @a domain to the DNS server.
* When an answer is received, the @a callback function is called with
* @a context and returned records as arguments.
*
* The sres resolver takes care of retransmitting the query if a root object
* is associate with the resolver or if sres_resolver_timer() is called in
* regular intervals. It generates an error record with nonzero status if no
* response is received.
*
* @param res pointer to resolver
* @param callback function called when query is answered or times out
* @param context pointer given as an extra argument to @a callback function
* @param type record type to query (see #sres_qtypes)
* @param domain name to query
*
* Query types also indicate the record type of the result.
* Any record can be queried with #sres_qtype_any.
* Well-known query types understood and decoded by @b sres include
* #sres_type_a,
* #sres_type_aaaa,
* #sres_type_cname,
* #sres_type_ptr
* #sres_type_soa,
* #sres_type_aaaa,
* #sres_type_srv, and
* #sres_type_naptr.
*
* Deprecated query type #sres_type_a6 is also decoded.
*
* @note The domain name is @b not concatenated with the domains from seach
* path or with the local domain. Use sres_search() in order to try domains
* in search path.
*
* @sa sres_search(), sres_blocking_query(), sres_cached_answers(),
* sres_query_sockaddr()
*
* @ERRORS
* @ERROR EFAULT @a res or @a domain point outside the address space
* @ERROR ENAMETOOLONG @a domain is longer than SRES_MAXDNAME
* @ERROR ENETDOWN no DNS servers configured
* @ERROR ENOMEM memory exhausted
*/
sres_query_t *
sres_query(sres_resolver_t *res,
sres_answer_f *callback,
sres_context_t *context,
uint16_t type,
char const *domain)
{
sres_query_t *query = NULL;
size_t dlen;
char b[8];
SU_DEBUG_9(("sres_query(%p, %p, %s, \"%s\") called\n",
(void *)res, (void *)context, sres_record_type(type, b), domain));
if (res == NULL || domain == NULL)
return su_seterrno(EFAULT), (void *)NULL;
dlen = strlen(domain);
if (dlen > SRES_MAXDNAME ||
(dlen == SRES_MAXDNAME && domain[dlen - 1] != '.')) {
su_seterrno(ENAMETOOLONG);
return NULL;
}
/* Reread resolv.conf if needed */
sres_resolver_update(res, 0);
if (res->res_n_servers == 0)
return (void)su_seterrno(ENETDOWN), (sres_query_t *)NULL;
query = sres_query_alloc(res, callback, context, type, domain);
if (query && sres_send_dns_query(res, query) != 0)
sres_free_query(res, query), query = NULL;
return query;
}
/**Search DNS.
*
* Sends DNS queries with specified @a type and @a name to the DNS server.
* If the @a name does not contain enought dots, the search domains are
* appended to the name and resulting domain name are also queried. When
* answer to all the search domains is received, the @a callback function
* is called with @a context and combined records from answers as arguments.