1/*
2 * Copyright (C) 2004, 2005, 2007, 2009  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/* $Id: forward.c,v 1.14 2009/09/02 23:48:02 tbox Exp $ */
19
20/*! \file */
21
22#include <config.h>
23
24#include <isc/magic.h>
25#include <isc/mem.h>
26#include <isc/rwlock.h>
27#include <isc/sockaddr.h>
28#include <isc/util.h>
29
30#include <dns/forward.h>
31#include <dns/rbt.h>
32#include <dns/result.h>
33#include <dns/types.h>
34
35struct dns_fwdtable {
36	/* Unlocked. */
37	unsigned int		magic;
38	isc_mem_t		*mctx;
39	isc_rwlock_t		rwlock;
40	/* Locked by lock. */
41	dns_rbt_t		*table;
42};
43
44#define FWDTABLEMAGIC		ISC_MAGIC('F', 'w', 'd', 'T')
45#define VALID_FWDTABLE(ft) 	ISC_MAGIC_VALID(ft, FWDTABLEMAGIC)
46
47static void
48auto_detach(void *, void *);
49
50isc_result_t
51dns_fwdtable_create(isc_mem_t *mctx, dns_fwdtable_t **fwdtablep) {
52	dns_fwdtable_t *fwdtable;
53	isc_result_t result;
54
55	REQUIRE(fwdtablep != NULL && *fwdtablep == NULL);
56
57	fwdtable = isc_mem_get(mctx, sizeof(dns_fwdtable_t));
58	if (fwdtable == NULL)
59		return (ISC_R_NOMEMORY);
60
61	fwdtable->table = NULL;
62	result = dns_rbt_create(mctx, auto_detach, fwdtable, &fwdtable->table);
63	if (result != ISC_R_SUCCESS)
64		goto cleanup_fwdtable;
65
66	result = isc_rwlock_init(&fwdtable->rwlock, 0, 0);
67	if (result != ISC_R_SUCCESS)
68		goto cleanup_rbt;
69
70	fwdtable->mctx = NULL;
71	isc_mem_attach(mctx, &fwdtable->mctx);
72	fwdtable->magic = FWDTABLEMAGIC;
73	*fwdtablep = fwdtable;
74
75	return (ISC_R_SUCCESS);
76
77   cleanup_rbt:
78	dns_rbt_destroy(&fwdtable->table);
79
80   cleanup_fwdtable:
81	isc_mem_put(mctx, fwdtable, sizeof(dns_fwdtable_t));
82
83	return (result);
84}
85
86isc_result_t
87dns_fwdtable_add(dns_fwdtable_t *fwdtable, dns_name_t *name,
88		 isc_sockaddrlist_t *addrs, dns_fwdpolicy_t fwdpolicy)
89{
90	isc_result_t result;
91	dns_forwarders_t *forwarders;
92	isc_sockaddr_t *sa, *nsa;
93
94	REQUIRE(VALID_FWDTABLE(fwdtable));
95
96	forwarders = isc_mem_get(fwdtable->mctx, sizeof(dns_forwarders_t));
97	if (forwarders == NULL)
98		return (ISC_R_NOMEMORY);
99
100	ISC_LIST_INIT(forwarders->addrs);
101	for (sa = ISC_LIST_HEAD(*addrs);
102	     sa != NULL;
103	     sa = ISC_LIST_NEXT(sa, link))
104	{
105		nsa = isc_mem_get(fwdtable->mctx, sizeof(isc_sockaddr_t));
106		if (nsa == NULL) {
107			result = ISC_R_NOMEMORY;
108			goto cleanup;
109		}
110		*nsa = *sa;
111		ISC_LINK_INIT(nsa, link);
112		ISC_LIST_APPEND(forwarders->addrs, nsa, link);
113	}
114	forwarders->fwdpolicy = fwdpolicy;
115
116	RWLOCK(&fwdtable->rwlock, isc_rwlocktype_write);
117	result = dns_rbt_addname(fwdtable->table, name, forwarders);
118	RWUNLOCK(&fwdtable->rwlock, isc_rwlocktype_write);
119
120	if (result != ISC_R_SUCCESS)
121		goto cleanup;
122
123	return (ISC_R_SUCCESS);
124
125 cleanup:
126	while (!ISC_LIST_EMPTY(forwarders->addrs)) {
127		sa = ISC_LIST_HEAD(forwarders->addrs);
128		ISC_LIST_UNLINK(forwarders->addrs, sa, link);
129		isc_mem_put(fwdtable->mctx, sa, sizeof(isc_sockaddr_t));
130	}
131	isc_mem_put(fwdtable->mctx, forwarders, sizeof(dns_forwarders_t));
132	return (result);
133}
134
135isc_result_t
136dns_fwdtable_delete(dns_fwdtable_t *fwdtable, dns_name_t *name) {
137	isc_result_t result;
138
139	REQUIRE(VALID_FWDTABLE(fwdtable));
140
141	RWLOCK(&fwdtable->rwlock, isc_rwlocktype_write);
142	result = dns_rbt_deletename(fwdtable->table, name, ISC_FALSE);
143	RWUNLOCK(&fwdtable->rwlock, isc_rwlocktype_write);
144
145	if (result == DNS_R_PARTIALMATCH)
146		result = ISC_R_NOTFOUND;
147
148	return (result);
149}
150
151isc_result_t
152dns_fwdtable_find(dns_fwdtable_t *fwdtable, dns_name_t *name,
153		  dns_forwarders_t **forwardersp)
154{
155	return (dns_fwdtable_find2(fwdtable, name, NULL, forwardersp));
156}
157
158isc_result_t
159dns_fwdtable_find2(dns_fwdtable_t *fwdtable, dns_name_t *name,
160		   dns_name_t *foundname, dns_forwarders_t **forwardersp)
161{
162	isc_result_t result;
163
164	REQUIRE(VALID_FWDTABLE(fwdtable));
165
166	RWLOCK(&fwdtable->rwlock, isc_rwlocktype_read);
167
168	result = dns_rbt_findname(fwdtable->table, name, 0, foundname,
169				  (void **)forwardersp);
170	if (result == DNS_R_PARTIALMATCH)
171		result = ISC_R_SUCCESS;
172
173	RWUNLOCK(&fwdtable->rwlock, isc_rwlocktype_read);
174
175	return (result);
176}
177
178void
179dns_fwdtable_destroy(dns_fwdtable_t **fwdtablep) {
180	dns_fwdtable_t *fwdtable;
181	isc_mem_t *mctx;
182
183	REQUIRE(fwdtablep != NULL && VALID_FWDTABLE(*fwdtablep));
184
185	fwdtable = *fwdtablep;
186
187	dns_rbt_destroy(&fwdtable->table);
188	isc_rwlock_destroy(&fwdtable->rwlock);
189	fwdtable->magic = 0;
190	mctx = fwdtable->mctx;
191	isc_mem_put(mctx, fwdtable, sizeof(dns_fwdtable_t));
192	isc_mem_detach(&mctx);
193
194	*fwdtablep = NULL;
195}
196
197/***
198 *** Private
199 ***/
200
201static void
202auto_detach(void *data, void *arg) {
203	dns_forwarders_t *forwarders = data;
204	dns_fwdtable_t *fwdtable = arg;
205	isc_sockaddr_t *sa;
206
207	UNUSED(arg);
208
209	while (!ISC_LIST_EMPTY(forwarders->addrs)) {
210		sa = ISC_LIST_HEAD(forwarders->addrs);
211		ISC_LIST_UNLINK(forwarders->addrs, sa, link);
212		isc_mem_put(fwdtable->mctx, sa, sizeof(isc_sockaddr_t));
213	}
214	isc_mem_put(fwdtable->mctx, forwarders, sizeof(dns_forwarders_t));
215}
216