1 /*
2 * Copyright (C) 2004, 2005, 2007, 2009, 2016 Internet Systems Consortium, Inc. ("ISC")
3 * Copyright (C) 2000, 2001 Internet Software Consortium.
4 *
5 * Permission to use, copy, modify, and/or distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
10 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
11 * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
12 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
13 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
14 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
15 * PERFORMANCE OF THIS SOFTWARE.
16 */
17
18 /*! \file */
19
20 #include <config.h>
21
22 #include <isc/magic.h>
23 #include <isc/mem.h>
24 #include <isc/rwlock.h>
25 #include <isc/util.h>
26
27 #include <dns/forward.h>
28 #include <dns/rbt.h>
29 #include <dns/result.h>
30 #include <dns/types.h>
31
32 struct dns_fwdtable {
33 /* Unlocked. */
34 unsigned int magic;
35 isc_mem_t *mctx;
36 isc_rwlock_t rwlock;
37 /* Locked by lock. */
38 dns_rbt_t *table;
39 };
40
41 #define FWDTABLEMAGIC ISC_MAGIC('F', 'w', 'd', 'T')
42 #define VALID_FWDTABLE(ft) ISC_MAGIC_VALID(ft, FWDTABLEMAGIC)
43
44 static void
45 auto_detach(void *, void *);
46
47 isc_result_t
dns_fwdtable_create(isc_mem_t * mctx,dns_fwdtable_t ** fwdtablep)48 dns_fwdtable_create(isc_mem_t *mctx, dns_fwdtable_t **fwdtablep) {
49 dns_fwdtable_t *fwdtable;
50 isc_result_t result;
51
52 REQUIRE(fwdtablep != NULL && *fwdtablep == NULL);
53
54 fwdtable = isc_mem_get(mctx, sizeof(dns_fwdtable_t));
55 if (fwdtable == NULL)
56 return (ISC_R_NOMEMORY);
57
58 fwdtable->table = NULL;
59 result = dns_rbt_create(mctx, auto_detach, fwdtable, &fwdtable->table);
60 if (result != ISC_R_SUCCESS)
61 goto cleanup_fwdtable;
62
63 result = isc_rwlock_init(&fwdtable->rwlock, 0, 0);
64 if (result != ISC_R_SUCCESS)
65 goto cleanup_rbt;
66
67 fwdtable->mctx = NULL;
68 isc_mem_attach(mctx, &fwdtable->mctx);
69 fwdtable->magic = FWDTABLEMAGIC;
70 *fwdtablep = fwdtable;
71
72 return (ISC_R_SUCCESS);
73
74 cleanup_rbt:
75 dns_rbt_destroy(&fwdtable->table);
76
77 cleanup_fwdtable:
78 isc_mem_put(mctx, fwdtable, sizeof(dns_fwdtable_t));
79
80 return (result);
81 }
82
83 isc_result_t
dns_fwdtable_add(dns_fwdtable_t * fwdtable,dns_name_t * name,isc_sockaddrlist_t * addrs,dns_fwdpolicy_t fwdpolicy)84 dns_fwdtable_add(dns_fwdtable_t *fwdtable, dns_name_t *name,
85 isc_sockaddrlist_t *addrs, dns_fwdpolicy_t fwdpolicy)
86 {
87 isc_result_t result;
88 dns_forwarders_t *forwarders;
89 isc_sockaddr_t *sa, *nsa;
90
91 REQUIRE(VALID_FWDTABLE(fwdtable));
92
93 forwarders = isc_mem_get(fwdtable->mctx, sizeof(dns_forwarders_t));
94 if (forwarders == NULL)
95 return (ISC_R_NOMEMORY);
96
97 ISC_LIST_INIT(forwarders->addrs);
98 for (sa = ISC_LIST_HEAD(*addrs);
99 sa != NULL;
100 sa = ISC_LIST_NEXT(sa, link))
101 {
102 nsa = isc_mem_get(fwdtable->mctx, sizeof(isc_sockaddr_t));
103 if (nsa == NULL) {
104 result = ISC_R_NOMEMORY;
105 goto cleanup;
106 }
107 *nsa = *sa;
108 ISC_LINK_INIT(nsa, link);
109 ISC_LIST_APPEND(forwarders->addrs, nsa, link);
110 }
111 forwarders->fwdpolicy = fwdpolicy;
112
113 RWLOCK(&fwdtable->rwlock, isc_rwlocktype_write);
114 result = dns_rbt_addname(fwdtable->table, name, forwarders);
115 RWUNLOCK(&fwdtable->rwlock, isc_rwlocktype_write);
116
117 if (result != ISC_R_SUCCESS)
118 goto cleanup;
119
120 return (ISC_R_SUCCESS);
121
122 cleanup:
123 while (!ISC_LIST_EMPTY(forwarders->addrs)) {
124 sa = ISC_LIST_HEAD(forwarders->addrs);
125 ISC_LIST_UNLINK(forwarders->addrs, sa, link);
126 isc_mem_put(fwdtable->mctx, sa, sizeof(isc_sockaddr_t));
127 }
128 isc_mem_put(fwdtable->mctx, forwarders, sizeof(dns_forwarders_t));
129 return (result);
130 }
131
132 isc_result_t
dns_fwdtable_delete(dns_fwdtable_t * fwdtable,dns_name_t * name)133 dns_fwdtable_delete(dns_fwdtable_t *fwdtable, dns_name_t *name) {
134 isc_result_t result;
135
136 REQUIRE(VALID_FWDTABLE(fwdtable));
137
138 RWLOCK(&fwdtable->rwlock, isc_rwlocktype_write);
139 result = dns_rbt_deletename(fwdtable->table, name, ISC_FALSE);
140 RWUNLOCK(&fwdtable->rwlock, isc_rwlocktype_write);
141
142 if (result == DNS_R_PARTIALMATCH)
143 result = ISC_R_NOTFOUND;
144
145 return (result);
146 }
147
148 isc_result_t
dns_fwdtable_find(dns_fwdtable_t * fwdtable,dns_name_t * name,dns_forwarders_t ** forwardersp)149 dns_fwdtable_find(dns_fwdtable_t *fwdtable, dns_name_t *name,
150 dns_forwarders_t **forwardersp)
151 {
152 return (dns_fwdtable_find2(fwdtable, name, NULL, forwardersp));
153 }
154
155 isc_result_t
dns_fwdtable_find2(dns_fwdtable_t * fwdtable,dns_name_t * name,dns_name_t * foundname,dns_forwarders_t ** forwardersp)156 dns_fwdtable_find2(dns_fwdtable_t *fwdtable, dns_name_t *name,
157 dns_name_t *foundname, dns_forwarders_t **forwardersp)
158 {
159 isc_result_t result;
160
161 REQUIRE(VALID_FWDTABLE(fwdtable));
162
163 RWLOCK(&fwdtable->rwlock, isc_rwlocktype_read);
164
165 result = dns_rbt_findname(fwdtable->table, name, 0, foundname,
166 (void **)forwardersp);
167 if (result == DNS_R_PARTIALMATCH)
168 result = ISC_R_SUCCESS;
169
170 RWUNLOCK(&fwdtable->rwlock, isc_rwlocktype_read);
171
172 return (result);
173 }
174
175 void
dns_fwdtable_destroy(dns_fwdtable_t ** fwdtablep)176 dns_fwdtable_destroy(dns_fwdtable_t **fwdtablep) {
177 dns_fwdtable_t *fwdtable;
178 isc_mem_t *mctx;
179
180 REQUIRE(fwdtablep != NULL && VALID_FWDTABLE(*fwdtablep));
181
182 fwdtable = *fwdtablep;
183
184 dns_rbt_destroy(&fwdtable->table);
185 isc_rwlock_destroy(&fwdtable->rwlock);
186 fwdtable->magic = 0;
187 mctx = fwdtable->mctx;
188 isc_mem_put(mctx, fwdtable, sizeof(dns_fwdtable_t));
189 isc_mem_detach(&mctx);
190
191 *fwdtablep = NULL;
192 }
193
194 /***
195 *** Private
196 ***/
197
198 static void
auto_detach(void * data,void * arg)199 auto_detach(void *data, void *arg) {
200 dns_forwarders_t *forwarders = data;
201 dns_fwdtable_t *fwdtable = arg;
202 isc_sockaddr_t *sa;
203
204 UNUSED(arg);
205
206 while (!ISC_LIST_EMPTY(forwarders->addrs)) {
207 sa = ISC_LIST_HEAD(forwarders->addrs);
208 ISC_LIST_UNLINK(forwarders->addrs, sa, link);
209 isc_mem_put(fwdtable->mctx, sa, sizeof(isc_sockaddr_t));
210 }
211 isc_mem_put(fwdtable->mctx, forwarders, sizeof(dns_forwarders_t));
212 }
213